Changeset 37491 in vbox for trunk/src/VBox
- Timestamp:
- Jun 16, 2011 12:23:34 PM (14 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 3 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/Makefile.kmk
r37282 r37491 302 302 src-server/HostPower.cpp \ 303 303 src-server/MachineImpl.cpp \ 304 src-server/MachineImplCloneVM.cpp \ 304 305 src-server/Matching.cpp \ 305 306 src-server/MediumAttachmentImpl.cpp \ -
trunk/src/VBox/Main/include/MachineImpl.h
r37485 r37491 908 908 friend class Appliance; 909 909 friend class VirtualBox; 910 911 friend class MachineCloneVM; 910 912 }; 911 913 -
trunk/src/VBox/Main/include/MachineImplCloneVM.h
r37485 r37491 1 /* $Id$ */2 1 /** @file 3 * VirtualBox COM class implementation2 * Definition of MachineCloneVM 4 3 */ 5 4 6 5 /* 7 * Copyright (C) 20 06-2011 Oracle Corporation6 * Copyright (C) 2011 Oracle Corporation 8 7 * 9 8 * This file is part of VirtualBox Open Source Edition (OSE), as … … 16 15 */ 17 16 18 #ifndef ____H_MACHINEIMPL 19 #define ____H_MACHINEIMPL 17 #ifndef ____H_MACHINEIMPLCLONEVM 18 #define ____H_MACHINEIMPLCLONEVM 20 19 21 #include "VirtualBoxBase.h" 22 #include "SnapshotImpl.h" 20 #include "MachineImpl.h" 23 21 #include "ProgressImpl.h" 24 #include "VRDEServerImpl.h"25 #include "MediumAttachmentImpl.h"26 #include "PciDeviceAttachmentImpl.h"27 #include "MediumLock.h"28 #include "NetworkAdapterImpl.h"29 #include "AudioAdapterImpl.h"30 #include "SerialPortImpl.h"31 #include "ParallelPortImpl.h"32 #include "BIOSSettingsImpl.h"33 #include "StorageControllerImpl.h" // required for MachineImpl.h to compile on Windows34 #include "BandwidthControlImpl.h"35 #include "BandwidthGroupImpl.h"36 #include "VBox/settings.h"37 #ifdef VBOX_WITH_RESOURCE_USAGE_API38 #include "Performance.h"39 #include "PerformanceImpl.h"40 #endif /* VBOX_WITH_RESOURCE_USAGE_API */41 22 42 / / generated header43 #include "SchemaDefs.h" 23 /* Forward declaration of the d-pointer. */ 24 struct MachineCloneVMPrivate; 44 25 45 #include "VBox/com/ErrorInfo.h" 26 class MachineCloneVM 27 { 28 public: 29 MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode, bool fFullClone); 30 ~MachineCloneVM(); 46 31 47 #include <iprt/file.h> 48 #include <iprt/thread.h> 49 #include <iprt/time.h> 50 51 #include <list> 52 53 // defines 54 //////////////////////////////////////////////////////////////////////////////// 55 56 // helper declarations 57 //////////////////////////////////////////////////////////////////////////////// 58 59 class Progress; 60 class ProgressProxy; 61 class Keyboard; 62 class Mouse; 63 class Display; 64 class MachineDebugger; 65 class USBController; 66 class Snapshot; 67 class SharedFolder; 68 class HostUSBDevice; 69 class StorageController; 70 71 class SessionMachine; 72 73 namespace settings 74 { 75 class MachineConfigFile; 76 struct Snapshot; 77 struct Hardware; 78 struct Storage; 79 struct StorageController; 80 struct MachineRegistryEntry; 81 } 82 83 // Machine class 84 //////////////////////////////////////////////////////////////////////////////// 85 86 class ATL_NO_VTABLE Machine : 87 public VirtualBoxBase, 88 VBOX_SCRIPTABLE_IMPL(IMachine) 89 { 90 Q_OBJECT 91 92 public: 93 94 enum StateDependency 95 { 96 AnyStateDep = 0, MutableStateDep, MutableOrSavedStateDep 97 }; 98 99 /** 100 * Internal machine data. 101 * 102 * Only one instance of this data exists per every machine -- it is shared 103 * by the Machine, SessionMachine and all SnapshotMachine instances 104 * associated with the given machine using the util::Shareable template 105 * through the mData variable. 106 * 107 * @note |const| members are persistent during lifetime so can be 108 * accessed without locking. 109 * 110 * @note There is no need to lock anything inside init() or uninit() 111 * methods, because they are always serialized (see AutoCaller). 112 */ 113 struct Data 114 { 115 /** 116 * Data structure to hold information about sessions opened for the 117 * given machine. 118 */ 119 struct Session 120 { 121 /** Control of the direct session opened by lockMachine() */ 122 ComPtr<IInternalSessionControl> mDirectControl; 123 124 typedef std::list<ComPtr<IInternalSessionControl> > RemoteControlList; 125 126 /** list of controls of all opened remote sessions */ 127 RemoteControlList mRemoteControls; 128 129 /** launchVMProcess() and OnSessionEnd() progress indicator */ 130 ComObjPtr<ProgressProxy> mProgress; 131 132 /** 133 * PID of the session object that must be passed to openSession() 134 * to finalize the launchVMProcess() request (i.e., PID of the 135 * process created by launchVMProcess()) 136 */ 137 RTPROCESS mPid; 138 139 /** Current session state */ 140 SessionState_T mState; 141 142 /** Session type string (for indirect sessions) */ 143 Bstr mType; 144 145 /** Session machine object */ 146 ComObjPtr<SessionMachine> mMachine; 147 148 /** Medium object lock collection. */ 149 MediumLockListMap mLockedMedia; 150 }; 151 152 Data(); 153 ~Data(); 154 155 const Guid mUuid; 156 BOOL mRegistered; 157 158 Utf8Str m_strConfigFile; 159 Utf8Str m_strConfigFileFull; 160 161 // machine settings XML file 162 settings::MachineConfigFile *pMachineConfigFile; 163 uint32_t flModifications; 164 165 BOOL mAccessible; 166 com::ErrorInfo mAccessError; 167 168 MachineState_T mMachineState; 169 RTTIMESPEC mLastStateChange; 170 171 /* Note: These are guarded by VirtualBoxBase::stateLockHandle() */ 172 uint32_t mMachineStateDeps; 173 RTSEMEVENTMULTI mMachineStateDepsSem; 174 uint32_t mMachineStateChangePending; 175 176 BOOL mCurrentStateModified; 177 /** Guest properties have been modified and need saving since the 178 * machine was started, or there are transient properties which need 179 * deleting and the machine is being shut down. */ 180 BOOL mGuestPropertiesModified; 181 182 Session mSession; 183 184 ComObjPtr<Snapshot> mFirstSnapshot; 185 ComObjPtr<Snapshot> mCurrentSnapshot; 186 187 // list of files to delete in Delete(); this list is filled by Unregister() 188 std::list<Utf8Str> llFilesToDelete; 189 }; 190 191 /** 192 * Saved state data. 193 * 194 * It's actually only the state file path string, but it needs to be 195 * separate from Data, because Machine and SessionMachine instances 196 * share it, while SnapshotMachine does not. 197 * 198 * The data variable is |mSSData|. 199 */ 200 struct SSData 201 { 202 Utf8Str strStateFilePath; 203 }; 204 205 /** 206 * User changeable machine data. 207 * 208 * This data is common for all machine snapshots, i.e. it is shared 209 * by all SnapshotMachine instances associated with the given machine 210 * using the util::Backupable template through the |mUserData| variable. 211 * 212 * SessionMachine instances can alter this data and discard changes. 213 * 214 * @note There is no need to lock anything inside init() or uninit() 215 * methods, because they are always serialized (see AutoCaller). 216 */ 217 struct UserData 218 { 219 settings::MachineUserData s; 220 }; 221 222 /** 223 * Hardware data. 224 * 225 * This data is unique for a machine and for every machine snapshot. 226 * Stored using the util::Backupable template in the |mHWData| variable. 227 * 228 * SessionMachine instances can alter this data and discard changes. 229 */ 230 struct HWData 231 { 232 /** 233 * Data structure to hold information about a guest property. 234 */ 235 struct GuestProperty { 236 /** Property name */ 237 Utf8Str strName; 238 /** Property value */ 239 Utf8Str strValue; 240 /** Property timestamp */ 241 LONG64 mTimestamp; 242 /** Property flags */ 243 ULONG mFlags; 244 }; 245 246 HWData(); 247 ~HWData(); 248 249 Bstr mHWVersion; 250 Guid mHardwareUUID; /**< If Null, use mData.mUuid. */ 251 ULONG mMemorySize; 252 ULONG mMemoryBalloonSize; 253 BOOL mPageFusionEnabled; 254 ULONG mVRAMSize; 255 ULONG mMonitorCount; 256 BOOL mHWVirtExEnabled; 257 BOOL mHWVirtExExclusive; 258 BOOL mHWVirtExNestedPagingEnabled; 259 BOOL mHWVirtExLargePagesEnabled; 260 BOOL mHWVirtExVPIDEnabled; 261 BOOL mHWVirtExForceEnabled; 262 BOOL mAccelerate2DVideoEnabled; 263 BOOL mPAEEnabled; 264 BOOL mSyntheticCpu; 265 ULONG mCPUCount; 266 BOOL mCPUHotPlugEnabled; 267 ULONG mCpuExecutionCap; 268 BOOL mAccelerate3DEnabled; 269 BOOL mHpetEnabled; 270 271 BOOL mCPUAttached[SchemaDefs::MaxCPUCount]; 272 273 settings::CpuIdLeaf mCpuIdStdLeafs[10]; 274 settings::CpuIdLeaf mCpuIdExtLeafs[10]; 275 276 DeviceType_T mBootOrder[SchemaDefs::MaxBootPosition]; 277 278 typedef std::list< ComObjPtr<SharedFolder> > SharedFolderList; 279 SharedFolderList mSharedFolders; 280 281 ClipboardMode_T mClipboardMode; 282 283 typedef std::list<GuestProperty> GuestPropertyList; 284 GuestPropertyList mGuestProperties; 285 Utf8Str mGuestPropertyNotificationPatterns; 286 287 FirmwareType_T mFirmwareType; 288 KeyboardHidType_T mKeyboardHidType; 289 PointingHidType_T mPointingHidType; 290 ChipsetType_T mChipsetType; 291 292 BOOL mIoCacheEnabled; 293 ULONG mIoCacheSize; 294 295 typedef std::list< ComObjPtr<PciDeviceAttachment> > PciDeviceAssignmentList; 296 PciDeviceAssignmentList mPciDeviceAssignments; 297 }; 298 299 /** 300 * Hard disk and other media data. 301 * 302 * The usage policy is the same as for HWData, but a separate structure 303 * is necessary because hard disk data requires different procedures when 304 * taking or deleting snapshots, etc. 305 * 306 * The data variable is |mMediaData|. 307 */ 308 struct MediaData 309 { 310 MediaData(); 311 ~MediaData(); 312 313 typedef std::list< ComObjPtr<MediumAttachment> > AttachmentList; 314 AttachmentList mAttachments; 315 }; 316 317 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(Machine, IMachine) 318 319 DECLARE_NOT_AGGREGATABLE(Machine) 320 321 DECLARE_PROTECT_FINAL_CONSTRUCT() 322 323 BEGIN_COM_MAP(Machine) 324 VBOX_DEFAULT_INTERFACE_ENTRIES(IMachine) 325 END_COM_MAP() 326 327 DECLARE_EMPTY_CTOR_DTOR(Machine) 328 329 HRESULT FinalConstruct(); 330 void FinalRelease(); 331 332 // public initializer/uninitializer for internal purposes only: 333 334 // initializer for creating a new, empty machine 335 HRESULT init(VirtualBox *aParent, 336 const Utf8Str &strConfigFile, 337 const Utf8Str &strName, 338 GuestOSType *aOsType, 339 const Guid &aId, 340 bool fForceOverwrite); 341 342 // initializer for loading existing machine XML (either registered or not) 343 HRESULT init(VirtualBox *aParent, 344 const Utf8Str &strConfigFile, 345 const Guid *aId); 346 347 // initializer for machine config in memory (OVF import) 348 HRESULT init(VirtualBox *aParent, 349 const Utf8Str &strName, 350 const settings::MachineConfigFile &config); 351 352 void uninit(); 353 354 #ifdef VBOX_WITH_RESOURCE_USAGE_API 355 // Needed from VirtualBox, for the delayed metrics cleanup. 356 void unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine); 357 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 32 HRESULT start(IProgress **pProgress); 358 33 359 34 protected: 360 HRESULT initImpl(VirtualBox *aParent, 361 const Utf8Str &strConfigFile); 362 HRESULT initDataAndChildObjects(); 363 HRESULT registeredInit(); 364 HRESULT tryCreateMachineConfigFile(bool fForceOverwrite); 365 void uninitDataAndChildObjects(); 35 HRESULT run(); 36 void destroy(); 366 37 367 public: 368 // IMachine properties 369 STDMETHOD(COMGETTER(Parent))(IVirtualBox **aParent); 370 STDMETHOD(COMGETTER(Accessible))(BOOL *aAccessible); 371 STDMETHOD(COMGETTER(AccessError))(IVirtualBoxErrorInfo **aAccessError); 372 STDMETHOD(COMGETTER(Name))(BSTR *aName); 373 STDMETHOD(COMSETTER(Name))(IN_BSTR aName); 374 STDMETHOD(COMGETTER(Description))(BSTR *aDescription); 375 STDMETHOD(COMSETTER(Description))(IN_BSTR aDescription); 376 STDMETHOD(COMGETTER(Id))(BSTR *aId); 377 STDMETHOD(COMGETTER(OSTypeId))(BSTR *aOSTypeId); 378 STDMETHOD(COMSETTER(OSTypeId))(IN_BSTR aOSTypeId); 379 STDMETHOD(COMGETTER(HardwareVersion))(BSTR *aVersion); 380 STDMETHOD(COMSETTER(HardwareVersion))(IN_BSTR aVersion); 381 STDMETHOD(COMGETTER(HardwareUUID))(BSTR *aUUID); 382 STDMETHOD(COMSETTER(HardwareUUID))(IN_BSTR aUUID); 383 STDMETHOD(COMGETTER(MemorySize))(ULONG *memorySize); 384 STDMETHOD(COMSETTER(MemorySize))(ULONG memorySize); 385 STDMETHOD(COMGETTER(CPUCount))(ULONG *cpuCount); 386 STDMETHOD(COMSETTER(CPUCount))(ULONG cpuCount); 387 STDMETHOD(COMGETTER(CPUHotPlugEnabled))(BOOL *enabled); 388 STDMETHOD(COMSETTER(CPUHotPlugEnabled))(BOOL enabled); 389 STDMETHOD(COMGETTER(CPUExecutionCap))(ULONG *aExecutionCap); 390 STDMETHOD(COMSETTER(CPUExecutionCap))(ULONG aExecutionCap); 391 STDMETHOD(COMGETTER(HpetEnabled))(BOOL *enabled); 392 STDMETHOD(COMSETTER(HpetEnabled))(BOOL enabled); 393 STDMETHOD(COMGETTER(MemoryBalloonSize))(ULONG *memoryBalloonSize); 394 STDMETHOD(COMSETTER(MemoryBalloonSize))(ULONG memoryBalloonSize); 395 STDMETHOD(COMGETTER(PageFusionEnabled))(BOOL *enabled); 396 STDMETHOD(COMSETTER(PageFusionEnabled))(BOOL enabled); 397 STDMETHOD(COMGETTER(VRAMSize))(ULONG *memorySize); 398 STDMETHOD(COMSETTER(VRAMSize))(ULONG memorySize); 399 STDMETHOD(COMGETTER(MonitorCount))(ULONG *monitorCount); 400 STDMETHOD(COMSETTER(MonitorCount))(ULONG monitorCount); 401 STDMETHOD(COMGETTER(Accelerate3DEnabled))(BOOL *enabled); 402 STDMETHOD(COMSETTER(Accelerate3DEnabled))(BOOL enabled); 403 STDMETHOD(COMGETTER(Accelerate2DVideoEnabled))(BOOL *enabled); 404 STDMETHOD(COMSETTER(Accelerate2DVideoEnabled))(BOOL enabled); 405 STDMETHOD(COMGETTER(BIOSSettings))(IBIOSSettings **biosSettings); 406 STDMETHOD(COMGETTER(SnapshotFolder))(BSTR *aSavedStateFolder); 407 STDMETHOD(COMSETTER(SnapshotFolder))(IN_BSTR aSavedStateFolder); 408 STDMETHOD(COMGETTER(MediumAttachments))(ComSafeArrayOut(IMediumAttachment *, aAttachments)); 409 STDMETHOD(COMGETTER(VRDEServer))(IVRDEServer **vrdeServer); 410 STDMETHOD(COMGETTER(AudioAdapter))(IAudioAdapter **audioAdapter); 411 STDMETHOD(COMGETTER(USBController))(IUSBController * *aUSBController); 412 STDMETHOD(COMGETTER(SettingsFilePath))(BSTR *aFilePath); 413 STDMETHOD(COMGETTER(SettingsModified))(BOOL *aModified); 414 STDMETHOD(COMGETTER(SessionState))(SessionState_T *aSessionState); 415 STDMETHOD(COMGETTER(SessionType))(BSTR *aSessionType); 416 STDMETHOD(COMGETTER(SessionPid))(ULONG *aSessionPid); 417 STDMETHOD(COMGETTER(State))(MachineState_T *machineState); 418 STDMETHOD(COMGETTER(LastStateChange))(LONG64 *aLastStateChange); 419 STDMETHOD(COMGETTER(StateFilePath))(BSTR *aStateFilePath); 420 STDMETHOD(COMGETTER(LogFolder))(BSTR *aLogFolder); 421 STDMETHOD(COMGETTER(CurrentSnapshot))(ISnapshot **aCurrentSnapshot); 422 STDMETHOD(COMGETTER(SnapshotCount))(ULONG *aSnapshotCount); 423 STDMETHOD(COMGETTER(CurrentStateModified))(BOOL *aCurrentStateModified); 424 STDMETHOD(COMGETTER(SharedFolders))(ComSafeArrayOut(ISharedFolder *, aSharedFolders)); 425 STDMETHOD(COMGETTER(ClipboardMode))(ClipboardMode_T *aClipboardMode); 426 STDMETHOD(COMSETTER(ClipboardMode))(ClipboardMode_T aClipboardMode); 427 STDMETHOD(COMGETTER(GuestPropertyNotificationPatterns))(BSTR *aPattern); 428 STDMETHOD(COMSETTER(GuestPropertyNotificationPatterns))(IN_BSTR aPattern); 429 STDMETHOD(COMGETTER(StorageControllers))(ComSafeArrayOut(IStorageController *, aStorageControllers)); 430 STDMETHOD(COMGETTER(TeleporterEnabled))(BOOL *aEnabled); 431 STDMETHOD(COMSETTER(TeleporterEnabled))(BOOL aEnabled); 432 STDMETHOD(COMGETTER(TeleporterPort))(ULONG *aPort); 433 STDMETHOD(COMSETTER(TeleporterPort))(ULONG aPort); 434 STDMETHOD(COMGETTER(TeleporterAddress))(BSTR *aAddress); 435 STDMETHOD(COMSETTER(TeleporterAddress))(IN_BSTR aAddress); 436 STDMETHOD(COMGETTER(TeleporterPassword))(BSTR *aPassword); 437 STDMETHOD(COMSETTER(TeleporterPassword))(IN_BSTR aPassword); 438 STDMETHOD(COMGETTER(FaultToleranceState))(FaultToleranceState_T *aEnabled); 439 STDMETHOD(COMSETTER(FaultToleranceState))(FaultToleranceState_T aEnabled); 440 STDMETHOD(COMGETTER(FaultToleranceAddress))(BSTR *aAddress); 441 STDMETHOD(COMSETTER(FaultToleranceAddress))(IN_BSTR aAddress); 442 STDMETHOD(COMGETTER(FaultTolerancePort))(ULONG *aPort); 443 STDMETHOD(COMSETTER(FaultTolerancePort))(ULONG aPort); 444 STDMETHOD(COMGETTER(FaultTolerancePassword))(BSTR *aPassword); 445 STDMETHOD(COMSETTER(FaultTolerancePassword))(IN_BSTR aPassword); 446 STDMETHOD(COMGETTER(FaultToleranceSyncInterval))(ULONG *aInterval); 447 STDMETHOD(COMSETTER(FaultToleranceSyncInterval))(ULONG aInterval); 448 STDMETHOD(COMGETTER(RTCUseUTC))(BOOL *aEnabled); 449 STDMETHOD(COMSETTER(RTCUseUTC))(BOOL aEnabled); 450 STDMETHOD(COMGETTER(FirmwareType)) (FirmwareType_T *aFirmware); 451 STDMETHOD(COMSETTER(FirmwareType)) (FirmwareType_T aFirmware); 452 STDMETHOD(COMGETTER(KeyboardHidType)) (KeyboardHidType_T *aKeyboardHidType); 453 STDMETHOD(COMSETTER(KeyboardHidType)) (KeyboardHidType_T aKeyboardHidType); 454 STDMETHOD(COMGETTER(PointingHidType)) (PointingHidType_T *aPointingHidType); 455 STDMETHOD(COMSETTER(PointingHidType)) (PointingHidType_T aPointingHidType); 456 STDMETHOD(COMGETTER(ChipsetType)) (ChipsetType_T *aChipsetType); 457 STDMETHOD(COMSETTER(ChipsetType)) (ChipsetType_T aChipsetType); 458 STDMETHOD(COMGETTER(IoCacheEnabled)) (BOOL *aEnabled); 459 STDMETHOD(COMSETTER(IoCacheEnabled)) (BOOL aEnabled); 460 STDMETHOD(COMGETTER(IoCacheSize)) (ULONG *aIoCacheSize); 461 STDMETHOD(COMSETTER(IoCacheSize)) (ULONG aIoCacheSize); 462 STDMETHOD(COMGETTER(PciDeviceAssignments))(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments)); 463 STDMETHOD(COMGETTER(BandwidthControl))(IBandwidthControl **aBandwidthControl); 38 /* d-pointer */ 39 MachineCloneVM(MachineCloneVMPrivate &d); 40 MachineCloneVMPrivate *d_ptr; 464 41 465 // IMachine methods 466 STDMETHOD(LockMachine)(ISession *aSession, LockType_T lockType); 467 STDMETHOD(LaunchVMProcess)(ISession *aSession, IN_BSTR aType, IN_BSTR aEnvironment, IProgress **aProgress); 468 469 STDMETHOD(SetBootOrder)(ULONG aPosition, DeviceType_T aDevice); 470 STDMETHOD(GetBootOrder)(ULONG aPosition, DeviceType_T *aDevice); 471 STDMETHOD(AttachDevice)(IN_BSTR aControllerName, LONG aControllerPort, 472 LONG aDevice, DeviceType_T aType, IMedium *aMedium); 473 STDMETHOD(DetachDevice)(IN_BSTR aControllerName, LONG aControllerPort, LONG aDevice); 474 STDMETHOD(PassthroughDevice)(IN_BSTR aControllerName, LONG aControllerPort, LONG aDevice, BOOL aPassthrough); 475 STDMETHOD(SetBandwidthGroupForDevice)(IN_BSTR aControllerName, LONG aControllerPort, 476 LONG aDevice, IBandwidthGroup *aBandwidthGroup); 477 STDMETHOD(MountMedium)(IN_BSTR aControllerName, LONG aControllerPort, 478 LONG aDevice, IMedium *aMedium, BOOL aForce); 479 STDMETHOD(GetMedium)(IN_BSTR aControllerName, LONG aControllerPort, LONG aDevice, 480 IMedium **aMedium); 481 STDMETHOD(GetSerialPort)(ULONG slot, ISerialPort **port); 482 STDMETHOD(GetParallelPort)(ULONG slot, IParallelPort **port); 483 STDMETHOD(GetNetworkAdapter)(ULONG slot, INetworkAdapter **adapter); 484 STDMETHOD(GetExtraDataKeys)(ComSafeArrayOut(BSTR, aKeys)); 485 STDMETHOD(GetExtraData)(IN_BSTR aKey, BSTR *aValue); 486 STDMETHOD(SetExtraData)(IN_BSTR aKey, IN_BSTR aValue); 487 STDMETHOD(GetCPUProperty)(CPUPropertyType_T property, BOOL *aVal); 488 STDMETHOD(SetCPUProperty)(CPUPropertyType_T property, BOOL aVal); 489 STDMETHOD(GetCPUIDLeaf)(ULONG id, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx); 490 STDMETHOD(SetCPUIDLeaf)(ULONG id, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx); 491 STDMETHOD(RemoveCPUIDLeaf)(ULONG id); 492 STDMETHOD(RemoveAllCPUIDLeaves)(); 493 STDMETHOD(GetHWVirtExProperty)(HWVirtExPropertyType_T property, BOOL *aVal); 494 STDMETHOD(SetHWVirtExProperty)(HWVirtExPropertyType_T property, BOOL aVal); 495 STDMETHOD(SaveSettings)(); 496 STDMETHOD(DiscardSettings)(); 497 STDMETHOD(Unregister)(CleanupMode_T cleanupMode, ComSafeArrayOut(IMedium*, aMedia)); 498 STDMETHOD(Delete)(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress); 499 STDMETHOD(Export)(IAppliance *aAppliance, IN_BSTR location, IVirtualSystemDescription **aDescription); 500 STDMETHOD(FindSnapshot)(IN_BSTR aNameOrId, ISnapshot **aSnapshot); 501 STDMETHOD(CreateSharedFolder)(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount); 502 STDMETHOD(RemoveSharedFolder)(IN_BSTR aName); 503 STDMETHOD(CanShowConsoleWindow)(BOOL *aCanShow); 504 STDMETHOD(ShowConsoleWindow)(LONG64 *aWinId); 505 STDMETHOD(GetGuestProperty)(IN_BSTR aName, BSTR *aValue, LONG64 *aTimestamp, BSTR *aFlags); 506 STDMETHOD(GetGuestPropertyValue)(IN_BSTR aName, BSTR *aValue); 507 STDMETHOD(GetGuestPropertyTimestamp)(IN_BSTR aName, LONG64 *aTimestamp); 508 STDMETHOD(SetGuestProperty)(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags); 509 STDMETHOD(SetGuestPropertyValue)(IN_BSTR aName, IN_BSTR aValue); 510 STDMETHOD(EnumerateGuestProperties)(IN_BSTR aPattern, ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(BSTR, aValues), ComSafeArrayOut(LONG64, aTimestamps), ComSafeArrayOut(BSTR, aFlags)); 511 STDMETHOD(GetMediumAttachmentsOfController)(IN_BSTR aName, ComSafeArrayOut(IMediumAttachment *, aAttachments)); 512 STDMETHOD(GetMediumAttachment)(IN_BSTR aConstrollerName, LONG aControllerPort, LONG aDevice, IMediumAttachment **aAttachment); 513 STDMETHOD(AddStorageController)(IN_BSTR aName, StorageBus_T aConnectionType, IStorageController **controller); 514 STDMETHOD(RemoveStorageController(IN_BSTR aName)); 515 STDMETHOD(GetStorageControllerByName(IN_BSTR aName, IStorageController **storageController)); 516 STDMETHOD(GetStorageControllerByInstance(ULONG aInstance, IStorageController **storageController)); 517 STDMETHOD(SetStorageControllerBootable)(IN_BSTR aName, BOOL fBootable); 518 STDMETHOD(QuerySavedGuestSize)(ULONG aScreenId, ULONG *puWidth, ULONG *puHeight); 519 STDMETHOD(QuerySavedThumbnailSize)(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight); 520 STDMETHOD(ReadSavedThumbnailToArray)(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)); 521 STDMETHOD(ReadSavedThumbnailPNGToArray)(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)); 522 STDMETHOD(QuerySavedScreenshotPNGSize)(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight); 523 STDMETHOD(ReadSavedScreenshotPNGToArray)(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)); 524 STDMETHOD(HotPlugCPU(ULONG aCpu)); 525 STDMETHOD(HotUnplugCPU(ULONG aCpu)); 526 STDMETHOD(GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)); 527 STDMETHOD(QueryLogFilename(ULONG aIdx, BSTR *aName)); 528 STDMETHOD(ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))); 529 STDMETHOD(AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL tryToUnbind)); 530 STDMETHOD(DetachHostPciDevice(LONG hostAddress)); 531 STDMETHOD(CloneTo(IMachine *aTarget, CloneMode_T mode, BOOL aFullClone, IProgress **aProgress)); 532 // public methods only for internal purposes 533 534 virtual bool isSnapshotMachine() const 535 { 536 return false; 537 } 538 539 virtual bool isSessionMachine() const 540 { 541 return false; 542 } 543 544 /** 545 * Override of the default locking class to be used for validating lock 546 * order with the standard member lock handle. 547 */ 548 virtual VBoxLockingClass getLockingClass() const 549 { 550 return LOCKCLASS_MACHINEOBJECT; 551 } 552 553 /// @todo (dmik) add lock and make non-inlined after revising classes 554 // that use it. Note: they should enter Machine lock to keep the returned 555 // information valid! 556 bool isRegistered() { return !!mData->mRegistered; } 557 558 // unsafe inline public methods for internal purposes only (ensure there is 559 // a caller and a read lock before calling them!) 560 561 /** 562 * Returns the VirtualBox object this machine belongs to. 563 * 564 * @note This method doesn't check this object's readiness. Intended to be 565 * used by ready Machine children (whose readiness is bound to the parent's 566 * one) or after doing addCaller() manually. 567 */ 568 VirtualBox* getVirtualBox() const { return mParent; } 569 570 /** 571 * Returns this machine ID. 572 * 573 * @note This method doesn't check this object's readiness. Intended to be 574 * used by ready Machine children (whose readiness is bound to the parent's 575 * one) or after adding a caller manually. 576 */ 577 const Guid& getId() const { return mData->mUuid; } 578 579 /** 580 * Returns the snapshot ID this machine represents or an empty UUID if this 581 * instance is not SnapshotMachine. 582 * 583 * @note This method doesn't check this object's readiness. Intended to be 584 * used by ready Machine children (whose readiness is bound to the parent's 585 * one) or after adding a caller manually. 586 */ 587 inline const Guid& getSnapshotId() const; 588 589 /** 590 * Returns this machine's full settings file path. 591 * 592 * @note This method doesn't lock this object or check its readiness. 593 * Intended to be used only after doing addCaller() manually and locking it 594 * for reading. 595 */ 596 const Utf8Str& getSettingsFileFull() const { return mData->m_strConfigFileFull; } 597 598 /** 599 * Returns this machine name. 600 * 601 * @note This method doesn't lock this object or check its readiness. 602 * Intended to be used only after doing addCaller() manually and locking it 603 * for reading. 604 */ 605 const Utf8Str& getName() const { return mUserData->s.strName; } 606 607 enum 608 { 609 IsModified_MachineData = 0x0001, 610 IsModified_Storage = 0x0002, 611 IsModified_NetworkAdapters = 0x0008, 612 IsModified_SerialPorts = 0x0010, 613 IsModified_ParallelPorts = 0x0020, 614 IsModified_VRDEServer = 0x0040, 615 IsModified_AudioAdapter = 0x0080, 616 IsModified_USB = 0x0100, 617 IsModified_BIOS = 0x0200, 618 IsModified_SharedFolders = 0x0400, 619 IsModified_Snapshots = 0x0800, 620 IsModified_BandwidthControl = 0x1000 621 }; 622 623 void setModified(uint32_t fl); 624 void setModifiedLock(uint32_t fl); 625 626 // callback handlers 627 virtual HRESULT onNetworkAdapterChange(INetworkAdapter * /* networkAdapter */, BOOL /* changeAdapter */) { return S_OK; } 628 virtual HRESULT onNATRedirectRuleChange(ULONG /* slot */, BOOL /* fRemove */ , IN_BSTR /* name */, 629 NATProtocol_T /* protocol */, IN_BSTR /* host ip */, LONG /* host port */, IN_BSTR /* guest port */, LONG /* guest port */ ) { return S_OK; } 630 virtual HRESULT onSerialPortChange(ISerialPort * /* serialPort */) { return S_OK; } 631 virtual HRESULT onParallelPortChange(IParallelPort * /* parallelPort */) { return S_OK; } 632 virtual HRESULT onVRDEServerChange(BOOL /* aRestart */) { return S_OK; } 633 virtual HRESULT onUSBControllerChange() { return S_OK; } 634 virtual HRESULT onStorageControllerChange() { return S_OK; } 635 virtual HRESULT onCPUChange(ULONG /* aCPU */, BOOL /* aRemove */) { return S_OK; } 636 virtual HRESULT onCPUExecutionCapChange(ULONG /* aExecutionCap */) { return S_OK; } 637 virtual HRESULT onMediumChange(IMediumAttachment * /* mediumAttachment */, BOOL /* force */) { return S_OK; } 638 virtual HRESULT onSharedFolderChange() { return S_OK; } 639 virtual HRESULT onBandwidthGroupChange(IBandwidthGroup * /* aBandwidthGroup */) { return S_OK; } 640 virtual HRESULT onStorageDeviceChange(IMediumAttachment * /* mediumAttachment */, BOOL /* remove */) { return S_OK; } 641 642 HRESULT saveRegistryEntry(settings::MachineRegistryEntry &data); 643 644 int calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult); 645 void copyPathRelativeToMachine(const Utf8Str &strSource, Utf8Str &strTarget); 646 647 void getLogFolder(Utf8Str &aLogFolder); 648 Utf8Str queryLogFilename(ULONG idx); 649 650 void composeSavedStateFilename(Utf8Str &strStateFilePath); 651 652 HRESULT launchVMProcess(IInternalSessionControl *aControl, 653 const Utf8Str &strType, 654 const Utf8Str &strEnvironment, 655 ProgressProxy *aProgress); 656 657 HRESULT getDirectControl(ComPtr<IInternalSessionControl> *directControl) 658 { 659 HRESULT rc; 660 *directControl = mData->mSession.mDirectControl; 661 662 if (!*directControl) 663 rc = E_ACCESSDENIED; 664 else 665 rc = S_OK; 666 667 return rc; 668 } 669 670 #if defined(RT_OS_WINDOWS) 671 672 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 673 ComPtr<IInternalSessionControl> *aControl = NULL, 674 HANDLE *aIPCSem = NULL, bool aAllowClosing = false); 675 bool isSessionSpawning(RTPROCESS *aPID = NULL); 676 677 bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine, 678 ComPtr<IInternalSessionControl> *aControl = NULL, 679 HANDLE *aIPCSem = NULL) 680 { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); } 681 682 #elif defined(RT_OS_OS2) 683 684 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 685 ComPtr<IInternalSessionControl> *aControl = NULL, 686 HMTX *aIPCSem = NULL, bool aAllowClosing = false); 687 688 bool isSessionSpawning(RTPROCESS *aPID = NULL); 689 690 bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine, 691 ComPtr<IInternalSessionControl> *aControl = NULL, 692 HMTX *aIPCSem = NULL) 693 { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); } 694 695 #else 696 697 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 698 ComPtr<IInternalSessionControl> *aControl = NULL, 699 bool aAllowClosing = false); 700 bool isSessionSpawning(); 701 702 bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine, 703 ComPtr<IInternalSessionControl> *aControl = NULL) 704 { return isSessionOpen(aMachine, aControl, true /* aAllowClosing */); } 705 706 #endif 707 708 bool checkForSpawnFailure(); 709 710 HRESULT prepareRegister(); 711 712 HRESULT getSharedFolder(CBSTR aName, 713 ComObjPtr<SharedFolder> &aSharedFolder, 714 bool aSetError = false) 715 { 716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 717 return findSharedFolder(aName, aSharedFolder, aSetError); 718 } 719 720 HRESULT addStateDependency(StateDependency aDepType = AnyStateDep, 721 MachineState_T *aState = NULL, 722 BOOL *aRegistered = NULL); 723 void releaseStateDependency(); 724 725 HRESULT getBandwidthGroup(const Utf8Str &strBandwidthGroup, 726 ComObjPtr<BandwidthGroup> &pBandwidthGroup, 727 bool fSetError = false) 728 { 729 return mBandwidthControl->getBandwidthGroupByName(strBandwidthGroup, 730 pBandwidthGroup, 731 fSetError); 732 } 733 734 protected: 735 736 HRESULT checkStateDependency(StateDependency aDepType); 737 738 Machine *getMachine(); 739 740 void ensureNoStateDependencies(); 741 742 virtual HRESULT setMachineState(MachineState_T aMachineState); 743 744 HRESULT findSharedFolder(const Utf8Str &aName, 745 ComObjPtr<SharedFolder> &aSharedFolder, 746 bool aSetError = false); 747 748 HRESULT loadSettings(bool aRegistered); 749 HRESULT loadMachineDataFromSettings(const settings::MachineConfigFile &config, 750 const Guid *puuidRegistry); 751 HRESULT loadSnapshot(const settings::Snapshot &data, 752 const Guid &aCurSnapshotId, 753 Snapshot *aParentSnapshot); 754 HRESULT loadHardware(const settings::Hardware &data); 755 HRESULT loadStorageControllers(const settings::Storage &data, 756 const Guid *puuidRegistry, 757 const Guid *puuidSnapshot); 758 HRESULT loadStorageDevices(StorageController *aStorageController, 759 const settings::StorageController &data, 760 const Guid *puuidRegistry, 761 const Guid *puuidSnapshot); 762 763 HRESULT findSnapshotById(const Guid &aId, 764 ComObjPtr<Snapshot> &aSnapshot, 765 bool aSetError = false); 766 HRESULT findSnapshotByName(const Utf8Str &strName, 767 ComObjPtr<Snapshot> &aSnapshot, 768 bool aSetError = false); 769 770 HRESULT getStorageControllerByName(const Utf8Str &aName, 771 ComObjPtr<StorageController> &aStorageController, 772 bool aSetError = false); 773 774 HRESULT getMediumAttachmentsOfController(CBSTR aName, 775 MediaData::AttachmentList &aAttachments); 776 777 enum 778 { 779 /* flags for #saveSettings() */ 780 SaveS_ResetCurStateModified = 0x01, 781 SaveS_InformCallbacksAnyway = 0x02, 782 SaveS_Force = 0x04, 783 /* flags for #saveStateSettings() */ 784 SaveSTS_CurStateModified = 0x20, 785 SaveSTS_StateFilePath = 0x40, 786 SaveSTS_StateTimeStamp = 0x80 787 }; 788 789 HRESULT prepareSaveSettings(bool *pfNeedsGlobalSaveSettings); 790 HRESULT saveSettings(bool *pfNeedsGlobalSaveSettings, int aFlags = 0); 791 792 void copyMachineDataToSettings(settings::MachineConfigFile &config); 793 HRESULT saveAllSnapshots(settings::MachineConfigFile &config); 794 HRESULT saveHardware(settings::Hardware &data); 795 HRESULT saveStorageControllers(settings::Storage &data); 796 HRESULT saveStorageDevices(ComObjPtr<StorageController> aStorageController, 797 settings::StorageController &data); 798 HRESULT saveStateSettings(int aFlags); 799 800 void addMediumToRegistry(ComObjPtr<Medium> &pMedium, 801 GuidList &llRegistriesThatNeedSaving, 802 Guid *puuid); 803 804 HRESULT createImplicitDiffs(IProgress *aProgress, 805 ULONG aWeight, 806 bool aOnline, 807 GuidList *pllRegistriesThatNeedSaving); 808 HRESULT deleteImplicitDiffs(GuidList *pllRegistriesThatNeedSaving); 809 810 MediumAttachment* findAttachment(const MediaData::AttachmentList &ll, 811 IN_BSTR aControllerName, 812 LONG aControllerPort, 813 LONG aDevice); 814 MediumAttachment* findAttachment(const MediaData::AttachmentList &ll, 815 ComObjPtr<Medium> pMedium); 816 MediumAttachment* findAttachment(const MediaData::AttachmentList &ll, 817 Guid &id); 818 819 HRESULT detachDevice(MediumAttachment *pAttach, 820 AutoWriteLock &writeLock, 821 Snapshot *pSnapshot, 822 GuidList *pllRegistriesThatNeedSaving); 823 HRESULT detachAllMedia(AutoWriteLock &writeLock, 824 Snapshot *pSnapshot, 825 CleanupMode_T cleanupMode, 826 MediaList &llMedia); 827 828 void commitMedia(bool aOnline = false); 829 void rollbackMedia(); 830 831 bool isInOwnDir(Utf8Str *aSettingsDir = NULL) const; 832 833 void rollback(bool aNotify); 834 void commit(); 835 void copyFrom(Machine *aThat); 836 bool isControllerHotplugCapable(StorageControllerType_T enmCtrlType); 837 838 struct DeleteTask; 839 static DECLCALLBACK(int) deleteThread(RTTHREAD Thread, void *pvUser); 840 HRESULT deleteTaskWorker(DeleteTask &task); 841 842 struct CloneVMTask; 843 HRESULT cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const; 844 settings::Snapshot cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const; 845 void cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const; 846 void cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const; 847 void cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const; 848 static int cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser); 849 850 static DECLCALLBACK(int) cloneVMThread(RTTHREAD Thread, void *pvUser); 851 HRESULT cloneVMTaskWorker(CloneVMTask *pTask); 852 853 #ifdef VBOX_WITH_GUEST_PROPS 854 HRESULT getGuestPropertyFromService(IN_BSTR aName, BSTR *aValue, 855 LONG64 *aTimestamp, BSTR *aFlags) const; 856 HRESULT getGuestPropertyFromVM(IN_BSTR aName, BSTR *aValue, 857 LONG64 *aTimestamp, BSTR *aFlags) const; 858 HRESULT setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, 859 IN_BSTR aFlags); 860 HRESULT setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue, 861 IN_BSTR aFlags); 862 HRESULT enumerateGuestPropertiesInService 863 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 864 ComSafeArrayOut(BSTR, aValues), 865 ComSafeArrayOut(LONG64, aTimestamps), 866 ComSafeArrayOut(BSTR, aFlags)); 867 HRESULT enumerateGuestPropertiesOnVM 868 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 869 ComSafeArrayOut(BSTR, aValues), 870 ComSafeArrayOut(LONG64, aTimestamps), 871 ComSafeArrayOut(BSTR, aFlags)); 872 #endif /* VBOX_WITH_GUEST_PROPS */ 873 874 #ifdef VBOX_WITH_RESOURCE_USAGE_API 875 void registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid); 876 877 pm::CollectorGuest *mCollectorGuest; 878 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 879 880 Machine* const mPeer; 881 882 VirtualBox * const mParent; 883 884 Shareable<Data> mData; 885 Shareable<SSData> mSSData; 886 887 Backupable<UserData> mUserData; 888 Backupable<HWData> mHWData; 889 Backupable<MediaData> mMediaData; 890 891 // the following fields need special backup/rollback/commit handling, 892 // so they cannot be a part of HWData 893 894 const ComObjPtr<VRDEServer> mVRDEServer; 895 const ComObjPtr<SerialPort> mSerialPorts[SchemaDefs::SerialPortCount]; 896 const ComObjPtr<ParallelPort> mParallelPorts[SchemaDefs::ParallelPortCount]; 897 const ComObjPtr<AudioAdapter> mAudioAdapter; 898 const ComObjPtr<USBController> mUSBController; 899 const ComObjPtr<BIOSSettings> mBIOSSettings; 900 const ComObjPtr<NetworkAdapter> mNetworkAdapters[SchemaDefs::NetworkAdapterCount]; 901 const ComObjPtr<BandwidthControl> mBandwidthControl; 902 903 typedef std::list< ComObjPtr<StorageController> > StorageControllerList; 904 Backupable<StorageControllerList> mStorageControllers; 905 906 friend class SessionMachine; 907 friend class SnapshotMachine; 908 friend class Appliance; 909 friend class VirtualBox; 42 friend class MachineCloneVMPrivate; 910 43 }; 911 44 912 // SessionMachine class 913 / ///////////////////////////////////////////////////////////////////////////////45 #endif // ____H_MACHINEIMPLCLONEVM 46 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ 914 47 915 /**916 * @note Notes on locking objects of this class:917 * SessionMachine shares some data with the primary Machine instance (pointed918 * to by the |mPeer| member). In order to provide data consistency it also919 * shares its lock handle. This means that whenever you lock a SessionMachine920 * instance using Auto[Reader]Lock or AutoMultiLock, the corresponding Machine921 * instance is also locked in the same lock mode. Keep it in mind.922 */923 class ATL_NO_VTABLE SessionMachine :924 public Machine,925 VBOX_SCRIPTABLE_IMPL(IInternalMachineControl)926 {927 public:928 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(SessionMachine, IMachine)929 930 DECLARE_NOT_AGGREGATABLE(SessionMachine)931 932 DECLARE_PROTECT_FINAL_CONSTRUCT()933 934 BEGIN_COM_MAP(SessionMachine)935 VBOX_DEFAULT_INTERFACE_ENTRIES(IMachine)936 COM_INTERFACE_ENTRY(IInternalMachineControl)937 END_COM_MAP()938 939 DECLARE_EMPTY_CTOR_DTOR(SessionMachine)940 941 HRESULT FinalConstruct();942 void FinalRelease();943 944 // public initializer/uninitializer for internal purposes only945 HRESULT init(Machine *aMachine);946 void uninit() { uninit(Uninit::Unexpected); }947 948 // util::Lockable interface949 RWLockHandle *lockHandle() const;950 951 // IInternalMachineControl methods952 STDMETHOD(SetRemoveSavedStateFile)(BOOL aRemove);953 STDMETHOD(UpdateState)(MachineState_T machineState);954 STDMETHOD(GetIPCId)(BSTR *id);955 STDMETHOD(BeginPowerUp)(IProgress *aProgress);956 STDMETHOD(EndPowerUp)(LONG iResult);957 STDMETHOD(BeginPoweringDown)(IProgress **aProgress);958 STDMETHOD(EndPoweringDown)(LONG aResult, IN_BSTR aErrMsg);959 STDMETHOD(RunUSBDeviceFilters)(IUSBDevice *aUSBDevice, BOOL *aMatched, ULONG *aMaskedIfs);960 STDMETHOD(CaptureUSBDevice)(IN_BSTR aId);961 STDMETHOD(DetachUSBDevice)(IN_BSTR aId, BOOL aDone);962 STDMETHOD(AutoCaptureUSBDevices)();963 STDMETHOD(DetachAllUSBDevices)(BOOL aDone);964 STDMETHOD(OnSessionEnd)(ISession *aSession, IProgress **aProgress);965 STDMETHOD(BeginSavingState)(IProgress **aProgress, BSTR *aStateFilePath);966 STDMETHOD(EndSavingState)(LONG aResult, IN_BSTR aErrMsg);967 STDMETHOD(AdoptSavedState)(IN_BSTR aSavedStateFile);968 STDMETHOD(BeginTakingSnapshot)(IConsole *aInitiator,969 IN_BSTR aName,970 IN_BSTR aDescription,971 IProgress *aConsoleProgress,972 BOOL fTakingSnapshotOnline,973 BSTR *aStateFilePath);974 STDMETHOD(EndTakingSnapshot)(BOOL aSuccess);975 STDMETHOD(DeleteSnapshot)(IConsole *aInitiator, IN_BSTR aId,976 BOOL fDeleteAllChildren,977 MachineState_T *aMachineState, IProgress **aProgress);978 STDMETHOD(FinishOnlineMergeMedium)(IMediumAttachment *aMediumAttachment,979 IMedium *aSource, IMedium *aTarget,980 BOOL fMergeForward,981 IMedium *pParentForTarget,982 ComSafeArrayIn(IMedium *, aChildrenToReparent));983 STDMETHOD(RestoreSnapshot)(IConsole *aInitiator,984 ISnapshot *aSnapshot,985 MachineState_T *aMachineState,986 IProgress **aProgress);987 STDMETHOD(PullGuestProperties)(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(BSTR, aValues),988 ComSafeArrayOut(LONG64, aTimestamps), ComSafeArrayOut(BSTR, aFlags));989 STDMETHOD(PushGuestProperty)(IN_BSTR aName, IN_BSTR aValue,990 LONG64 aTimestamp, IN_BSTR aFlags);991 STDMETHOD(LockMedia)() { return lockMedia(); }992 STDMETHOD(UnlockMedia)() { unlockMedia(); return S_OK; }993 994 // public methods only for internal purposes995 996 virtual bool isSessionMachine() const997 {998 return true;999 }1000 1001 bool checkForDeath();1002 1003 HRESULT onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter);1004 HRESULT onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,1005 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort);1006 HRESULT onStorageControllerChange();1007 HRESULT onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce);1008 HRESULT onSerialPortChange(ISerialPort *serialPort);1009 HRESULT onParallelPortChange(IParallelPort *parallelPort);1010 HRESULT onCPUChange(ULONG aCPU, BOOL aRemove);1011 HRESULT onCPUExecutionCapChange(ULONG aCpuExecutionCap);1012 HRESULT onVRDEServerChange(BOOL aRestart);1013 HRESULT onUSBControllerChange();1014 HRESULT onUSBDeviceAttach(IUSBDevice *aDevice,1015 IVirtualBoxErrorInfo *aError,1016 ULONG aMaskedIfs);1017 HRESULT onUSBDeviceDetach(IN_BSTR aId,1018 IVirtualBoxErrorInfo *aError);1019 HRESULT onSharedFolderChange();1020 HRESULT onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup);1021 HRESULT onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove);1022 1023 bool hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs);1024 1025 private:1026 1027 struct ConsoleTaskData1028 {1029 ConsoleTaskData()1030 : mLastState(MachineState_Null)1031 { }1032 1033 MachineState_T mLastState;1034 ComObjPtr<Progress> mProgress;1035 1036 // used when taking snapshot1037 ComObjPtr<Snapshot> mSnapshot;1038 1039 // used when saving state (either as part of a snapshot or separate)1040 Utf8Str strStateFilePath;1041 };1042 1043 struct Uninit1044 {1045 enum Reason { Unexpected, Abnormal, Normal };1046 };1047 1048 struct SnapshotTask;1049 struct DeleteSnapshotTask;1050 struct RestoreSnapshotTask;1051 1052 friend struct DeleteSnapshotTask;1053 friend struct RestoreSnapshotTask;1054 1055 void uninit(Uninit::Reason aReason);1056 1057 HRESULT endSavingState(HRESULT aRC, const Utf8Str &aErrMsg);1058 void releaseSavedStateFile(const Utf8Str &strSavedStateFile, Snapshot *pSnapshotToIgnore);1059 1060 void deleteSnapshotHandler(DeleteSnapshotTask &aTask);1061 void restoreSnapshotHandler(RestoreSnapshotTask &aTask);1062 1063 HRESULT prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,1064 const Guid &machineId,1065 const Guid &snapshotId,1066 bool fOnlineMergePossible,1067 MediumLockList *aVMMALockList,1068 ComObjPtr<Medium> &aSource,1069 ComObjPtr<Medium> &aTarget,1070 bool &fMergeForward,1071 ComObjPtr<Medium> &pParentForTarget,1072 MediaList &aChildrenToReparent,1073 bool &fNeedOnlineMerge,1074 MediumLockList * &aMediumLockList);1075 void cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,1076 const ComObjPtr<Medium> &aSource,1077 const MediaList &aChildrenToReparent,1078 bool fNeedsOnlineMerge,1079 MediumLockList *aMediumLockList,1080 const Guid &aMediumId,1081 const Guid &aSnapshotId);1082 HRESULT onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMediumAttachment,1083 const ComObjPtr<Medium> &aSource,1084 const ComObjPtr<Medium> &aTarget,1085 bool fMergeForward,1086 const ComObjPtr<Medium> &pParentForTarget,1087 const MediaList &aChildrenToReparent,1088 MediumLockList *aMediumLockList,1089 ComObjPtr<Progress> &aProgress,1090 bool *pfNeedsMachineSaveSettings);1091 1092 HRESULT lockMedia();1093 void unlockMedia();1094 1095 HRESULT setMachineState(MachineState_T aMachineState);1096 HRESULT updateMachineStateOnClient();1097 1098 HRESULT mRemoveSavedState;1099 1100 ConsoleTaskData mConsoleTaskData;1101 1102 /** interprocess semaphore handle for this machine */1103 #if defined(RT_OS_WINDOWS)1104 HANDLE mIPCSem;1105 Bstr mIPCSemName;1106 friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,1107 ComPtr<IInternalSessionControl> *aControl,1108 HANDLE *aIPCSem, bool aAllowClosing);1109 #elif defined(RT_OS_OS2)1110 HMTX mIPCSem;1111 Bstr mIPCSemName;1112 friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,1113 ComPtr<IInternalSessionControl> *aControl,1114 HMTX *aIPCSem, bool aAllowClosing);1115 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)1116 int mIPCSem;1117 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN1118 Bstr mIPCKey;1119 # endif /*VBOX_WITH_NEW_SYS_V_KEYGEN */1120 #else1121 # error "Port me!"1122 #endif1123 1124 static DECLCALLBACK(int) taskHandler(RTTHREAD thread, void *pvUser);1125 };1126 1127 // SnapshotMachine class1128 ////////////////////////////////////////////////////////////////////////////////1129 1130 /**1131 * @note Notes on locking objects of this class:1132 * SnapshotMachine shares some data with the primary Machine instance (pointed1133 * to by the |mPeer| member). In order to provide data consistency it also1134 * shares its lock handle. This means that whenever you lock a SessionMachine1135 * instance using Auto[Reader]Lock or AutoMultiLock, the corresponding Machine1136 * instance is also locked in the same lock mode. Keep it in mind.1137 */1138 class ATL_NO_VTABLE SnapshotMachine :1139 public Machine1140 {1141 public:1142 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(SnapshotMachine, IMachine)1143 1144 DECLARE_NOT_AGGREGATABLE(SnapshotMachine)1145 1146 DECLARE_PROTECT_FINAL_CONSTRUCT()1147 1148 BEGIN_COM_MAP(SnapshotMachine)1149 VBOX_DEFAULT_INTERFACE_ENTRIES(IMachine)1150 END_COM_MAP()1151 1152 DECLARE_EMPTY_CTOR_DTOR(SnapshotMachine)1153 1154 HRESULT FinalConstruct();1155 void FinalRelease();1156 1157 // public initializer/uninitializer for internal purposes only1158 HRESULT init(SessionMachine *aSessionMachine,1159 IN_GUID aSnapshotId,1160 const Utf8Str &aStateFilePath);1161 HRESULT init(Machine *aMachine,1162 const settings::Hardware &hardware,1163 const settings::Storage &storage,1164 IN_GUID aSnapshotId,1165 const Utf8Str &aStateFilePath);1166 void uninit();1167 1168 // util::Lockable interface1169 RWLockHandle *lockHandle() const;1170 1171 // public methods only for internal purposes1172 1173 virtual bool isSnapshotMachine() const1174 {1175 return true;1176 }1177 1178 HRESULT onSnapshotChange(Snapshot *aSnapshot);1179 1180 // unsafe inline public methods for internal purposes only (ensure there is1181 // a caller and a read lock before calling them!)1182 1183 const Guid& getSnapshotId() const { return mSnapshotId; }1184 1185 private:1186 1187 Guid mSnapshotId;1188 1189 friend class Snapshot;1190 };1191 1192 // third party methods that depend on SnapshotMachine definition1193 1194 inline const Guid &Machine::getSnapshotId() const1195 {1196 return (isSnapshotMachine())1197 ? static_cast<const SnapshotMachine*>(this)->getSnapshotId()1198 : Guid::Empty;1199 }1200 1201 1202 #endif // ____H_MACHINEIMPL1203 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/src-server/MachineImpl.cpp
r37486 r37491 50 50 #include "DisplayUtils.h" 51 51 #include "BandwidthControlImpl.h" 52 #include "MachineImplCloneVM.h" 52 53 53 54 // generated header … … 6073 6074 } 6074 6075 6075 struct Machine::CloneVMTask6076 {6077 ComObjPtr<Machine> pSrcMachine;6078 ComObjPtr<Machine> pTrgMachine;6079 ComPtr<IMachine> pOldMachineState;6080 ComObjPtr<Progress> pProgress;6081 Guid snapshotId;6082 CloneMode_T mode;6083 bool fLinkDisks;6084 typedef struct6085 {6086 ComPtr<IMedium> pMedium;6087 uint64_t uSize;6088 }MediumTask;6089 typedef struct6090 {6091 RTCList<MediumTask> chain;6092 bool fCreateDiffs;6093 }MediumTaskChain;6094 RTCList<MediumTaskChain> llMedias;6095 typedef struct6096 {6097 Guid snapshotUuid;6098 Utf8Str strSaveStateFile;6099 uint64_t cbSize;6100 }SaveStateTask;6101 RTCList<SaveStateTask> llSaveStateFiles; /* Snapshot UUID -> File path */6102 };6103 6104 6076 STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, BOOL fFullClone, IProgress **pProgress) 6105 6077 { … … 6108 6080 CheckComArgNotNull(pTarget); 6109 6081 CheckComArgOutPointerValid(pProgress); 6110 6111 AutoCaller autoCaller(this); 6112 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6113 6114 AutoReadLock srcLock(this COMMA_LOCKVAL_SRC_POS); 6115 6116 CloneVMTask *pTask = NULL; 6117 6118 HRESULT rc; 6119 try 6120 { 6121 pTask = new CloneVMTask; 6122 pTask->pSrcMachine = this; 6123 pTask->pTrgMachine = static_cast<Machine*>(pTarget); 6124 pTask->fLinkDisks = !fFullClone; 6125 pTask->mode = mode; 6126 6127 /* Lock the target machine early (so nobody mess around with it in the meantime). */ 6128 AutoWriteLock trgLock(pTask->pTrgMachine COMMA_LOCKVAL_SRC_POS); 6129 6130 if (pTask->pSrcMachine->isSnapshotMachine()) 6131 pTask->snapshotId = pTask->pSrcMachine->getSnapshotId(); 6132 6133 /* Add the current machine and all snapshot machines below this machine 6134 * in a list for further processing. */ 6135 RTCList< ComObjPtr<Machine> > machineList; 6136 6137 /* Include current state? */ 6138 if ( pTask->mode == CloneMode_MachineState) 6139 // || pTask->mode == CloneMode_AllStates) 6140 machineList.append(pTask->pSrcMachine); 6141 /* Should be done a depth copy with all child snapshots? */ 6142 if ( pTask->mode == CloneMode_MachineAndChildStates 6143 || pTask->mode == CloneMode_AllStates) 6144 { 6145 ULONG cSnapshots = 0; 6146 rc = pTask->pSrcMachine->COMGETTER(SnapshotCount)(&cSnapshots); 6147 if (FAILED(rc)) throw rc; 6148 if (cSnapshots > 0) 6149 { 6150 Utf8Str id; 6151 if ( pTask->mode == CloneMode_MachineAndChildStates 6152 && !pTask->snapshotId.isEmpty()) 6153 id = pTask->snapshotId.toString(); 6154 ComPtr<ISnapshot> pSnapshot; 6155 rc = pTask->pSrcMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam()); 6156 if (FAILED(rc)) throw rc; 6157 rc = cloneCreateMachineList(pSnapshot, machineList); 6158 if (FAILED(rc)) throw rc; 6159 rc = pSnapshot->COMGETTER(Machine)(pTask->pOldMachineState.asOutParam()); 6160 if (FAILED(rc)) throw rc; 6161 } 6162 } 6163 6164 /* Go over every machine and walk over every attachment this machine has. */ 6165 ULONG uCount = 2; /* One init task and the machine creation. */ 6166 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */ 6167 for (size_t i = 0; i < machineList.size(); ++i) 6168 { 6169 ComObjPtr<Machine> machine = machineList.at(i); 6170 /* If this is the Snapshot Machine we want to clone, we need to 6171 * create a new diff file for the new "current state". */ 6172 bool fCreateDiffs = false; 6173 if (machine == pTask->pOldMachineState) 6174 fCreateDiffs = true; 6175 SafeIfaceArray<IMediumAttachment> sfaAttachments; 6176 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments)); 6177 if (FAILED(rc)) throw rc; 6178 /* Add all attachments (and there parents) of the different 6179 * machines to a worker list. */ 6180 for (size_t a = 0; a < sfaAttachments.size(); ++a) 6181 { 6182 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a]; 6183 DeviceType_T type; 6184 rc = pAtt->COMGETTER(Type)(&type); 6185 if (FAILED(rc)) throw rc; 6186 6187 /* Only harddisk's are of interest. */ 6188 if (type != DeviceType_HardDisk) 6189 continue; 6190 6191 /* Valid medium attached? */ 6192 ComPtr<IMedium> pSrcMedium; 6193 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam()); 6194 if (FAILED(rc)) throw rc; 6195 if (pSrcMedium.isNull()) 6196 continue; 6197 6198 /* Build up a child->parent list of this attachment. (Note: we are 6199 * not interested of any child's not attached to this VM. So this 6200 * will not create a full copy of the base/child relationship.) */ 6201 Machine::CloneVMTask::MediumTaskChain mtc; 6202 mtc.fCreateDiffs = fCreateDiffs; 6203 while(!pSrcMedium.isNull()) 6204 { 6205 /* Refresh the state so that the file size get read. */ 6206 MediumState_T e; 6207 rc = pSrcMedium->RefreshState(&e); 6208 if (FAILED(rc)) throw rc; 6209 LONG64 lSize; 6210 rc = pSrcMedium->COMGETTER(Size)(&lSize); 6211 if (FAILED(rc)) throw rc; 6212 6213 /* Save the current medium, for later cloning. */ 6214 Machine::CloneVMTask::MediumTask mt; 6215 mt.pMedium = pSrcMedium; 6216 mt.uSize = lSize; 6217 mtc.chain.append(mt); 6218 6219 /* Calculate progress data */ 6220 ++uCount; 6221 uTotalWeight += lSize; 6222 6223 /* Query next parent. */ 6224 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam()); 6225 if (FAILED(rc)) throw rc; 6226 }; 6227 pTask->llMedias.append(mtc); 6228 } 6229 Bstr bstrSrcSaveStatePath; 6230 rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam()); 6231 if (FAILED(rc)) throw rc; 6232 if (!bstrSrcSaveStatePath.isEmpty()) 6233 { 6234 Machine::CloneVMTask::SaveStateTask sst; 6235 sst.snapshotUuid = machine->getSnapshotId(); 6236 sst.strSaveStateFile = bstrSrcSaveStatePath; 6237 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &sst.cbSize); 6238 if (RT_FAILURE(vrc)) 6239 throw setError(VBOX_E_IPRT_ERROR, tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc); 6240 pTask->llSaveStateFiles.append(sst); 6241 ++uCount; 6242 uTotalWeight += sst.cbSize; 6243 } 6244 } 6245 6246 rc = pTask->pProgress.createObject(); 6247 if (FAILED(rc)) throw rc; 6248 rc = pTask->pProgress->init(getVirtualBox(), 6249 static_cast<IMachine*>(this) /* aInitiator */, 6250 Bstr(tr("Cloning Machine")).raw(), 6251 true /* fCancellable */, 6252 uCount, 6253 uTotalWeight, 6254 Bstr(tr("Initialize Cloning")).raw(), 6255 1); 6256 if (FAILED(rc)) throw rc; 6257 6258 int vrc = RTThreadCreate(NULL, 6259 Machine::cloneVMThread, 6260 static_cast<void*>(pTask), 6261 0, 6262 RTTHREADTYPE_MAIN_WORKER, 6263 0, 6264 "MachineClone"); 6265 if (RT_FAILURE(vrc)) 6266 throw setError(VBOX_E_IPRT_ERROR, "Could not create machine clone thread (%Rrc)", vrc); 6267 } 6268 catch (HRESULT rc2) 6269 { 6270 if (pTask) 6271 delete pTask; 6272 rc = rc2; 6273 } 6274 6275 if (SUCCEEDED(rc)) 6276 pTask->pProgress.queryInterfaceTo(pProgress); 6082 AssertReturn(!fFullClone, E_NOTIMPL); 6083 6084 AutoCaller autoCaller(this); 6085 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6086 6087 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, fFullClone); 6088 6089 HRESULT rc = pWorker->start(pProgress); 6277 6090 6278 6091 LogFlowFuncLeave(); 6279 6280 return rc;6281 }6282 6283 /**6284 * Static task wrapper passed to RTThreadCreate() in Machine::CloneTo() which then6285 * calls Machine::cloneVMTaskWorker() on the actual machine object.6286 * @param Thread6287 * @param pvUser6288 * @return6289 */6290 /* static */6291 DECLCALLBACK(int) Machine::cloneVMThread(RTTHREAD /* Thread */, void *pvUser)6292 {6293 LogFlowFuncEnter();6294 6295 CloneVMTask *pTask = static_cast<CloneVMTask*>(pvUser);6296 AssertReturn(pTask, VERR_INVALID_POINTER);6297 AssertReturn(pTask->pSrcMachine, VERR_INVALID_POINTER);6298 AssertReturn(pTask->pTrgMachine, VERR_INVALID_POINTER);6299 AssertReturn(pTask->pProgress, VERR_INVALID_POINTER);6300 6301 HRESULT rc = pTask->pSrcMachine->cloneVMTaskWorker(pTask);6302 pTask->pProgress->notifyComplete(rc);6303 6304 delete pTask;6305 6306 LogFlowFuncLeave();6307 6308 return VINF_SUCCESS;6309 }6310 6311 HRESULT Machine::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const6312 {6313 HRESULT rc = S_OK;6314 Bstr name;6315 rc = pSnapshot->COMGETTER(Name)(name.asOutParam());6316 if (FAILED(rc)) return rc;6317 6318 ComPtr<IMachine> pMachine;6319 rc = pSnapshot->COMGETTER(Machine)(pMachine.asOutParam());6320 if (FAILED(rc)) return rc;6321 machineList.append((Machine*)(IMachine*)pMachine);6322 6323 SafeIfaceArray<ISnapshot> sfaChilds;6324 rc = pSnapshot->COMGETTER(Children)(ComSafeArrayAsOutParam(sfaChilds));6325 if (FAILED(rc)) return rc;6326 for (size_t i = 0; i < sfaChilds.size(); ++i)6327 {6328 rc = cloneCreateMachineList(sfaChilds[i], machineList);6329 if (FAILED(rc)) return rc;6330 }6331 6332 return rc;6333 }6334 6335 settings::Snapshot Machine::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const6336 {6337 settings::SnapshotsList::const_iterator it;6338 for (it = snl.begin(); it != snl.end(); ++it)6339 {6340 if (it->uuid == id)6341 return *it;6342 else if (!it->llChildSnapshots.empty())6343 return cloneFindSnapshot(pMCF, it->llChildSnapshots, id);6344 }6345 return settings::Snapshot();6346 }6347 6348 void Machine::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const6349 {6350 settings::StorageControllersList::iterator it3;6351 for (it3 = sc.begin();6352 it3 != sc.end();6353 ++it3)6354 {6355 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;6356 settings::AttachedDevicesList::iterator it4;6357 for (it4 = llAttachments.begin();6358 it4 != llAttachments.end();6359 ++it4)6360 {6361 if ( it4->deviceType == DeviceType_HardDisk6362 && it4->uuid == bstrOldId)6363 {6364 it4->uuid = bstrNewId;6365 }6366 }6367 }6368 }6369 6370 void Machine::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const6371 {6372 settings::SnapshotsList::iterator it;6373 for ( it = sl.begin();6374 it != sl.end();6375 ++it)6376 {6377 cloneUpdateStorageLists(it->storage.llStorageControllers, bstrOldId, bstrNewId);6378 if (!it->llChildSnapshots.empty())6379 cloneUpdateSnapshotStorageLists(it->llChildSnapshots, bstrOldId, bstrNewId);6380 }6381 }6382 6383 void Machine::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const6384 {6385 settings::SnapshotsList::iterator it;6386 for (it = snl.begin(); it != snl.end(); ++it)6387 {6388 if (it->uuid == id)6389 it->strStateFile = strFile;6390 else if (!it->llChildSnapshots.empty())6391 cloneUpdateStateFile(it->llChildSnapshots, id, strFile);6392 }6393 }6394 6395 /* static */6396 int Machine::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser)6397 {6398 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);6399 6400 BOOL fCanceled = false;6401 HRESULT rc = pProgress->COMGETTER(Canceled)(&fCanceled);6402 if (FAILED(rc)) return VERR_GENERAL_FAILURE;6403 /* If canceled by the user tell it to the copy operation. */6404 if (fCanceled) return VERR_CANCELLED;6405 /* Set the new process. */6406 rc = pProgress->SetCurrentOperationProgress(uPercentage);6407 if (FAILED(rc)) return VERR_GENERAL_FAILURE;6408 6409 return VINF_SUCCESS;6410 }6411 6412 /**6413 * Task thread implementation for Machine::CloneTo(), called from Machine::cloneVMThread().6414 * @param task6415 * @return6416 */6417 HRESULT Machine::cloneVMTaskWorker(CloneVMTask *pTask)6418 {6419 AutoCaller autoCaller(this);6420 if (FAILED(autoCaller.rc())) return autoCaller.rc();6421 6422 AutoReadLock srcLock(this COMMA_LOCKVAL_SRC_POS);6423 AutoWriteLock trgLock(pTask->pTrgMachine COMMA_LOCKVAL_SRC_POS);6424 6425 MultiResult rc = S_OK;6426 6427 /*6428 * Todo:6429 * - Regardless where the old media comes from (e.g. snapshots folder) it6430 * goes to the new main VM folder. Maybe we like to be a little bit6431 * smarter here.6432 * - Snapshot diffs (can) have the uuid as name. After cloning this isn't6433 * right anymore. Is it worth to change to the new uuid? Or should the6434 * cloned disks called exactly as the original one or should all new disks6435 * get a new name with the new VM name in it.6436 */6437 6438 /* Where should all the media go? */6439 Utf8Str strTrgMachineFolder = pTask->pTrgMachine->getSettingsFileFull();6440 strTrgMachineFolder.stripFilename();6441 6442 RTCList< ComObjPtr<Medium> > newMedias; /* All created images */6443 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */6444 try6445 {6446 /* Copy all the configuration from this machine to an empty6447 * configuration dataset. */6448 settings::MachineConfigFile *pTrgMCF = new settings::MachineConfigFile(0);6449 *pTrgMCF = *mData->pMachineConfigFile;6450 6451 /* Reset media registry. */6452 pTrgMCF->mediaRegistry.llHardDisks.clear();6453 /* If we got a valid snapshot id, replace the hardware/storage section6454 * with the stuff from the snapshot. */6455 settings::Snapshot sn;6456 if (!pTask->snapshotId.isEmpty())6457 sn = cloneFindSnapshot(pTrgMCF, pTrgMCF->llFirstSnapshot, pTask->snapshotId);6458 6459 if (pTask->mode == CloneMode_MachineState)6460 {6461 if (!sn.uuid.isEmpty())6462 {6463 pTrgMCF->hardwareMachine = sn.hardware;6464 pTrgMCF->storageMachine = sn.storage;6465 }6466 6467 /* Remove any hint on snapshots. */6468 pTrgMCF->llFirstSnapshot.clear();6469 pTrgMCF->uuidCurrentSnapshot.clear();6470 }else6471 if ( pTask->mode == CloneMode_MachineAndChildStates6472 && !sn.uuid.isEmpty())6473 {6474 /* Copy the snapshot data to the current machine. */6475 pTrgMCF->hardwareMachine = sn.hardware;6476 pTrgMCF->storageMachine = sn.storage;6477 6478 /* The snapshot will be the root one. */6479 pTrgMCF->uuidCurrentSnapshot = sn.uuid;6480 pTrgMCF->llFirstSnapshot.clear();6481 pTrgMCF->llFirstSnapshot.push_back(sn);6482 }6483 6484 /* When the current snapshot folder is absolute we reset it to the6485 * default relative folder. */6486 if (RTPathStartsWithRoot(pTrgMCF->machineUserData.strSnapshotFolder.c_str()))6487 pTrgMCF->machineUserData.strSnapshotFolder = "Snapshots";6488 pTrgMCF->strStateFile = "";6489 /* Force writing of setting file. */6490 pTrgMCF->fCurrentStateModified = true;6491 /* Set the new name. */6492 pTrgMCF->machineUserData.strName = pTask->pTrgMachine->mUserData->s.strName;6493 pTrgMCF->uuid = pTask->pTrgMachine->mData->mUuid;6494 6495 Bstr bstrSrcSnapshotFolder;6496 rc = pTask->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());6497 if (FAILED(rc)) throw rc;6498 /* The absolute name of the snapshot folder. */6499 Utf8Str strTrgSnapshotFolder = Utf8StrFmt("%s%c%s%c", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, pTrgMCF->machineUserData.strSnapshotFolder.c_str(), RTPATH_DELIMITER);6500 6501 /* We need to create a map with the already created medias. This is6502 * necessary, cause different snapshots could have the same6503 * parents/parent chain. If a medium is in this map already, it isn't6504 * cloned a second time, but simply used. */6505 typedef std::map<Utf8Str, ComObjPtr<Medium> > TStrMediumMap;6506 typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair;6507 TStrMediumMap map;6508 for (size_t i = 0; i < pTask->llMedias.size(); ++i)6509 {6510 const Machine::CloneVMTask::MediumTaskChain &mtc = pTask->llMedias.at(i);6511 ComObjPtr<Medium> pNewParent;6512 for (size_t a = mtc.chain.size(); a > 0; --a)6513 {6514 const Machine::CloneVMTask::MediumTask &mt = mtc.chain.at(a - 1);6515 ComPtr<IMedium> pMedium = mt.pMedium;6516 6517 Bstr bstrSrcName;6518 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());6519 if (FAILED(rc)) throw rc;6520 6521 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize);6522 if (FAILED(rc)) throw rc;6523 6524 Bstr bstrSrcId;6525 rc = pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());6526 if (FAILED(rc)) throw rc;6527 6528 /* Is a clone already there? */6529 TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));6530 if (it != map.end())6531 pNewParent = it->second;6532 else6533 {6534 ComPtr<IMediumFormat> pSrcFormat;6535 rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());6536 ULONG uSrcCaps = 0;6537 rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps);6538 if (FAILED(rc)) throw rc;6539 6540 Bstr bstrSrcFormat = "VDI";6541 ULONG srcVar = MediumVariant_Standard;6542 /* Is the source file based? */6543 if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)6544 {6545 /* Yes, just use the source format. Otherwise the defaults6546 * will be used. */6547 rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());6548 if (FAILED(rc)) throw rc;6549 rc = pMedium->COMGETTER(Variant)(&srcVar);6550 if (FAILED(rc)) throw rc;6551 }6552 6553 /* Start creating the clone. */6554 ComObjPtr<Medium> pTarget;6555 rc = pTarget.createObject();6556 if (FAILED(rc)) throw rc;6557 6558 Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw());6559 rc = pTarget->init(mParent,6560 Utf8Str(bstrSrcFormat),6561 strFile,6562 pTask->pTrgMachine->mData->mUuid, /* media registry */6563 NULL /* llRegistriesThatNeedSaving */);6564 if (FAILED(rc)) throw rc;6565 6566 /* Do the disk cloning. */6567 ComPtr<IProgress> progress2;6568 rc = pMedium->CloneTo(pTarget,6569 srcVar,6570 pNewParent,6571 progress2.asOutParam());6572 if (FAILED(rc)) throw rc;6573 6574 /* Wait until the asynchrony process has finished. */6575 srcLock.release();6576 rc = pTask->pProgress->WaitForAsyncProgressCompletion(progress2);6577 srcLock.acquire();6578 if (FAILED(rc)) throw rc;6579 6580 /* Check the result of the asynchrony process. */6581 LONG iRc;6582 rc = progress2->COMGETTER(ResultCode)(&iRc);6583 if (FAILED(rc)) throw rc;6584 if (FAILED(iRc))6585 {6586 /* If the thread of the progress object has an error, then6587 * retrieve the error info from there, or it'll be lost. */6588 ProgressErrorInfo info(progress2);6589 throw setError(iRc, Utf8Str(info.getText()).c_str());6590 }6591 6592 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));6593 6594 /* Remember created medias. */6595 newMedias.append(pTarget);6596 /* This medium becomes the parent of the next medium in the6597 * chain. */6598 pNewParent = pTarget;6599 }6600 }6601 6602 /* Create diffs for the last image chain. */6603 if (mtc.fCreateDiffs)6604 {6605 Bstr bstrSrcId;6606 rc = pNewParent->COMGETTER(Id)(bstrSrcId.asOutParam());6607 if (FAILED(rc)) throw rc;6608 GuidList *pllRegistriesThatNeedSaving;6609 ComObjPtr<Medium> diff;6610 diff.createObject();6611 rc = diff->init(mParent,6612 pNewParent->getPreferredDiffFormat(),6613 strTrgSnapshotFolder,6614 pTask->pTrgMachine->mData->mUuid,6615 NULL); // pllRegistriesThatNeedSaving6616 if (FAILED(rc)) throw rc;6617 MediumLockList *pMediumLockList(new MediumLockList()); /* todo: deleteeeeeeeee */6618 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,6619 true /* fMediumLockWrite */,6620 pNewParent,6621 *pMediumLockList);6622 if (FAILED(rc)) throw rc;6623 rc = pMediumLockList->Lock();6624 if (FAILED(rc)) throw rc;6625 rc = pNewParent->createDiffStorage(diff, MediumVariant_Standard,6626 pMediumLockList,6627 NULL /* aProgress */,6628 true /* aWait */,6629 NULL); // pllRegistriesThatNeedSaving6630 delete pMediumLockList;6631 if (FAILED(rc)) throw rc;6632 pNewParent = diff;6633 }6634 Bstr bstrSrcId;6635 rc = mtc.chain.first().pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());6636 if (FAILED(rc)) throw rc;6637 Bstr bstrTrgId;6638 rc = pNewParent->COMGETTER(Id)(bstrTrgId.asOutParam());6639 if (FAILED(rc)) throw rc;6640 /* We have to patch the configuration, so it contains the new6641 * medium uuid instead of the old one. */6642 cloneUpdateStorageLists(pTrgMCF->storageMachine.llStorageControllers, bstrSrcId, bstrTrgId);6643 cloneUpdateSnapshotStorageLists(pTrgMCF->llFirstSnapshot, bstrSrcId, bstrTrgId);6644 }6645 /* Clone all save state files. */6646 for (size_t i = 0; i < pTask->llSaveStateFiles.size(); ++i)6647 {6648 Machine::CloneVMTask::SaveStateTask sst = pTask->llSaveStateFiles.at(i);6649 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str()));6650 6651 /* Move to next sub-operation. */6652 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize);6653 if (FAILED(rc)) throw rc;6654 /* Copy the file only if it was not copied already. */6655 if (!newFiles.contains(strTrgSaveState.c_str()))6656 {6657 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, cloneCopyStateFileProgress, &pTask->pProgress);6658 if (RT_FAILURE(vrc))6659 throw setError(VBOX_E_IPRT_ERROR,6660 tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);6661 newFiles.append(strTrgSaveState);6662 }6663 /* Update the path in the configuration either for the current6664 * machine state or the snapshots. */6665 if (sst.snapshotUuid.isEmpty())6666 pTrgMCF->strStateFile = strTrgSaveState;6667 else6668 cloneUpdateStateFile(pTrgMCF->llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);6669 }6670 6671 if (false)6672 // if (!pTask->pOldMachineState.isNull())6673 {6674 SafeIfaceArray<IMediumAttachment> sfaAttachments;6675 rc = pTask->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));6676 if (FAILED(rc)) throw rc;6677 for (size_t a = 0; a < sfaAttachments.size(); ++a)6678 {6679 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];6680 DeviceType_T type;6681 rc = pAtt->COMGETTER(Type)(&type);6682 if (FAILED(rc)) throw rc;6683 6684 /* Only harddisk's are of interest. */6685 if (type != DeviceType_HardDisk)6686 continue;6687 6688 /* Valid medium attached? */6689 ComPtr<IMedium> pSrcMedium;6690 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());6691 if (FAILED(rc)) throw rc;6692 if (pSrcMedium.isNull())6693 continue;6694 6695 // ComObjPtr<Medium> pMedium = static_cast<Medium*>((IMedium*)pSrcMedium);6696 // ComObjPtr<Medium> diff;6697 // diff.createObject();6698 // store this diff in the same registry as the parent6699 // Guid uuidRegistryParent;6700 // if (!medium->getFirstRegistryMachineId(uuidRegistryParent))6701 // {6702 // parent image has no registry: this can happen if we're attaching a new immutable6703 // image that has not yet been attached (medium then points to the base and we're6704 // creating the diff image for the immutable, and the parent is not yet registered);6705 // put the parent in the machine registry then6706 // addMediumToRegistry(medium, llRegistriesThatNeedSaving, &uuidRegistryParent);6707 // }6708 // rc = diff->init(mParent,6709 // pMedium->getPreferredDiffFormat(),6710 // strFullSnapshotFolder.append(RTPATH_SLASH_STR),6711 // uuidRegistryParent,6712 // pllRegistriesThatNeedSaving);6713 // if (FAILED(rc)) throw rc;6714 //6715 // rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,6716 // pMediumLockList,6717 // NULL /* aProgress */,6718 // true /* aWait */,6719 // pllRegistriesThatNeedSaving);6720 }6721 }6722 6723 {6724 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Create Machine Clone '%s' ..."), pTrgMCF->machineUserData.strName.c_str()).raw(), 1);6725 if (FAILED(rc)) throw rc;6726 /* After modifying the new machine config, we can copy the stuff6727 * over to the new machine. The machine have to be mutable for6728 * this. */6729 rc = pTask->pTrgMachine->checkStateDependency(MutableStateDep);6730 if (FAILED(rc)) throw rc;6731 rc = pTask->pTrgMachine->loadMachineDataFromSettings(*pTrgMCF,6732 &pTask->pTrgMachine->mData->mUuid);6733 if (FAILED(rc)) throw rc;6734 }6735 6736 /* The medias are created before the machine was there. We have to make6737 * sure the new medias know of there new parent or we get in trouble6738 * when the media registry is saved for this VM, especially in case of6739 * difference image chain's. See VirtualBox::saveMediaRegistry.*/6740 // for (size_t i = 0; i < newBaseMedias.size(); ++i)6741 // {6742 // rc = newBaseMedias.at(i)->addRegistry(pTask->pTrgMachine->mData->mUuid, true /* fRecursive */);6743 // if (FAILED(rc)) throw rc;6744 // }6745 6746 /* Now save the new configuration to disk. */6747 rc = pTask->pTrgMachine->SaveSettings();6748 if (FAILED(rc)) throw rc;6749 }6750 catch(HRESULT rc2)6751 {6752 rc = rc2;6753 }6754 catch (...)6755 {6756 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);6757 }6758 6759 /* Cleanup on failure (CANCEL also) */6760 if (FAILED(rc))6761 {6762 int vrc = VINF_SUCCESS;6763 /* Delete all created files. */6764 for (size_t i = 0; i < newFiles.size(); ++i)6765 {6766 vrc = RTFileDelete(newFiles.at(i).c_str());6767 if (RT_FAILURE(vrc))6768 rc = setError(VBOX_E_IPRT_ERROR, tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);6769 }6770 /* Delete all already created medias. (Reverse, cause there could be6771 * parent->child relations.) */6772 for (size_t i = newMedias.size(); i > 0; --i)6773 {6774 ComObjPtr<Medium> &pMedium = newMedias.at(i - 1);6775 AutoCaller mac(pMedium);6776 if (FAILED(mac.rc())) { continue; rc = mac.rc(); }6777 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);6778 bool fFile = pMedium->isMediumFormatFile();6779 Utf8Str strLoc = pMedium->getLocationFull();6780 mlock.release();6781 /* Close the medium. If this succeed, delete it finally from the6782 * disk. */6783 rc = pMedium->close(NULL, mac);6784 if (FAILED(rc)) continue;6785 if (fFile)6786 {6787 vrc = RTFileDelete(strLoc.c_str());6788 if (RT_FAILURE(vrc))6789 rc = setError(VBOX_E_IPRT_ERROR, tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);6790 }6791 }6792 /* Delete the machine folder when not empty. */6793 RTDirRemove(strTrgMachineFolder.c_str());6794 }6795 6092 6796 6093 return rc; -
trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp
r37486 r37491 1 1 /* $Id$ */ 2 2 /** @file 3 * Implementation of IMachine in VBoxSVC.3 * Implementation of MachineCloneVM 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 20 06-2011 Oracle Corporation7 * Copyright (C) 2011 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 25 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 33 34 #include "Logging.h" 18 #include "MachineImplCloneVM.h" 19 35 20 #include "VirtualBoxImpl.h" 36 #include "MachineImpl.h"37 #include "ProgressImpl.h"38 #include "ProgressProxyImpl.h"39 #include "MediumAttachmentImpl.h"40 21 #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 #include "DisplayUtils.h" 51 #include "BandwidthControlImpl.h" 52 53 // generated header 54 #include "VBoxEvents.h" 55 56 #ifdef VBOX_WITH_USB 57 # include "USBProxyService.h" 58 #endif 59 60 #include "AutoCaller.h" 61 #include "Performance.h" 62 63 #include <iprt/asm.h> 22 64 23 #include <iprt/path.h> 65 24 #include <iprt/dir.h> 66 #include <iprt/env.h>67 #include <iprt/lockvalidator.h>68 #include <iprt/process.h>69 25 #include <iprt/cpp/utils.h> 70 #include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */ 71 #include <iprt/string.h> 72 73 #include <VBox/com/array.h> 26 74 27 #include <VBox/com/list.h> 75 76 #include <VBox/err.h> 77 #include <VBox/param.h> 78 #include <VBox/settings.h> 79 #include <VBox/vmm/ssm.h> 80 81 #ifdef VBOX_WITH_GUEST_PROPS 82 # include <VBox/HostServices/GuestPropertySvc.h> 83 # include <VBox/com/array.h> 84 #endif 85 86 #include "VBox/com/MultiResult.h" 87 88 #include <algorithm> 89 90 #include <typeinfo> 91 92 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 93 # define HOSTSUFF_EXE ".exe" 94 #else /* !RT_OS_WINDOWS */ 95 # define HOSTSUFF_EXE "" 96 #endif /* !RT_OS_WINDOWS */ 97 98 // defines / prototypes 28 #include <VBox/com/MultiResult.h> 29 30 // typedefs 99 31 ///////////////////////////////////////////////////////////////////////////// 100 32 33 typedef struct 34 { 35 ComPtr<IMedium> pMedium; 36 uint64_t uSize; 37 }MEDIUMTASK; 38 39 typedef struct 40 { 41 RTCList<MEDIUMTASK> chain; 42 bool fCreateDiffs; 43 }MEDIUMTASKCHAIN; 44 45 typedef struct 46 { 47 Guid snapshotUuid; 48 Utf8Str strSaveStateFile; 49 uint64_t cbSize; 50 }SAVESTATETASK; 51 52 // The private class 101 53 ///////////////////////////////////////////////////////////////////////////// 102 // Machine::Data structure 103 ///////////////////////////////////////////////////////////////////////////// 104 105 Machine::Data::Data() 106 { 107 mRegistered = FALSE; 108 pMachineConfigFile = NULL; 109 flModifications = 0; 110 mAccessible = FALSE; 111 /* mUuid is initialized in Machine::init() */ 112 113 mMachineState = MachineState_PoweredOff; 114 RTTimeNow(&mLastStateChange); 115 116 mMachineStateDeps = 0; 117 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 118 mMachineStateChangePending = 0; 119 120 mCurrentStateModified = TRUE; 121 mGuestPropertiesModified = FALSE; 122 123 mSession.mPid = NIL_RTPROCESS; 124 mSession.mState = SessionState_Unlocked; 125 } 126 127 Machine::Data::~Data() 128 { 129 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI) 130 { 131 RTSemEventMultiDestroy(mMachineStateDepsSem); 132 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 133 } 134 if (pMachineConfigFile) 135 { 136 delete pMachineConfigFile; 137 pMachineConfigFile = NULL; 138 } 139 } 140 141 ///////////////////////////////////////////////////////////////////////////// 142 // Machine::HWData structure 143 ///////////////////////////////////////////////////////////////////////////// 144 145 Machine::HWData::HWData() 146 { 147 /* default values for a newly created machine */ 148 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */ 149 mMemorySize = 128; 150 mCPUCount = 1; 151 mCPUHotPlugEnabled = false; 152 mMemoryBalloonSize = 0; 153 mPageFusionEnabled = false; 154 mVRAMSize = 8; 155 mAccelerate3DEnabled = false; 156 mAccelerate2DVideoEnabled = false; 157 mMonitorCount = 1; 158 mHWVirtExEnabled = true; 159 mHWVirtExNestedPagingEnabled = true; 160 #if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX) 161 mHWVirtExLargePagesEnabled = true; 162 #else 163 /* Not supported on 32 bits hosts. */ 164 mHWVirtExLargePagesEnabled = false; 165 #endif 166 mHWVirtExVPIDEnabled = true; 167 mHWVirtExForceEnabled = false; 168 #if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) 169 mHWVirtExExclusive = false; 170 #else 171 mHWVirtExExclusive = true; 172 #endif 173 #if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) 174 mPAEEnabled = true; 175 #else 176 mPAEEnabled = false; 177 #endif 178 mSyntheticCpu = false; 179 mHpetEnabled = false; 180 181 /* default boot order: floppy - DVD - HDD */ 182 mBootOrder[0] = DeviceType_Floppy; 183 mBootOrder[1] = DeviceType_DVD; 184 mBootOrder[2] = DeviceType_HardDisk; 185 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i) 186 mBootOrder[i] = DeviceType_Null; 187 188 mClipboardMode = ClipboardMode_Bidirectional; 189 mGuestPropertyNotificationPatterns = ""; 190 191 mFirmwareType = FirmwareType_BIOS; 192 mKeyboardHidType = KeyboardHidType_PS2Keyboard; 193 mPointingHidType = PointingHidType_PS2Mouse; 194 mChipsetType = ChipsetType_PIIX3; 195 196 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++) 197 mCPUAttached[i] = false; 198 199 mIoCacheEnabled = true; 200 mIoCacheSize = 5; /* 5MB */ 201 202 /* Maximum CPU execution cap by default. */ 203 mCpuExecutionCap = 100; 204 } 205 206 Machine::HWData::~HWData() 207 { 208 } 209 210 ///////////////////////////////////////////////////////////////////////////// 211 // Machine::HDData structure 212 ///////////////////////////////////////////////////////////////////////////// 213 214 Machine::MediaData::MediaData() 215 { 216 } 217 218 Machine::MediaData::~MediaData() 219 { 220 } 221 222 ///////////////////////////////////////////////////////////////////////////// 223 // Machine class 224 ///////////////////////////////////////////////////////////////////////////// 225 226 // constructor / destructor 227 ///////////////////////////////////////////////////////////////////////////// 228 229 Machine::Machine() 230 : mCollectorGuest(NULL), 231 mPeer(NULL), 232 mParent(NULL) 233 {} 234 235 Machine::~Machine() 236 {} 237 238 HRESULT Machine::FinalConstruct() 239 { 240 LogFlowThisFunc(("\n")); 241 return BaseFinalConstruct(); 242 } 243 244 void Machine::FinalRelease() 245 { 246 LogFlowThisFunc(("\n")); 247 uninit(); 248 BaseFinalRelease(); 249 } 250 251 /** 252 * Initializes a new machine instance; this init() variant creates a new, empty machine. 253 * This gets called from VirtualBox::CreateMachine(). 254 * 255 * @param aParent Associated parent object 256 * @param strConfigFile Local file system path to the VM settings file (can 257 * be relative to the VirtualBox config directory). 258 * @param strName name for the machine 259 * @param aId UUID for the new machine. 260 * @param aOsType OS Type of this machine or NULL. 261 * @param fForceOverwrite Whether to overwrite an existing machine settings file. 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 GuestOSType *aOsType, 269 const Guid &aId, 270 bool fForceOverwrite) 271 { 272 LogFlowThisFuncEnter(); 273 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str())); 274 275 /* Enclose the state transition NotReady->InInit->Ready */ 276 AutoInitSpan autoInitSpan(this); 277 AssertReturn(autoInitSpan.isOk(), E_FAIL); 278 279 HRESULT rc = initImpl(aParent, strConfigFile); 280 if (FAILED(rc)) return rc; 281 282 rc = tryCreateMachineConfigFile(fForceOverwrite); 283 if (FAILED(rc)) return rc; 284 285 if (SUCCEEDED(rc)) 286 { 287 // create an empty machine config 288 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 289 290 rc = initDataAndChildObjects(); 291 } 292 293 if (SUCCEEDED(rc)) 294 { 295 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 296 mData->mAccessible = TRUE; 297 298 unconst(mData->mUuid) = aId; 299 300 mUserData->s.strName = strName; 301 302 // the "name sync" flag determines whether the machine directory gets renamed along 303 // with the machine file; say so if the settings file name is the same as the 304 // settings file parent directory (machine directory) 305 mUserData->s.fNameSync = isInOwnDir(); 306 307 // initialize the default snapshots folder 308 rc = COMSETTER(SnapshotFolder)(NULL); 309 AssertComRC(rc); 310 311 if (aOsType) 312 { 313 /* Store OS type */ 314 mUserData->s.strOsType = aOsType->id(); 315 316 /* Apply BIOS defaults */ 317 mBIOSSettings->applyDefaults(aOsType); 318 319 /* Apply network adapters defaults */ 320 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot) 321 mNetworkAdapters[slot]->applyDefaults(aOsType); 322 323 /* Apply serial port defaults */ 324 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot) 325 mSerialPorts[slot]->applyDefaults(aOsType); 326 } 327 328 /* commit all changes made during the initialization */ 329 commit(); 330 } 331 332 /* Confirm a successful initialization when it's the case */ 333 if (SUCCEEDED(rc)) 334 { 335 if (mData->mAccessible) 336 autoInitSpan.setSucceeded(); 337 else 338 autoInitSpan.setLimited(); 339 } 340 341 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n", 342 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 343 mData->mRegistered, 344 mData->mAccessible, 345 rc)); 346 347 LogFlowThisFuncLeave(); 348 349 return rc; 350 } 351 352 /** 353 * Initializes a new instance with data from machine XML (formerly Init_Registered). 354 * Gets called in two modes: 355 * 356 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the 357 * UUID is specified and we mark the machine as "registered"; 358 * 359 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL 360 * and the machine remains unregistered until RegisterMachine() is called. 361 * 362 * @param aParent Associated parent object 363 * @param aConfigFile Local file system path to the VM settings file (can 364 * be relative to the VirtualBox config directory). 365 * @param aId UUID of the machine or NULL (see above). 366 * 367 * @return Success indicator. if not S_OK, the machine object is invalid 368 */ 369 HRESULT Machine::init(VirtualBox *aParent, 370 const Utf8Str &strConfigFile, 371 const Guid *aId) 372 { 373 LogFlowThisFuncEnter(); 374 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str())); 375 376 /* Enclose the state transition NotReady->InInit->Ready */ 377 AutoInitSpan autoInitSpan(this); 378 AssertReturn(autoInitSpan.isOk(), E_FAIL); 379 380 HRESULT rc = initImpl(aParent, strConfigFile); 381 if (FAILED(rc)) return rc; 382 383 if (aId) 384 { 385 // loading a registered VM: 386 unconst(mData->mUuid) = *aId; 387 mData->mRegistered = TRUE; 388 // now load the settings from XML: 389 rc = registeredInit(); 390 // this calls initDataAndChildObjects() and loadSettings() 391 } 392 else 393 { 394 // opening an unregistered VM (VirtualBox::OpenMachine()): 395 rc = initDataAndChildObjects(); 396 397 if (SUCCEEDED(rc)) 398 { 399 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 400 mData->mAccessible = TRUE; 401 402 try 403 { 404 // load and parse machine XML; this will throw on XML or logic errors 405 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 406 407 // reject VM UUID duplicates, they can happen if someone 408 // tries to register an already known VM config again 409 if (aParent->findMachine(mData->pMachineConfigFile->uuid, 410 true /* fPermitInaccessible */, 411 false /* aDoSetError */, 412 NULL) != VBOX_E_OBJECT_NOT_FOUND) 413 { 414 throw setError(E_FAIL, 415 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"), 416 mData->m_strConfigFile.c_str()); 417 } 418 419 // use UUID from machine config 420 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid; 421 422 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile, 423 NULL /* puuidRegistry */); 424 if (FAILED(rc)) throw rc; 425 426 commit(); 427 } 428 catch (HRESULT err) 429 { 430 /* we assume that error info is set by the thrower */ 431 rc = err; 432 } 433 catch (...) 434 { 435 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 436 } 437 } 438 } 439 440 /* Confirm a successful initialization when it's the case */ 441 if (SUCCEEDED(rc)) 442 { 443 if (mData->mAccessible) 444 autoInitSpan.setSucceeded(); 445 else 446 { 447 autoInitSpan.setLimited(); 448 449 // uninit media from this machine's media registry, or else 450 // reloading the settings will fail 451 mParent->unregisterMachineMedia(getId()); 452 } 453 } 454 455 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 456 "rc=%08X\n", 457 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 458 mData->mRegistered, mData->mAccessible, rc)); 459 460 LogFlowThisFuncLeave(); 461 462 return rc; 463 } 464 465 /** 466 * Initializes a new instance from a machine config that is already in memory 467 * (import OVF case). Since we are importing, the UUID in the machine 468 * config is ignored and we always generate a fresh one. 469 * 470 * @param strName Name for the new machine; this overrides what is specified in config and is used 471 * for the settings file as well. 472 * @param config Machine configuration loaded and parsed from XML. 473 * 474 * @return Success indicator. if not S_OK, the machine object is invalid 475 */ 476 HRESULT Machine::init(VirtualBox *aParent, 477 const Utf8Str &strName, 478 const settings::MachineConfigFile &config) 479 { 480 LogFlowThisFuncEnter(); 481 482 /* Enclose the state transition NotReady->InInit->Ready */ 483 AutoInitSpan autoInitSpan(this); 484 AssertReturn(autoInitSpan.isOk(), E_FAIL); 485 486 Utf8Str strConfigFile; 487 aParent->getDefaultMachineFolder(strConfigFile); 488 strConfigFile.append(RTPATH_DELIMITER); 489 strConfigFile.append(strName); 490 strConfigFile.append(RTPATH_DELIMITER); 491 strConfigFile.append(strName); 492 strConfigFile.append(".vbox"); 493 494 HRESULT rc = initImpl(aParent, strConfigFile); 495 if (FAILED(rc)) return rc; 496 497 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */); 498 if (FAILED(rc)) return rc; 499 500 rc = initDataAndChildObjects(); 501 502 if (SUCCEEDED(rc)) 503 { 504 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 505 mData->mAccessible = TRUE; 506 507 // create empty machine config for instance data 508 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 509 510 // generate fresh UUID, ignore machine config 511 unconst(mData->mUuid).create(); 512 513 rc = loadMachineDataFromSettings(config, 514 &mData->mUuid); // puuidRegistry: initialize media with this registry ID 515 516 // override VM name as well, it may be different 517 mUserData->s.strName = strName; 518 519 /* commit all changes made during the initialization */ 520 if (SUCCEEDED(rc)) 521 commit(); 522 } 523 524 /* Confirm a successful initialization when it's the case */ 525 if (SUCCEEDED(rc)) 526 { 527 if (mData->mAccessible) 528 autoInitSpan.setSucceeded(); 529 else 530 { 531 autoInitSpan.setLimited(); 532 533 // uninit media from this machine's media registry, or else 534 // reloading the settings will fail 535 mParent->unregisterMachineMedia(getId()); 536 } 537 } 538 539 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 540 "rc=%08X\n", 541 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 542 mData->mRegistered, mData->mAccessible, rc)); 543 544 LogFlowThisFuncLeave(); 545 546 return rc; 547 } 548 549 /** 550 * Shared code between the various init() implementations. 551 * @param aParent 552 * @return 553 */ 554 HRESULT Machine::initImpl(VirtualBox *aParent, 555 const Utf8Str &strConfigFile) 556 { 557 LogFlowThisFuncEnter(); 558 559 AssertReturn(aParent, E_INVALIDARG); 560 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG); 561 562 HRESULT rc = S_OK; 563 564 /* share the parent weakly */ 565 unconst(mParent) = aParent; 566 567 /* allocate the essential machine data structure (the rest will be 568 * allocated later by initDataAndChildObjects() */ 569 mData.allocate(); 570 571 /* memorize the config file name (as provided) */ 572 mData->m_strConfigFile = strConfigFile; 573 574 /* get the full file name */ 575 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull); 576 if (RT_FAILURE(vrc1)) 577 return setError(VBOX_E_FILE_ERROR, 578 tr("Invalid machine settings file name '%s' (%Rrc)"), 579 strConfigFile.c_str(), 580 vrc1); 581 582 LogFlowThisFuncLeave(); 583 584 return rc; 585 } 586 587 /** 588 * Tries to create a machine settings file in the path stored in the machine 589 * instance data. Used when a new machine is created to fail gracefully if 590 * the settings file could not be written (e.g. because machine dir is read-only). 591 * @return 592 */ 593 HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite) 594 { 595 HRESULT rc = S_OK; 596 597 // when we create a new machine, we must be able to create the settings file 598 RTFILE f = NIL_RTFILE; 599 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); 600 if ( RT_SUCCESS(vrc) 601 || vrc == VERR_SHARING_VIOLATION 602 ) 603 { 604 if (RT_SUCCESS(vrc)) 605 RTFileClose(f); 606 if (!fForceOverwrite) 607 rc = setError(VBOX_E_FILE_ERROR, 608 tr("Machine settings file '%s' already exists"), 609 mData->m_strConfigFileFull.c_str()); 610 else 611 { 612 /* try to delete the config file, as otherwise the creation 613 * of a new settings file will fail. */ 614 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str()); 615 if (RT_FAILURE(vrc2)) 616 rc = setError(VBOX_E_FILE_ERROR, 617 tr("Could not delete the existing settings file '%s' (%Rrc)"), 618 mData->m_strConfigFileFull.c_str(), vrc2); 619 } 620 } 621 else if ( vrc != VERR_FILE_NOT_FOUND 622 && vrc != VERR_PATH_NOT_FOUND 623 ) 624 rc = setError(VBOX_E_FILE_ERROR, 625 tr("Invalid machine settings file name '%s' (%Rrc)"), 626 mData->m_strConfigFileFull.c_str(), 627 vrc); 628 return rc; 629 } 630 631 /** 632 * Initializes the registered machine by loading the settings file. 633 * This method is separated from #init() in order to make it possible to 634 * retry the operation after VirtualBox startup instead of refusing to 635 * startup the whole VirtualBox server in case if the settings file of some 636 * registered VM is invalid or inaccessible. 637 * 638 * @note Must be always called from this object's write lock 639 * (unless called from #init() that doesn't need any locking). 640 * @note Locks the mUSBController method for writing. 641 * @note Subclasses must not call this method. 642 */ 643 HRESULT Machine::registeredInit() 644 { 645 AssertReturn(!isSessionMachine(), E_FAIL); 646 AssertReturn(!isSnapshotMachine(), E_FAIL); 647 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL); 648 AssertReturn(!mData->mAccessible, E_FAIL); 649 650 HRESULT rc = initDataAndChildObjects(); 651 652 if (SUCCEEDED(rc)) 653 { 654 /* Temporarily reset the registered flag in order to let setters 655 * potentially called from loadSettings() succeed (isMutable() used in 656 * all setters will return FALSE for a Machine instance if mRegistered 657 * is TRUE). */ 658 mData->mRegistered = FALSE; 659 660 try 661 { 662 // load and parse machine XML; this will throw on XML or logic errors 663 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 664 665 if (mData->mUuid != mData->pMachineConfigFile->uuid) 666 throw setError(E_FAIL, 667 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"), 668 mData->pMachineConfigFile->uuid.raw(), 669 mData->m_strConfigFileFull.c_str(), 670 mData->mUuid.toString().c_str(), 671 mParent->settingsFilePath().c_str()); 672 673 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile, 674 NULL /* const Guid *puuidRegistry */); 675 if (FAILED(rc)) throw rc; 676 } 677 catch (HRESULT err) 678 { 679 /* we assume that error info is set by the thrower */ 680 rc = err; 681 } 682 catch (...) 683 { 684 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 685 } 686 687 /* Restore the registered flag (even on failure) */ 688 mData->mRegistered = TRUE; 689 } 690 691 if (SUCCEEDED(rc)) 692 { 693 /* Set mAccessible to TRUE only if we successfully locked and loaded 694 * the settings file */ 695 mData->mAccessible = TRUE; 696 697 /* commit all changes made during loading the settings file */ 698 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive 699 } 700 else 701 { 702 /* If the machine is registered, then, instead of returning a 703 * failure, we mark it as inaccessible and set the result to 704 * success to give it a try later */ 705 706 /* fetch the current error info */ 707 mData->mAccessError = com::ErrorInfo(); 708 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n", 709 mData->mUuid.raw(), 710 mData->mAccessError.getText().raw())); 711 712 /* rollback all changes */ 713 rollback(false /* aNotify */); 714 715 // uninit media from this machine's media registry, or else 716 // reloading the settings will fail 717 mParent->unregisterMachineMedia(getId()); 718 719 /* uninitialize the common part to make sure all data is reset to 720 * default (null) values */ 721 uninitDataAndChildObjects(); 722 723 rc = S_OK; 724 } 725 726 return rc; 727 } 728 729 /** 730 * Uninitializes the instance. 731 * Called either from FinalRelease() or by the parent when it gets destroyed. 732 * 733 * @note The caller of this method must make sure that this object 734 * a) doesn't have active callers on the current thread and b) is not locked 735 * by the current thread; otherwise uninit() will hang either a) due to 736 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to 737 * a dead-lock caused by this thread waiting for all callers on the other 738 * threads are done but preventing them from doing so by holding a lock. 739 */ 740 void Machine::uninit() 741 { 742 LogFlowThisFuncEnter(); 743 744 Assert(!isWriteLockOnCurrentThread()); 745 746 /* Enclose the state transition Ready->InUninit->NotReady */ 747 AutoUninitSpan autoUninitSpan(this); 748 if (autoUninitSpan.uninitDone()) 749 return; 750 751 Assert(!isSnapshotMachine()); 752 Assert(!isSessionMachine()); 753 Assert(!!mData); 754 755 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed())); 756 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered)); 757 758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 759 760 if (!mData->mSession.mMachine.isNull()) 761 { 762 /* Theoretically, this can only happen if the VirtualBox server has been 763 * terminated while there were clients running that owned open direct 764 * sessions. Since in this case we are definitely called by 765 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit() 766 * won't happen on the client watcher thread (because it does 767 * VirtualBox::addCaller() for the duration of the 768 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit() 769 * cannot happen until the VirtualBox caller is released). This is 770 * important, because SessionMachine::uninit() cannot correctly operate 771 * after we return from this method (it expects the Machine instance is 772 * still valid). We'll call it ourselves below. 773 */ 774 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n", 775 (SessionMachine*)mData->mSession.mMachine)); 776 777 if (Global::IsOnlineOrTransient(mData->mMachineState)) 778 { 779 LogWarningThisFunc(("Setting state to Aborted!\n")); 780 /* set machine state using SessionMachine reimplementation */ 781 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted); 782 } 783 784 /* 785 * Uninitialize SessionMachine using public uninit() to indicate 786 * an unexpected uninitialization. 787 */ 788 mData->mSession.mMachine->uninit(); 789 /* SessionMachine::uninit() must set mSession.mMachine to null */ 790 Assert(mData->mSession.mMachine.isNull()); 791 } 792 793 // uninit media from this machine's media registry, if they're still there 794 Guid uuidMachine(getId()); 795 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init 796 mParent->unregisterMachineMedia(uuidMachine); 797 798 /* the lock is no more necessary (SessionMachine is uninitialized) */ 799 alock.leave(); 800 801 // has machine been modified? 802 if (mData->flModifications) 803 { 804 LogWarningThisFunc(("Discarding unsaved settings changes!\n")); 805 rollback(false /* aNotify */); 806 } 807 808 if (mData->mAccessible) 809 uninitDataAndChildObjects(); 810 811 /* free the essential data structure last */ 812 mData.free(); 813 814 LogFlowThisFuncLeave(); 815 } 816 817 // IMachine properties 818 ///////////////////////////////////////////////////////////////////////////// 819 820 STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent) 821 { 822 CheckComArgOutPointerValid(aParent); 823 824 AutoLimitedCaller autoCaller(this); 825 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 826 827 /* mParent is constant during life time, no need to lock */ 828 ComObjPtr<VirtualBox> pVirtualBox(mParent); 829 pVirtualBox.queryInterfaceTo(aParent); 830 831 return S_OK; 832 } 833 834 STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible) 835 { 836 CheckComArgOutPointerValid(aAccessible); 837 838 AutoLimitedCaller autoCaller(this); 839 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 840 841 LogFlowThisFunc(("ENTER\n")); 842 843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 844 845 HRESULT rc = S_OK; 846 847 if (!mData->mAccessible) 848 { 849 /* try to initialize the VM once more if not accessible */ 850 851 AutoReinitSpan autoReinitSpan(this); 852 AssertReturn(autoReinitSpan.isOk(), E_FAIL); 853 854 #ifdef DEBUG 855 LogFlowThisFunc(("Dumping media backreferences\n")); 856 mParent->dumpAllBackRefs(); 857 #endif 858 859 if (mData->pMachineConfigFile) 860 { 861 // reset the XML file to force loadSettings() (called from registeredInit()) 862 // to parse it again; the file might have changed 863 delete mData->pMachineConfigFile; 864 mData->pMachineConfigFile = NULL; 865 } 866 867 rc = registeredInit(); 868 869 if (SUCCEEDED(rc) && mData->mAccessible) 870 { 871 autoReinitSpan.setSucceeded(); 872 873 /* make sure interesting parties will notice the accessibility 874 * state change */ 875 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState); 876 mParent->onMachineDataChange(mData->mUuid); 877 } 878 } 879 880 if (SUCCEEDED(rc)) 881 *aAccessible = mData->mAccessible; 882 883 LogFlowThisFuncLeave(); 884 885 return rc; 886 } 887 888 STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError) 889 { 890 CheckComArgOutPointerValid(aAccessError); 891 892 AutoLimitedCaller autoCaller(this); 893 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 894 895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 896 897 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable()) 898 { 899 /* return shortly */ 900 aAccessError = NULL; 901 return S_OK; 902 } 903 904 HRESULT rc = S_OK; 905 906 ComObjPtr<VirtualBoxErrorInfo> errorInfo; 907 rc = errorInfo.createObject(); 908 if (SUCCEEDED(rc)) 909 { 910 errorInfo->init(mData->mAccessError.getResultCode(), 911 mData->mAccessError.getInterfaceID().ref(), 912 Utf8Str(mData->mAccessError.getComponent()).c_str(), 913 Utf8Str(mData->mAccessError.getText())); 914 rc = errorInfo.queryInterfaceTo(aAccessError); 915 } 916 917 return rc; 918 } 919 920 STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName) 921 { 922 CheckComArgOutPointerValid(aName); 923 924 AutoCaller autoCaller(this); 925 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 926 927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 928 929 mUserData->s.strName.cloneTo(aName); 930 931 return S_OK; 932 } 933 934 STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName) 935 { 936 CheckComArgStrNotEmptyOrNull(aName); 937 938 AutoCaller autoCaller(this); 939 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 940 941 // prohibit setting a UUID only as the machine name, or else it can 942 // never be found by findMachine() 943 Guid test(aName); 944 if (test.isNotEmpty()) 945 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name")); 946 947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 948 949 HRESULT rc = checkStateDependency(MutableStateDep); 950 if (FAILED(rc)) return rc; 951 952 setModified(IsModified_MachineData); 953 mUserData.backup(); 954 mUserData->s.strName = aName; 955 956 return S_OK; 957 } 958 959 STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription) 960 { 961 CheckComArgOutPointerValid(aDescription); 962 963 AutoCaller autoCaller(this); 964 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 965 966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 967 968 mUserData->s.strDescription.cloneTo(aDescription); 969 970 return S_OK; 971 } 972 973 STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription) 974 { 975 AutoCaller autoCaller(this); 976 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 977 978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 979 980 HRESULT rc = checkStateDependency(MutableStateDep); 981 if (FAILED(rc)) return rc; 982 983 setModified(IsModified_MachineData); 984 mUserData.backup(); 985 mUserData->s.strDescription = aDescription; 986 987 return S_OK; 988 } 989 990 STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId) 991 { 992 CheckComArgOutPointerValid(aId); 993 994 AutoLimitedCaller autoCaller(this); 995 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 996 997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 998 999 mData->mUuid.toUtf16().cloneTo(aId); 1000 1001 return S_OK; 1002 } 1003 1004 STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId) 1005 { 1006 CheckComArgOutPointerValid(aOSTypeId); 1007 1008 AutoCaller autoCaller(this); 1009 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1010 1011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1012 1013 mUserData->s.strOsType.cloneTo(aOSTypeId); 1014 1015 return S_OK; 1016 } 1017 1018 STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId) 1019 { 1020 CheckComArgStrNotEmptyOrNull(aOSTypeId); 1021 1022 AutoCaller autoCaller(this); 1023 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1024 1025 /* look up the object by Id to check it is valid */ 1026 ComPtr<IGuestOSType> guestOSType; 1027 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam()); 1028 if (FAILED(rc)) return rc; 1029 1030 /* when setting, always use the "etalon" value for consistency -- lookup 1031 * by ID is case-insensitive and the input value may have different case */ 1032 Bstr osTypeId; 1033 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam()); 1034 if (FAILED(rc)) return rc; 1035 1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1037 1038 rc = checkStateDependency(MutableStateDep); 1039 if (FAILED(rc)) return rc; 1040 1041 setModified(IsModified_MachineData); 1042 mUserData.backup(); 1043 mUserData->s.strOsType = osTypeId; 1044 1045 return S_OK; 1046 } 1047 1048 1049 STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType) 1050 { 1051 CheckComArgOutPointerValid(aFirmwareType); 1052 1053 AutoCaller autoCaller(this); 1054 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1055 1056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1057 1058 *aFirmwareType = mHWData->mFirmwareType; 1059 1060 return S_OK; 1061 } 1062 1063 STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType) 1064 { 1065 AutoCaller autoCaller(this); 1066 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1068 1069 int rc = checkStateDependency(MutableStateDep); 1070 if (FAILED(rc)) return rc; 1071 1072 setModified(IsModified_MachineData); 1073 mHWData.backup(); 1074 mHWData->mFirmwareType = aFirmwareType; 1075 1076 return S_OK; 1077 } 1078 1079 STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType) 1080 { 1081 CheckComArgOutPointerValid(aKeyboardHidType); 1082 1083 AutoCaller autoCaller(this); 1084 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1085 1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1087 1088 *aKeyboardHidType = mHWData->mKeyboardHidType; 1089 1090 return S_OK; 1091 } 1092 1093 STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType) 1094 { 1095 AutoCaller autoCaller(this); 1096 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1098 1099 int rc = checkStateDependency(MutableStateDep); 1100 if (FAILED(rc)) return rc; 1101 1102 setModified(IsModified_MachineData); 1103 mHWData.backup(); 1104 mHWData->mKeyboardHidType = aKeyboardHidType; 1105 1106 return S_OK; 1107 } 1108 1109 STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType) 1110 { 1111 CheckComArgOutPointerValid(aPointingHidType); 1112 1113 AutoCaller autoCaller(this); 1114 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1115 1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1117 1118 *aPointingHidType = mHWData->mPointingHidType; 1119 1120 return S_OK; 1121 } 1122 1123 STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType) 1124 { 1125 AutoCaller autoCaller(this); 1126 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1128 1129 int rc = checkStateDependency(MutableStateDep); 1130 if (FAILED(rc)) return rc; 1131 1132 setModified(IsModified_MachineData); 1133 mHWData.backup(); 1134 mHWData->mPointingHidType = aPointingHidType; 1135 1136 return S_OK; 1137 } 1138 1139 STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType) 1140 { 1141 CheckComArgOutPointerValid(aChipsetType); 1142 1143 AutoCaller autoCaller(this); 1144 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1145 1146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1147 1148 *aChipsetType = mHWData->mChipsetType; 1149 1150 return S_OK; 1151 } 1152 1153 STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType) 1154 { 1155 AutoCaller autoCaller(this); 1156 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1158 1159 int rc = checkStateDependency(MutableStateDep); 1160 if (FAILED(rc)) return rc; 1161 1162 setModified(IsModified_MachineData); 1163 mHWData.backup(); 1164 mHWData->mChipsetType = aChipsetType; 1165 1166 return S_OK; 1167 } 1168 1169 STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion) 1170 { 1171 if (!aHWVersion) 1172 return E_POINTER; 1173 1174 AutoCaller autoCaller(this); 1175 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1176 1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1178 1179 mHWData->mHWVersion.cloneTo(aHWVersion); 1180 1181 return S_OK; 1182 } 1183 1184 STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion) 1185 { 1186 /* check known version */ 1187 Utf8Str hwVersion = aHWVersion; 1188 if ( hwVersion.compare("1") != 0 1189 && hwVersion.compare("2") != 0) 1190 return setError(E_INVALIDARG, 1191 tr("Invalid hardware version: %ls\n"), aHWVersion); 1192 1193 AutoCaller autoCaller(this); 1194 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1195 1196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1197 1198 HRESULT rc = checkStateDependency(MutableStateDep); 1199 if (FAILED(rc)) return rc; 1200 1201 setModified(IsModified_MachineData); 1202 mHWData.backup(); 1203 mHWData->mHWVersion = hwVersion; 1204 1205 return S_OK; 1206 } 1207 1208 STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID) 1209 { 1210 CheckComArgOutPointerValid(aUUID); 1211 1212 AutoCaller autoCaller(this); 1213 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1214 1215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1216 1217 if (!mHWData->mHardwareUUID.isEmpty()) 1218 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID); 1219 else 1220 mData->mUuid.toUtf16().cloneTo(aUUID); 1221 1222 return S_OK; 1223 } 1224 1225 STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID) 1226 { 1227 Guid hardwareUUID(aUUID); 1228 if (hardwareUUID.isEmpty()) 1229 return E_INVALIDARG; 1230 1231 AutoCaller autoCaller(this); 1232 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1233 1234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1235 1236 HRESULT rc = checkStateDependency(MutableStateDep); 1237 if (FAILED(rc)) return rc; 1238 1239 setModified(IsModified_MachineData); 1240 mHWData.backup(); 1241 if (hardwareUUID == mData->mUuid) 1242 mHWData->mHardwareUUID.clear(); 1243 else 1244 mHWData->mHardwareUUID = hardwareUUID; 1245 1246 return S_OK; 1247 } 1248 1249 STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize) 1250 { 1251 if (!memorySize) 1252 return E_POINTER; 1253 1254 AutoCaller autoCaller(this); 1255 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1256 1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1258 1259 *memorySize = mHWData->mMemorySize; 1260 1261 return S_OK; 1262 } 1263 1264 STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize) 1265 { 1266 /* check RAM limits */ 1267 if ( memorySize < MM_RAM_MIN_IN_MB 1268 || memorySize > MM_RAM_MAX_IN_MB 1269 ) 1270 return setError(E_INVALIDARG, 1271 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"), 1272 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB); 1273 1274 AutoCaller autoCaller(this); 1275 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1276 1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1278 1279 HRESULT rc = checkStateDependency(MutableStateDep); 1280 if (FAILED(rc)) return rc; 1281 1282 setModified(IsModified_MachineData); 1283 mHWData.backup(); 1284 mHWData->mMemorySize = memorySize; 1285 1286 return S_OK; 1287 } 1288 1289 STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount) 1290 { 1291 if (!CPUCount) 1292 return E_POINTER; 1293 1294 AutoCaller autoCaller(this); 1295 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1296 1297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1298 1299 *CPUCount = mHWData->mCPUCount; 1300 1301 return S_OK; 1302 } 1303 1304 STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount) 1305 { 1306 /* check CPU limits */ 1307 if ( CPUCount < SchemaDefs::MinCPUCount 1308 || CPUCount > SchemaDefs::MaxCPUCount 1309 ) 1310 return setError(E_INVALIDARG, 1311 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"), 1312 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount); 1313 1314 AutoCaller autoCaller(this); 1315 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1316 1317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1318 1319 /* We cant go below the current number of CPUs attached if hotplug is enabled*/ 1320 if (mHWData->mCPUHotPlugEnabled) 1321 { 1322 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++) 1323 { 1324 if (mHWData->mCPUAttached[idx]) 1325 return setError(E_INVALIDARG, 1326 tr("There is still a CPU attached to socket %lu." 1327 "Detach the CPU before removing the socket"), 1328 CPUCount, idx+1); 1329 } 1330 } 1331 1332 HRESULT rc = checkStateDependency(MutableStateDep); 1333 if (FAILED(rc)) return rc; 1334 1335 setModified(IsModified_MachineData); 1336 mHWData.backup(); 1337 mHWData->mCPUCount = CPUCount; 1338 1339 return S_OK; 1340 } 1341 1342 STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap) 1343 { 1344 if (!aExecutionCap) 1345 return E_POINTER; 1346 1347 AutoCaller autoCaller(this); 1348 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1349 1350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1351 1352 *aExecutionCap = mHWData->mCpuExecutionCap; 1353 1354 return S_OK; 1355 } 1356 1357 STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap) 1358 { 1359 HRESULT rc = S_OK; 1360 1361 /* check throttle limits */ 1362 if ( aExecutionCap < 1 1363 || aExecutionCap > 100 1364 ) 1365 return setError(E_INVALIDARG, 1366 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"), 1367 aExecutionCap, 1, 100); 1368 1369 AutoCaller autoCaller(this); 1370 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1371 1372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1373 1374 alock.release(); 1375 rc = onCPUExecutionCapChange(aExecutionCap); 1376 alock.acquire(); 1377 if (FAILED(rc)) return rc; 1378 1379 setModified(IsModified_MachineData); 1380 mHWData.backup(); 1381 mHWData->mCpuExecutionCap = aExecutionCap; 1382 1383 /* Save settings if online - todo why is this required?? */ 1384 if (Global::IsOnline(mData->mMachineState)) 1385 saveSettings(NULL); 1386 1387 return S_OK; 1388 } 1389 1390 1391 STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled) 1392 { 1393 if (!enabled) 1394 return E_POINTER; 1395 1396 AutoCaller autoCaller(this); 1397 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1398 1399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1400 1401 *enabled = mHWData->mCPUHotPlugEnabled; 1402 1403 return S_OK; 1404 } 1405 1406 STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled) 1407 { 1408 HRESULT rc = S_OK; 1409 1410 AutoCaller autoCaller(this); 1411 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1412 1413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1414 1415 rc = checkStateDependency(MutableStateDep); 1416 if (FAILED(rc)) return rc; 1417 1418 if (mHWData->mCPUHotPlugEnabled != enabled) 1419 { 1420 if (enabled) 1421 { 1422 setModified(IsModified_MachineData); 1423 mHWData.backup(); 1424 1425 /* Add the amount of CPUs currently attached */ 1426 for (unsigned i = 0; i < mHWData->mCPUCount; i++) 1427 { 1428 mHWData->mCPUAttached[i] = true; 1429 } 1430 } 1431 else 1432 { 1433 /* 1434 * We can disable hotplug only if the amount of maximum CPUs is equal 1435 * to the amount of attached CPUs 1436 */ 1437 unsigned cCpusAttached = 0; 1438 unsigned iHighestId = 0; 1439 1440 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++) 1441 { 1442 if (mHWData->mCPUAttached[i]) 1443 { 1444 cCpusAttached++; 1445 iHighestId = i; 1446 } 1447 } 1448 1449 if ( (cCpusAttached != mHWData->mCPUCount) 1450 || (iHighestId >= mHWData->mCPUCount)) 1451 return setError(E_INVALIDARG, 1452 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n")); 1453 1454 setModified(IsModified_MachineData); 1455 mHWData.backup(); 1456 } 1457 } 1458 1459 mHWData->mCPUHotPlugEnabled = enabled; 1460 1461 return rc; 1462 } 1463 1464 STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled) 1465 { 1466 CheckComArgOutPointerValid(enabled); 1467 1468 AutoCaller autoCaller(this); 1469 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1471 1472 *enabled = mHWData->mHpetEnabled; 1473 1474 return S_OK; 1475 } 1476 1477 STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled) 1478 { 1479 HRESULT rc = S_OK; 1480 1481 AutoCaller autoCaller(this); 1482 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1484 1485 rc = checkStateDependency(MutableStateDep); 1486 if (FAILED(rc)) return rc; 1487 1488 setModified(IsModified_MachineData); 1489 mHWData.backup(); 1490 1491 mHWData->mHpetEnabled = enabled; 1492 1493 return rc; 1494 } 1495 1496 STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize) 1497 { 1498 if (!memorySize) 1499 return E_POINTER; 1500 1501 AutoCaller autoCaller(this); 1502 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1503 1504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1505 1506 *memorySize = mHWData->mVRAMSize; 1507 1508 return S_OK; 1509 } 1510 1511 STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize) 1512 { 1513 /* check VRAM limits */ 1514 if (memorySize < SchemaDefs::MinGuestVRAM || 1515 memorySize > SchemaDefs::MaxGuestVRAM) 1516 return setError(E_INVALIDARG, 1517 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"), 1518 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM); 1519 1520 AutoCaller autoCaller(this); 1521 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1522 1523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1524 1525 HRESULT rc = checkStateDependency(MutableStateDep); 1526 if (FAILED(rc)) return rc; 1527 1528 setModified(IsModified_MachineData); 1529 mHWData.backup(); 1530 mHWData->mVRAMSize = memorySize; 1531 1532 return S_OK; 1533 } 1534 1535 /** @todo this method should not be public */ 1536 STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize) 1537 { 1538 if (!memoryBalloonSize) 1539 return E_POINTER; 1540 1541 AutoCaller autoCaller(this); 1542 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1543 1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1545 1546 *memoryBalloonSize = mHWData->mMemoryBalloonSize; 1547 1548 return S_OK; 1549 } 1550 1551 /** 1552 * Set the memory balloon size. 1553 * 1554 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so 1555 * we have to make sure that we never call IGuest from here. 1556 */ 1557 STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize) 1558 { 1559 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */ 1560 #if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) 1561 /* check limits */ 1562 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)) 1563 return setError(E_INVALIDARG, 1564 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"), 1565 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)); 1566 1567 AutoCaller autoCaller(this); 1568 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1569 1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1571 1572 setModified(IsModified_MachineData); 1573 mHWData.backup(); 1574 mHWData->mMemoryBalloonSize = memoryBalloonSize; 1575 1576 return S_OK; 1577 #else 1578 NOREF(memoryBalloonSize); 1579 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts")); 1580 #endif 1581 } 1582 1583 STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled) 1584 { 1585 if (!enabled) 1586 return E_POINTER; 1587 1588 AutoCaller autoCaller(this); 1589 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1590 1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1592 1593 *enabled = mHWData->mPageFusionEnabled; 1594 return S_OK; 1595 } 1596 1597 STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled) 1598 { 1599 #ifdef VBOX_WITH_PAGE_SHARING 1600 AutoCaller autoCaller(this); 1601 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1602 1603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1604 1605 /** @todo must support changes for running vms and keep this in sync with IGuest. */ 1606 setModified(IsModified_MachineData); 1607 mHWData.backup(); 1608 mHWData->mPageFusionEnabled = enabled; 1609 return S_OK; 1610 #else 1611 NOREF(enabled); 1612 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts")); 1613 #endif 1614 } 1615 1616 STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled) 1617 { 1618 if (!enabled) 1619 return E_POINTER; 1620 1621 AutoCaller autoCaller(this); 1622 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1623 1624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1625 1626 *enabled = mHWData->mAccelerate3DEnabled; 1627 1628 return S_OK; 1629 } 1630 1631 STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable) 1632 { 1633 AutoCaller autoCaller(this); 1634 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1635 1636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1637 1638 HRESULT rc = checkStateDependency(MutableStateDep); 1639 if (FAILED(rc)) return rc; 1640 1641 /** @todo check validity! */ 1642 1643 setModified(IsModified_MachineData); 1644 mHWData.backup(); 1645 mHWData->mAccelerate3DEnabled = enable; 1646 1647 return S_OK; 1648 } 1649 1650 1651 STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled) 1652 { 1653 if (!enabled) 1654 return E_POINTER; 1655 1656 AutoCaller autoCaller(this); 1657 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1658 1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1660 1661 *enabled = mHWData->mAccelerate2DVideoEnabled; 1662 1663 return S_OK; 1664 } 1665 1666 STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable) 1667 { 1668 AutoCaller autoCaller(this); 1669 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1670 1671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1672 1673 HRESULT rc = checkStateDependency(MutableStateDep); 1674 if (FAILED(rc)) return rc; 1675 1676 /** @todo check validity! */ 1677 1678 setModified(IsModified_MachineData); 1679 mHWData.backup(); 1680 mHWData->mAccelerate2DVideoEnabled = enable; 1681 1682 return S_OK; 1683 } 1684 1685 STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount) 1686 { 1687 if (!monitorCount) 1688 return E_POINTER; 1689 1690 AutoCaller autoCaller(this); 1691 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1692 1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1694 1695 *monitorCount = mHWData->mMonitorCount; 1696 1697 return S_OK; 1698 } 1699 1700 STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount) 1701 { 1702 /* make sure monitor count is a sensible number */ 1703 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors) 1704 return setError(E_INVALIDARG, 1705 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"), 1706 monitorCount, 1, SchemaDefs::MaxGuestMonitors); 1707 1708 AutoCaller autoCaller(this); 1709 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1710 1711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1712 1713 HRESULT rc = checkStateDependency(MutableStateDep); 1714 if (FAILED(rc)) return rc; 1715 1716 setModified(IsModified_MachineData); 1717 mHWData.backup(); 1718 mHWData->mMonitorCount = monitorCount; 1719 1720 return S_OK; 1721 } 1722 1723 STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings) 1724 { 1725 if (!biosSettings) 1726 return E_POINTER; 1727 1728 AutoCaller autoCaller(this); 1729 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1730 1731 /* mBIOSSettings is constant during life time, no need to lock */ 1732 mBIOSSettings.queryInterfaceTo(biosSettings); 1733 1734 return S_OK; 1735 } 1736 1737 STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal) 1738 { 1739 if (!aVal) 1740 return E_POINTER; 1741 1742 AutoCaller autoCaller(this); 1743 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1744 1745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1746 1747 switch(property) 1748 { 1749 case CPUPropertyType_PAE: 1750 *aVal = mHWData->mPAEEnabled; 1751 break; 1752 1753 case CPUPropertyType_Synthetic: 1754 *aVal = mHWData->mSyntheticCpu; 1755 break; 1756 1757 default: 1758 return E_INVALIDARG; 1759 } 1760 return S_OK; 1761 } 1762 1763 STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal) 1764 { 1765 AutoCaller autoCaller(this); 1766 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1767 1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1769 1770 HRESULT rc = checkStateDependency(MutableStateDep); 1771 if (FAILED(rc)) return rc; 1772 1773 switch(property) 1774 { 1775 case CPUPropertyType_PAE: 1776 setModified(IsModified_MachineData); 1777 mHWData.backup(); 1778 mHWData->mPAEEnabled = !!aVal; 1779 break; 1780 1781 case CPUPropertyType_Synthetic: 1782 setModified(IsModified_MachineData); 1783 mHWData.backup(); 1784 mHWData->mSyntheticCpu = !!aVal; 1785 break; 1786 1787 default: 1788 return E_INVALIDARG; 1789 } 1790 return S_OK; 1791 } 1792 1793 STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx) 1794 { 1795 CheckComArgOutPointerValid(aValEax); 1796 CheckComArgOutPointerValid(aValEbx); 1797 CheckComArgOutPointerValid(aValEcx); 1798 CheckComArgOutPointerValid(aValEdx); 1799 1800 AutoCaller autoCaller(this); 1801 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1802 1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1804 1805 switch(aId) 1806 { 1807 case 0x0: 1808 case 0x1: 1809 case 0x2: 1810 case 0x3: 1811 case 0x4: 1812 case 0x5: 1813 case 0x6: 1814 case 0x7: 1815 case 0x8: 1816 case 0x9: 1817 case 0xA: 1818 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId) 1819 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId); 1820 1821 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax; 1822 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx; 1823 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx; 1824 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx; 1825 break; 1826 1827 case 0x80000000: 1828 case 0x80000001: 1829 case 0x80000002: 1830 case 0x80000003: 1831 case 0x80000004: 1832 case 0x80000005: 1833 case 0x80000006: 1834 case 0x80000007: 1835 case 0x80000008: 1836 case 0x80000009: 1837 case 0x8000000A: 1838 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId) 1839 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId); 1840 1841 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax; 1842 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx; 1843 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx; 1844 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx; 1845 break; 1846 1847 default: 1848 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1849 } 1850 return S_OK; 1851 } 1852 1853 STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx) 1854 { 1855 AutoCaller autoCaller(this); 1856 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1857 1858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1859 1860 HRESULT rc = checkStateDependency(MutableStateDep); 1861 if (FAILED(rc)) return rc; 1862 1863 switch(aId) 1864 { 1865 case 0x0: 1866 case 0x1: 1867 case 0x2: 1868 case 0x3: 1869 case 0x4: 1870 case 0x5: 1871 case 0x6: 1872 case 0x7: 1873 case 0x8: 1874 case 0x9: 1875 case 0xA: 1876 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA); 1877 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 1878 setModified(IsModified_MachineData); 1879 mHWData.backup(); 1880 mHWData->mCpuIdStdLeafs[aId].ulId = aId; 1881 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax; 1882 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx; 1883 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx; 1884 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx; 1885 break; 1886 1887 case 0x80000000: 1888 case 0x80000001: 1889 case 0x80000002: 1890 case 0x80000003: 1891 case 0x80000004: 1892 case 0x80000005: 1893 case 0x80000006: 1894 case 0x80000007: 1895 case 0x80000008: 1896 case 0x80000009: 1897 case 0x8000000A: 1898 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA); 1899 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 1900 setModified(IsModified_MachineData); 1901 mHWData.backup(); 1902 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId; 1903 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax; 1904 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx; 1905 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx; 1906 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx; 1907 break; 1908 1909 default: 1910 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1911 } 1912 return S_OK; 1913 } 1914 1915 STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId) 1916 { 1917 AutoCaller autoCaller(this); 1918 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1919 1920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1921 1922 HRESULT rc = checkStateDependency(MutableStateDep); 1923 if (FAILED(rc)) return rc; 1924 1925 switch(aId) 1926 { 1927 case 0x0: 1928 case 0x1: 1929 case 0x2: 1930 case 0x3: 1931 case 0x4: 1932 case 0x5: 1933 case 0x6: 1934 case 0x7: 1935 case 0x8: 1936 case 0x9: 1937 case 0xA: 1938 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA); 1939 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 1940 setModified(IsModified_MachineData); 1941 mHWData.backup(); 1942 /* Invalidate leaf. */ 1943 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX; 1944 break; 1945 1946 case 0x80000000: 1947 case 0x80000001: 1948 case 0x80000002: 1949 case 0x80000003: 1950 case 0x80000004: 1951 case 0x80000005: 1952 case 0x80000006: 1953 case 0x80000007: 1954 case 0x80000008: 1955 case 0x80000009: 1956 case 0x8000000A: 1957 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA); 1958 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 1959 setModified(IsModified_MachineData); 1960 mHWData.backup(); 1961 /* Invalidate leaf. */ 1962 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX; 1963 break; 1964 1965 default: 1966 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1967 } 1968 return S_OK; 1969 } 1970 1971 STDMETHODIMP Machine::RemoveAllCPUIDLeaves() 1972 { 1973 AutoCaller autoCaller(this); 1974 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1975 1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1977 1978 HRESULT rc = checkStateDependency(MutableStateDep); 1979 if (FAILED(rc)) return rc; 1980 1981 setModified(IsModified_MachineData); 1982 mHWData.backup(); 1983 1984 /* Invalidate all standard leafs. */ 1985 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++) 1986 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX; 1987 1988 /* Invalidate all extended leafs. */ 1989 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++) 1990 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX; 1991 1992 return S_OK; 1993 } 1994 1995 STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal) 1996 { 1997 if (!aVal) 1998 return E_POINTER; 1999 2000 AutoCaller autoCaller(this); 2001 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2002 2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2004 2005 switch(property) 2006 { 2007 case HWVirtExPropertyType_Enabled: 2008 *aVal = mHWData->mHWVirtExEnabled; 2009 break; 2010 2011 case HWVirtExPropertyType_Exclusive: 2012 *aVal = mHWData->mHWVirtExExclusive; 2013 break; 2014 2015 case HWVirtExPropertyType_VPID: 2016 *aVal = mHWData->mHWVirtExVPIDEnabled; 2017 break; 2018 2019 case HWVirtExPropertyType_NestedPaging: 2020 *aVal = mHWData->mHWVirtExNestedPagingEnabled; 2021 break; 2022 2023 case HWVirtExPropertyType_LargePages: 2024 *aVal = mHWData->mHWVirtExLargePagesEnabled; 2025 #if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */ 2026 *aVal = FALSE; 2027 #endif 2028 break; 2029 2030 case HWVirtExPropertyType_Force: 2031 *aVal = mHWData->mHWVirtExForceEnabled; 2032 break; 2033 2034 default: 2035 return E_INVALIDARG; 2036 } 2037 return S_OK; 2038 } 2039 2040 STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal) 2041 { 2042 AutoCaller autoCaller(this); 2043 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2044 2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2046 2047 HRESULT rc = checkStateDependency(MutableStateDep); 2048 if (FAILED(rc)) return rc; 2049 2050 switch(property) 2051 { 2052 case HWVirtExPropertyType_Enabled: 2053 setModified(IsModified_MachineData); 2054 mHWData.backup(); 2055 mHWData->mHWVirtExEnabled = !!aVal; 2056 break; 2057 2058 case HWVirtExPropertyType_Exclusive: 2059 setModified(IsModified_MachineData); 2060 mHWData.backup(); 2061 mHWData->mHWVirtExExclusive = !!aVal; 2062 break; 2063 2064 case HWVirtExPropertyType_VPID: 2065 setModified(IsModified_MachineData); 2066 mHWData.backup(); 2067 mHWData->mHWVirtExVPIDEnabled = !!aVal; 2068 break; 2069 2070 case HWVirtExPropertyType_NestedPaging: 2071 setModified(IsModified_MachineData); 2072 mHWData.backup(); 2073 mHWData->mHWVirtExNestedPagingEnabled = !!aVal; 2074 break; 2075 2076 case HWVirtExPropertyType_LargePages: 2077 setModified(IsModified_MachineData); 2078 mHWData.backup(); 2079 mHWData->mHWVirtExLargePagesEnabled = !!aVal; 2080 break; 2081 2082 case HWVirtExPropertyType_Force: 2083 setModified(IsModified_MachineData); 2084 mHWData.backup(); 2085 mHWData->mHWVirtExForceEnabled = !!aVal; 2086 break; 2087 2088 default: 2089 return E_INVALIDARG; 2090 } 2091 2092 return S_OK; 2093 } 2094 2095 STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder) 2096 { 2097 CheckComArgOutPointerValid(aSnapshotFolder); 2098 2099 AutoCaller autoCaller(this); 2100 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2101 2102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2103 2104 Utf8Str strFullSnapshotFolder; 2105 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 2106 strFullSnapshotFolder.cloneTo(aSnapshotFolder); 2107 2108 return S_OK; 2109 } 2110 2111 STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder) 2112 { 2113 /* @todo (r=dmik): 2114 * 1. Allow to change the name of the snapshot folder containing snapshots 2115 * 2. Rename the folder on disk instead of just changing the property 2116 * value (to be smart and not to leave garbage). Note that it cannot be 2117 * done here because the change may be rolled back. Thus, the right 2118 * place is #saveSettings(). 2119 */ 2120 2121 AutoCaller autoCaller(this); 2122 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2123 2124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2125 2126 HRESULT rc = checkStateDependency(MutableStateDep); 2127 if (FAILED(rc)) return rc; 2128 2129 if (!mData->mCurrentSnapshot.isNull()) 2130 return setError(E_FAIL, 2131 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)")); 2132 2133 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original 2134 2135 Utf8Str strSnapshotFolder(strSnapshotFolder0); 2136 if (strSnapshotFolder.isEmpty()) 2137 strSnapshotFolder = "Snapshots"; 2138 int vrc = calculateFullPath(strSnapshotFolder, 2139 strSnapshotFolder); 2140 if (RT_FAILURE(vrc)) 2141 return setError(E_FAIL, 2142 tr("Invalid snapshot folder '%ls' (%Rrc)"), 2143 aSnapshotFolder, vrc); 2144 2145 setModified(IsModified_MachineData); 2146 mUserData.backup(); 2147 2148 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder); 2149 2150 return S_OK; 2151 } 2152 2153 STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments)) 2154 { 2155 if (ComSafeArrayOutIsNull(aAttachments)) 2156 return E_POINTER; 2157 2158 AutoCaller autoCaller(this); 2159 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2160 2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2162 2163 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments); 2164 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 2165 2166 return S_OK; 2167 } 2168 2169 STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer) 2170 { 2171 if (!vrdeServer) 2172 return E_POINTER; 2173 2174 AutoCaller autoCaller(this); 2175 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2176 2177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2178 2179 Assert(!!mVRDEServer); 2180 mVRDEServer.queryInterfaceTo(vrdeServer); 2181 2182 return S_OK; 2183 } 2184 2185 STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter) 2186 { 2187 if (!audioAdapter) 2188 return E_POINTER; 2189 2190 AutoCaller autoCaller(this); 2191 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2192 2193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2194 2195 mAudioAdapter.queryInterfaceTo(audioAdapter); 2196 return S_OK; 2197 } 2198 2199 STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController) 2200 { 2201 #ifdef VBOX_WITH_VUSB 2202 CheckComArgOutPointerValid(aUSBController); 2203 2204 AutoCaller autoCaller(this); 2205 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2206 2207 clearError(); 2208 MultiResult rc(S_OK); 2209 2210 # ifdef VBOX_WITH_USB 2211 rc = mParent->host()->checkUSBProxyService(); 2212 if (FAILED(rc)) return rc; 2213 # endif 2214 2215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2216 2217 return rc = mUSBController.queryInterfaceTo(aUSBController); 2218 #else 2219 /* Note: The GUI depends on this method returning E_NOTIMPL with no 2220 * extended error info to indicate that USB is simply not available 2221 * (w/o treating it as a failure), for example, as in OSE */ 2222 NOREF(aUSBController); 2223 ReturnComNotImplemented(); 2224 #endif /* VBOX_WITH_VUSB */ 2225 } 2226 2227 STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath) 2228 { 2229 CheckComArgOutPointerValid(aFilePath); 2230 2231 AutoLimitedCaller autoCaller(this); 2232 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2233 2234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2235 2236 mData->m_strConfigFileFull.cloneTo(aFilePath); 2237 return S_OK; 2238 } 2239 2240 STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified) 2241 { 2242 CheckComArgOutPointerValid(aModified); 2243 2244 AutoCaller autoCaller(this); 2245 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2246 2247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2248 2249 HRESULT rc = checkStateDependency(MutableStateDep); 2250 if (FAILED(rc)) return rc; 2251 2252 if (!mData->pMachineConfigFile->fileExists()) 2253 // this is a new machine, and no config file exists yet: 2254 *aModified = TRUE; 2255 else 2256 *aModified = (mData->flModifications != 0); 2257 2258 return S_OK; 2259 } 2260 2261 STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState) 2262 { 2263 CheckComArgOutPointerValid(aSessionState); 2264 2265 AutoCaller autoCaller(this); 2266 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2267 2268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2269 2270 *aSessionState = mData->mSession.mState; 2271 2272 return S_OK; 2273 } 2274 2275 STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType) 2276 { 2277 CheckComArgOutPointerValid(aSessionType); 2278 2279 AutoCaller autoCaller(this); 2280 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2281 2282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2283 2284 mData->mSession.mType.cloneTo(aSessionType); 2285 2286 return S_OK; 2287 } 2288 2289 STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid) 2290 { 2291 CheckComArgOutPointerValid(aSessionPid); 2292 2293 AutoCaller autoCaller(this); 2294 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2295 2296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2297 2298 *aSessionPid = mData->mSession.mPid; 2299 2300 return S_OK; 2301 } 2302 2303 STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState) 2304 { 2305 if (!machineState) 2306 return E_POINTER; 2307 2308 AutoCaller autoCaller(this); 2309 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2310 2311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2312 2313 *machineState = mData->mMachineState; 2314 2315 return S_OK; 2316 } 2317 2318 STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange) 2319 { 2320 CheckComArgOutPointerValid(aLastStateChange); 2321 2322 AutoCaller autoCaller(this); 2323 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2324 2325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2326 2327 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange); 2328 2329 return S_OK; 2330 } 2331 2332 STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath) 2333 { 2334 CheckComArgOutPointerValid(aStateFilePath); 2335 2336 AutoCaller autoCaller(this); 2337 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2338 2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2340 2341 mSSData->strStateFilePath.cloneTo(aStateFilePath); 2342 2343 return S_OK; 2344 } 2345 2346 STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder) 2347 { 2348 CheckComArgOutPointerValid(aLogFolder); 2349 2350 AutoCaller autoCaller(this); 2351 AssertComRCReturnRC(autoCaller.rc()); 2352 2353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2354 2355 Utf8Str logFolder; 2356 getLogFolder(logFolder); 2357 logFolder.cloneTo(aLogFolder); 2358 2359 return S_OK; 2360 } 2361 2362 STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot) 2363 { 2364 CheckComArgOutPointerValid(aCurrentSnapshot); 2365 2366 AutoCaller autoCaller(this); 2367 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2368 2369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2370 2371 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot); 2372 2373 return S_OK; 2374 } 2375 2376 STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount) 2377 { 2378 CheckComArgOutPointerValid(aSnapshotCount); 2379 2380 AutoCaller autoCaller(this); 2381 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2382 2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2384 2385 *aSnapshotCount = mData->mFirstSnapshot.isNull() 2386 ? 0 2387 : mData->mFirstSnapshot->getAllChildrenCount() + 1; 2388 2389 return S_OK; 2390 } 2391 2392 STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified) 2393 { 2394 CheckComArgOutPointerValid(aCurrentStateModified); 2395 2396 AutoCaller autoCaller(this); 2397 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2398 2399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2400 2401 /* Note: for machines with no snapshots, we always return FALSE 2402 * (mData->mCurrentStateModified will be TRUE in this case, for historical 2403 * reasons :) */ 2404 2405 *aCurrentStateModified = mData->mFirstSnapshot.isNull() 2406 ? FALSE 2407 : mData->mCurrentStateModified; 2408 2409 return S_OK; 2410 } 2411 2412 STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders)) 2413 { 2414 CheckComArgOutSafeArrayPointerValid(aSharedFolders); 2415 2416 AutoCaller autoCaller(this); 2417 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2418 2419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2420 2421 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders); 2422 folders.detachTo(ComSafeArrayOutArg(aSharedFolders)); 2423 2424 return S_OK; 2425 } 2426 2427 STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode) 2428 { 2429 CheckComArgOutPointerValid(aClipboardMode); 2430 2431 AutoCaller autoCaller(this); 2432 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2433 2434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2435 2436 *aClipboardMode = mHWData->mClipboardMode; 2437 2438 return S_OK; 2439 } 2440 2441 STDMETHODIMP 2442 Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) 2443 { 2444 AutoCaller autoCaller(this); 2445 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2446 2447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2448 2449 HRESULT rc = checkStateDependency(MutableStateDep); 2450 if (FAILED(rc)) return rc; 2451 2452 setModified(IsModified_MachineData); 2453 mHWData.backup(); 2454 mHWData->mClipboardMode = aClipboardMode; 2455 2456 return S_OK; 2457 } 2458 2459 STDMETHODIMP 2460 Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) 2461 { 2462 CheckComArgOutPointerValid(aPatterns); 2463 2464 AutoCaller autoCaller(this); 2465 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2466 2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2468 2469 try 2470 { 2471 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns); 2472 } 2473 catch (...) 2474 { 2475 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 2476 } 2477 2478 return S_OK; 2479 } 2480 2481 STDMETHODIMP 2482 Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) 2483 { 2484 AutoCaller autoCaller(this); 2485 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2486 2487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2488 2489 HRESULT rc = checkStateDependency(MutableStateDep); 2490 if (FAILED(rc)) return rc; 2491 2492 setModified(IsModified_MachineData); 2493 mHWData.backup(); 2494 mHWData->mGuestPropertyNotificationPatterns = aPatterns; 2495 return rc; 2496 } 2497 2498 STDMETHODIMP 2499 Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers)) 2500 { 2501 CheckComArgOutSafeArrayPointerValid(aStorageControllers); 2502 2503 AutoCaller autoCaller(this); 2504 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2505 2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2507 2508 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data()); 2509 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers)); 2510 2511 return S_OK; 2512 } 2513 2514 STDMETHODIMP 2515 Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled) 2516 { 2517 CheckComArgOutPointerValid(aEnabled); 2518 2519 AutoCaller autoCaller(this); 2520 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2521 2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2523 2524 *aEnabled = mUserData->s.fTeleporterEnabled; 2525 2526 return S_OK; 2527 } 2528 2529 STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled) 2530 { 2531 AutoCaller autoCaller(this); 2532 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2533 2534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2535 2536 /* Only allow it to be set to true when PoweredOff or Aborted. 2537 (Clearing it is always permitted.) */ 2538 if ( aEnabled 2539 && mData->mRegistered 2540 && ( !isSessionMachine() 2541 || ( mData->mMachineState != MachineState_PoweredOff 2542 && mData->mMachineState != MachineState_Teleported 2543 && mData->mMachineState != MachineState_Aborted 2544 ) 2545 ) 2546 ) 2547 return setError(VBOX_E_INVALID_VM_STATE, 2548 tr("The machine is not powered off (state is %s)"), 2549 Global::stringifyMachineState(mData->mMachineState)); 2550 2551 setModified(IsModified_MachineData); 2552 mUserData.backup(); 2553 mUserData->s.fTeleporterEnabled = !!aEnabled; 2554 2555 return S_OK; 2556 } 2557 2558 STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort) 2559 { 2560 CheckComArgOutPointerValid(aPort); 2561 2562 AutoCaller autoCaller(this); 2563 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2564 2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2566 2567 *aPort = (ULONG)mUserData->s.uTeleporterPort; 2568 2569 return S_OK; 2570 } 2571 2572 STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort) 2573 { 2574 if (aPort >= _64K) 2575 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort); 2576 2577 AutoCaller autoCaller(this); 2578 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2579 2580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2581 2582 HRESULT rc = checkStateDependency(MutableStateDep); 2583 if (FAILED(rc)) return rc; 2584 2585 setModified(IsModified_MachineData); 2586 mUserData.backup(); 2587 mUserData->s.uTeleporterPort = (uint32_t)aPort; 2588 2589 return S_OK; 2590 } 2591 2592 STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress) 2593 { 2594 CheckComArgOutPointerValid(aAddress); 2595 2596 AutoCaller autoCaller(this); 2597 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2598 2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2600 2601 mUserData->s.strTeleporterAddress.cloneTo(aAddress); 2602 2603 return S_OK; 2604 } 2605 2606 STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress) 2607 { 2608 AutoCaller autoCaller(this); 2609 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2610 2611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2612 2613 HRESULT rc = checkStateDependency(MutableStateDep); 2614 if (FAILED(rc)) return rc; 2615 2616 setModified(IsModified_MachineData); 2617 mUserData.backup(); 2618 mUserData->s.strTeleporterAddress = aAddress; 2619 2620 return S_OK; 2621 } 2622 2623 STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword) 2624 { 2625 CheckComArgOutPointerValid(aPassword); 2626 2627 AutoCaller autoCaller(this); 2628 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2629 2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2631 2632 mUserData->s.strTeleporterPassword.cloneTo(aPassword); 2633 2634 return S_OK; 2635 } 2636 2637 STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword) 2638 { 2639 AutoCaller autoCaller(this); 2640 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2641 2642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2643 2644 HRESULT rc = checkStateDependency(MutableStateDep); 2645 if (FAILED(rc)) return rc; 2646 2647 setModified(IsModified_MachineData); 2648 mUserData.backup(); 2649 mUserData->s.strTeleporterPassword = aPassword; 2650 2651 return S_OK; 2652 } 2653 2654 STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState) 2655 { 2656 CheckComArgOutPointerValid(aState); 2657 2658 AutoCaller autoCaller(this); 2659 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2660 2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2662 2663 *aState = mUserData->s.enmFaultToleranceState; 2664 return S_OK; 2665 } 2666 2667 STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState) 2668 { 2669 AutoCaller autoCaller(this); 2670 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2671 2672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2673 2674 /* @todo deal with running state change. */ 2675 HRESULT rc = checkStateDependency(MutableStateDep); 2676 if (FAILED(rc)) return rc; 2677 2678 setModified(IsModified_MachineData); 2679 mUserData.backup(); 2680 mUserData->s.enmFaultToleranceState = aState; 2681 return S_OK; 2682 } 2683 2684 STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress) 2685 { 2686 CheckComArgOutPointerValid(aAddress); 2687 2688 AutoCaller autoCaller(this); 2689 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2690 2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2692 2693 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress); 2694 return S_OK; 2695 } 2696 2697 STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress) 2698 { 2699 AutoCaller autoCaller(this); 2700 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2701 2702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2703 2704 /* @todo deal with running state change. */ 2705 HRESULT rc = checkStateDependency(MutableStateDep); 2706 if (FAILED(rc)) return rc; 2707 2708 setModified(IsModified_MachineData); 2709 mUserData.backup(); 2710 mUserData->s.strFaultToleranceAddress = aAddress; 2711 return S_OK; 2712 } 2713 2714 STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort) 2715 { 2716 CheckComArgOutPointerValid(aPort); 2717 2718 AutoCaller autoCaller(this); 2719 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2720 2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2722 2723 *aPort = mUserData->s.uFaultTolerancePort; 2724 return S_OK; 2725 } 2726 2727 STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort) 2728 { 2729 AutoCaller autoCaller(this); 2730 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2731 2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2733 2734 /* @todo deal with running state change. */ 2735 HRESULT rc = checkStateDependency(MutableStateDep); 2736 if (FAILED(rc)) return rc; 2737 2738 setModified(IsModified_MachineData); 2739 mUserData.backup(); 2740 mUserData->s.uFaultTolerancePort = aPort; 2741 return S_OK; 2742 } 2743 2744 STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword) 2745 { 2746 CheckComArgOutPointerValid(aPassword); 2747 2748 AutoCaller autoCaller(this); 2749 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2750 2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2752 2753 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword); 2754 2755 return S_OK; 2756 } 2757 2758 STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword) 2759 { 2760 AutoCaller autoCaller(this); 2761 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2762 2763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2764 2765 /* @todo deal with running state change. */ 2766 HRESULT rc = checkStateDependency(MutableStateDep); 2767 if (FAILED(rc)) return rc; 2768 2769 setModified(IsModified_MachineData); 2770 mUserData.backup(); 2771 mUserData->s.strFaultTolerancePassword = aPassword; 2772 2773 return S_OK; 2774 } 2775 2776 STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval) 2777 { 2778 CheckComArgOutPointerValid(aInterval); 2779 2780 AutoCaller autoCaller(this); 2781 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2782 2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2784 2785 *aInterval = mUserData->s.uFaultToleranceInterval; 2786 return S_OK; 2787 } 2788 2789 STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval) 2790 { 2791 AutoCaller autoCaller(this); 2792 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2793 2794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2795 2796 /* @todo deal with running state change. */ 2797 HRESULT rc = checkStateDependency(MutableStateDep); 2798 if (FAILED(rc)) return rc; 2799 2800 setModified(IsModified_MachineData); 2801 mUserData.backup(); 2802 mUserData->s.uFaultToleranceInterval = aInterval; 2803 return S_OK; 2804 } 2805 2806 STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled) 2807 { 2808 CheckComArgOutPointerValid(aEnabled); 2809 2810 AutoCaller autoCaller(this); 2811 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2812 2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2814 2815 *aEnabled = mUserData->s.fRTCUseUTC; 2816 2817 return S_OK; 2818 } 2819 2820 STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled) 2821 { 2822 AutoCaller autoCaller(this); 2823 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2824 2825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2826 2827 /* Only allow it to be set to true when PoweredOff or Aborted. 2828 (Clearing it is always permitted.) */ 2829 if ( aEnabled 2830 && mData->mRegistered 2831 && ( !isSessionMachine() 2832 || ( mData->mMachineState != MachineState_PoweredOff 2833 && mData->mMachineState != MachineState_Teleported 2834 && mData->mMachineState != MachineState_Aborted 2835 ) 2836 ) 2837 ) 2838 return setError(VBOX_E_INVALID_VM_STATE, 2839 tr("The machine is not powered off (state is %s)"), 2840 Global::stringifyMachineState(mData->mMachineState)); 2841 2842 setModified(IsModified_MachineData); 2843 mUserData.backup(); 2844 mUserData->s.fRTCUseUTC = !!aEnabled; 2845 2846 return S_OK; 2847 } 2848 2849 STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled) 2850 { 2851 CheckComArgOutPointerValid(aEnabled); 2852 2853 AutoCaller autoCaller(this); 2854 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2855 2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2857 2858 *aEnabled = mHWData->mIoCacheEnabled; 2859 2860 return S_OK; 2861 } 2862 2863 STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled) 2864 { 2865 AutoCaller autoCaller(this); 2866 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2867 2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2869 2870 HRESULT rc = checkStateDependency(MutableStateDep); 2871 if (FAILED(rc)) return rc; 2872 2873 setModified(IsModified_MachineData); 2874 mHWData.backup(); 2875 mHWData->mIoCacheEnabled = aEnabled; 2876 2877 return S_OK; 2878 } 2879 2880 STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize) 2881 { 2882 CheckComArgOutPointerValid(aIoCacheSize); 2883 2884 AutoCaller autoCaller(this); 2885 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2886 2887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2888 2889 *aIoCacheSize = mHWData->mIoCacheSize; 2890 2891 return S_OK; 2892 } 2893 2894 STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize) 2895 { 2896 AutoCaller autoCaller(this); 2897 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2898 2899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2900 2901 HRESULT rc = checkStateDependency(MutableStateDep); 2902 if (FAILED(rc)) return rc; 2903 2904 setModified(IsModified_MachineData); 2905 mHWData.backup(); 2906 mHWData->mIoCacheSize = aIoCacheSize; 2907 2908 return S_OK; 2909 } 2910 2911 2912 /** 2913 * @note Locks objects! 2914 */ 2915 STDMETHODIMP Machine::LockMachine(ISession *aSession, 2916 LockType_T lockType) 2917 { 2918 CheckComArgNotNull(aSession); 2919 2920 AutoCaller autoCaller(this); 2921 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2922 2923 /* check the session state */ 2924 SessionState_T state; 2925 HRESULT rc = aSession->COMGETTER(State)(&state); 2926 if (FAILED(rc)) return rc; 2927 2928 if (state != SessionState_Unlocked) 2929 return setError(VBOX_E_INVALID_OBJECT_STATE, 2930 tr("The given session is busy")); 2931 2932 // get the client's IInternalSessionControl interface 2933 ComPtr<IInternalSessionControl> pSessionControl = aSession; 2934 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"), 2935 E_INVALIDARG); 2936 2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2938 2939 if (!mData->mRegistered) 2940 return setError(E_UNEXPECTED, 2941 tr("The machine '%s' is not registered"), 2942 mUserData->s.strName.c_str()); 2943 2944 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 2945 2946 SessionState_T oldState = mData->mSession.mState; 2947 /* Hack: in case the session is closing and there is a progress object 2948 * which allows waiting for the session to be closed, take the opportunity 2949 * and do a limited wait (max. 1 second). This helps a lot when the system 2950 * is busy and thus session closing can take a little while. */ 2951 if ( mData->mSession.mState == SessionState_Unlocking 2952 && mData->mSession.mProgress) 2953 { 2954 alock.release(); 2955 mData->mSession.mProgress->WaitForCompletion(1000); 2956 alock.acquire(); 2957 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 2958 } 2959 2960 // try again now 2961 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists) 2962 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock: 2963 ) 2964 { 2965 // OK, share the session... we are now dealing with three processes: 2966 // 1) VBoxSVC (where this code runs); 2967 // 2) process C: the caller's client process (who wants a shared session); 2968 // 3) process W: the process which already holds the write lock on the machine (write-locking session) 2969 2970 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL) 2971 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl; 2972 ComAssertRet(!pSessionW.isNull(), E_FAIL); 2973 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine; 2974 AssertReturn(!pSessionMachine.isNull(), E_FAIL); 2975 2976 /* 2977 * Leave the lock before calling the client process. It's safe here 2978 * since the only thing to do after we get the lock again is to add 2979 * the remote control to the list (which doesn't directly influence 2980 * anything). 2981 */ 2982 alock.leave(); 2983 2984 // get the console of the session holding the write lock (this is a remote call) 2985 ComPtr<IConsole> pConsoleW; 2986 LogFlowThisFunc(("Calling GetRemoteConsole()...\n")); 2987 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam()); 2988 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc)); 2989 if (FAILED(rc)) 2990 // the failure may occur w/o any error info (from RPC), so provide one 2991 return setError(VBOX_E_VM_ERROR, 2992 tr("Failed to get a console object from the direct session (%Rrc)"), rc); 2993 2994 ComAssertRet(!pConsoleW.isNull(), E_FAIL); 2995 2996 // share the session machine and W's console with the caller's session 2997 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 2998 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW); 2999 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 3000 3001 if (FAILED(rc)) 3002 // the failure may occur w/o any error info (from RPC), so provide one 3003 return setError(VBOX_E_VM_ERROR, 3004 tr("Failed to assign the machine to the session (%Rrc)"), rc); 3005 alock.enter(); 3006 3007 // need to revalidate the state after entering the lock again 3008 if (mData->mSession.mState != SessionState_Locked) 3009 { 3010 pSessionControl->Uninitialize(); 3011 return setError(VBOX_E_INVALID_SESSION_STATE, 3012 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"), 3013 mUserData->s.strName.c_str()); 3014 } 3015 3016 // add the caller's session to the list 3017 mData->mSession.mRemoteControls.push_back(pSessionControl); 3018 } 3019 else if ( mData->mSession.mState == SessionState_Locked 3020 || mData->mSession.mState == SessionState_Unlocking 3021 ) 3022 { 3023 // sharing not permitted, or machine still unlocking: 3024 return setError(VBOX_E_INVALID_OBJECT_STATE, 3025 tr("The machine '%s' is already locked for a session (or being unlocked)"), 3026 mUserData->s.strName.c_str()); 3027 } 3028 else 3029 { 3030 // machine is not locked: then write-lock the machine (create the session machine) 3031 3032 // must not be busy 3033 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 3034 3035 // get the caller's session PID 3036 RTPROCESS pid = NIL_RTPROCESS; 3037 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS)); 3038 pSessionControl->GetPID((ULONG*)&pid); 3039 Assert(pid != NIL_RTPROCESS); 3040 3041 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning); 3042 3043 if (fLaunchingVMProcess) 3044 { 3045 // this machine is awaiting for a spawning session to be opened: 3046 // then the calling process must be the one that got started by 3047 // LaunchVMProcess() 3048 3049 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid)); 3050 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid)); 3051 3052 if (mData->mSession.mPid != pid) 3053 return setError(E_ACCESSDENIED, 3054 tr("An unexpected process (PID=0x%08X) has tried to lock the " 3055 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"), 3056 pid, mUserData->s.strName.c_str(), mData->mSession.mPid); 3057 } 3058 3059 // create the mutable SessionMachine from the current machine 3060 ComObjPtr<SessionMachine> sessionMachine; 3061 sessionMachine.createObject(); 3062 rc = sessionMachine->init(this); 3063 AssertComRC(rc); 3064 3065 /* NOTE: doing return from this function after this point but 3066 * before the end is forbidden since it may call SessionMachine::uninit() 3067 * (through the ComObjPtr's destructor) which requests the VirtualBox write 3068 * lock while still holding the Machine lock in alock so that a deadlock 3069 * is possible due to the wrong lock order. */ 3070 3071 if (SUCCEEDED(rc)) 3072 { 3073 /* 3074 * Set the session state to Spawning to protect against subsequent 3075 * attempts to open a session and to unregister the machine after 3076 * we leave the lock. 3077 */ 3078 SessionState_T origState = mData->mSession.mState; 3079 mData->mSession.mState = SessionState_Spawning; 3080 3081 /* 3082 * Leave the lock before calling the client process -- it will call 3083 * Machine/SessionMachine methods. Leaving the lock here is quite safe 3084 * because the state is Spawning, so that LaunchVMProcess() and 3085 * LockMachine() calls will fail. This method, called before we 3086 * enter the lock again, will fail because of the wrong PID. 3087 * 3088 * Note that mData->mSession.mRemoteControls accessed outside 3089 * the lock may not be modified when state is Spawning, so it's safe. 3090 */ 3091 alock.leave(); 3092 3093 LogFlowThisFunc(("Calling AssignMachine()...\n")); 3094 rc = pSessionControl->AssignMachine(sessionMachine); 3095 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc)); 3096 3097 /* The failure may occur w/o any error info (from RPC), so provide one */ 3098 if (FAILED(rc)) 3099 setError(VBOX_E_VM_ERROR, 3100 tr("Failed to assign the machine to the session (%Rrc)"), rc); 3101 3102 if ( SUCCEEDED(rc) 3103 && fLaunchingVMProcess 3104 ) 3105 { 3106 /* complete the remote session initialization */ 3107 3108 /* get the console from the direct session */ 3109 ComPtr<IConsole> console; 3110 rc = pSessionControl->GetRemoteConsole(console.asOutParam()); 3111 ComAssertComRC(rc); 3112 3113 if (SUCCEEDED(rc) && !console) 3114 { 3115 ComAssert(!!console); 3116 rc = E_FAIL; 3117 } 3118 3119 /* assign machine & console to the remote session */ 3120 if (SUCCEEDED(rc)) 3121 { 3122 /* 3123 * after LaunchVMProcess(), the first and the only 3124 * entry in remoteControls is that remote session 3125 */ 3126 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 3127 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console); 3128 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 3129 3130 /* The failure may occur w/o any error info (from RPC), so provide one */ 3131 if (FAILED(rc)) 3132 setError(VBOX_E_VM_ERROR, 3133 tr("Failed to assign the machine to the remote session (%Rrc)"), rc); 3134 } 3135 3136 if (FAILED(rc)) 3137 pSessionControl->Uninitialize(); 3138 } 3139 3140 /* enter the lock again */ 3141 alock.enter(); 3142 3143 /* Restore the session state */ 3144 mData->mSession.mState = origState; 3145 } 3146 3147 // finalize spawning anyway (this is why we don't return on errors above) 3148 if (fLaunchingVMProcess) 3149 { 3150 /* Note that the progress object is finalized later */ 3151 /** @todo Consider checking mData->mSession.mProgress for cancellation 3152 * around here. */ 3153 3154 /* We don't reset mSession.mPid here because it is necessary for 3155 * SessionMachine::uninit() to reap the child process later. */ 3156 3157 if (FAILED(rc)) 3158 { 3159 /* Close the remote session, remove the remote control from the list 3160 * and reset session state to Closed (@note keep the code in sync 3161 * with the relevant part in openSession()). */ 3162 3163 Assert(mData->mSession.mRemoteControls.size() == 1); 3164 if (mData->mSession.mRemoteControls.size() == 1) 3165 { 3166 ErrorInfoKeeper eik; 3167 mData->mSession.mRemoteControls.front()->Uninitialize(); 3168 } 3169 3170 mData->mSession.mRemoteControls.clear(); 3171 mData->mSession.mState = SessionState_Unlocked; 3172 } 3173 } 3174 else 3175 { 3176 /* memorize PID of the directly opened session */ 3177 if (SUCCEEDED(rc)) 3178 mData->mSession.mPid = pid; 3179 } 3180 3181 if (SUCCEEDED(rc)) 3182 { 3183 /* memorize the direct session control and cache IUnknown for it */ 3184 mData->mSession.mDirectControl = pSessionControl; 3185 mData->mSession.mState = SessionState_Locked; 3186 /* associate the SessionMachine with this Machine */ 3187 mData->mSession.mMachine = sessionMachine; 3188 3189 /* request an IUnknown pointer early from the remote party for later 3190 * identity checks (it will be internally cached within mDirectControl 3191 * at least on XPCOM) */ 3192 ComPtr<IUnknown> unk = mData->mSession.mDirectControl; 3193 NOREF(unk); 3194 } 3195 3196 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which 3197 * would break the lock order */ 3198 alock.leave(); 3199 3200 /* uninitialize the created session machine on failure */ 3201 if (FAILED(rc)) 3202 sessionMachine->uninit(); 3203 3204 } 3205 3206 if (SUCCEEDED(rc)) 3207 { 3208 /* 3209 * tell the client watcher thread to update the set of 3210 * machines that have open sessions 3211 */ 3212 mParent->updateClientWatcher(); 3213 3214 if (oldState != SessionState_Locked) 3215 /* fire an event */ 3216 mParent->onSessionStateChange(getId(), SessionState_Locked); 3217 } 3218 3219 return rc; 3220 } 3221 3222 /** 3223 * @note Locks objects! 3224 */ 3225 STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, 3226 IN_BSTR aType, 3227 IN_BSTR aEnvironment, 3228 IProgress **aProgress) 3229 { 3230 CheckComArgStrNotEmptyOrNull(aType); 3231 Utf8Str strType(aType); 3232 Utf8Str strEnvironment(aEnvironment); 3233 /* "emergencystop" doesn't need the session, so skip the checks/interface 3234 * retrieval. This code doesn't quite fit in here, but introducing a 3235 * special API method would be even more effort, and would require explicit 3236 * support by every API client. It's better to hide the feature a bit. */ 3237 if (strType != "emergencystop") 3238 CheckComArgNotNull(aSession); 3239 CheckComArgOutPointerValid(aProgress); 3240 3241 AutoCaller autoCaller(this); 3242 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3243 3244 ComPtr<IInternalSessionControl> control; 3245 HRESULT rc = S_OK; 3246 3247 if (strType != "emergencystop") 3248 { 3249 /* check the session state */ 3250 SessionState_T state; 3251 rc = aSession->COMGETTER(State)(&state); 3252 if (FAILED(rc)) 3253 return rc; 3254 3255 if (state != SessionState_Unlocked) 3256 return setError(VBOX_E_INVALID_OBJECT_STATE, 3257 tr("The given session is busy")); 3258 3259 /* get the IInternalSessionControl interface */ 3260 control = aSession; 3261 ComAssertMsgRet(!control.isNull(), 3262 ("No IInternalSessionControl interface"), 3263 E_INVALIDARG); 3264 } 3265 3266 /* get the teleporter enable state for the progress object init. */ 3267 BOOL fTeleporterEnabled; 3268 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled); 3269 if (FAILED(rc)) 3270 return rc; 3271 3272 /* create a progress object */ 3273 if (strType != "emergencystop") 3274 { 3275 ComObjPtr<ProgressProxy> progress; 3276 progress.createObject(); 3277 rc = progress->init(mParent, 3278 static_cast<IMachine*>(this), 3279 Bstr(tr("Starting VM")).raw(), 3280 TRUE /* aCancelable */, 3281 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */, 3282 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(), 3283 2 /* uFirstOperationWeight */, 3284 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */); 3285 3286 if (SUCCEEDED(rc)) 3287 { 3288 rc = launchVMProcess(control, strType, strEnvironment, progress); 3289 if (SUCCEEDED(rc)) 3290 { 3291 progress.queryInterfaceTo(aProgress); 3292 3293 /* signal the client watcher thread */ 3294 mParent->updateClientWatcher(); 3295 3296 /* fire an event */ 3297 mParent->onSessionStateChange(getId(), SessionState_Spawning); 3298 } 3299 } 3300 } 3301 else 3302 { 3303 /* no progress object - either instant success or failure */ 3304 *aProgress = NULL; 3305 3306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3307 3308 if (mData->mSession.mState != SessionState_Locked) 3309 return setError(VBOX_E_INVALID_OBJECT_STATE, 3310 tr("The machine '%s' is not locked by a session"), 3311 mUserData->s.strName.c_str()); 3312 3313 /* must have a VM process associated - do not kill normal API clients 3314 * with an open session */ 3315 if (!Global::IsOnline(mData->mMachineState)) 3316 return setError(VBOX_E_INVALID_OBJECT_STATE, 3317 tr("The machine '%s' does not have a VM process"), 3318 mUserData->s.strName.c_str()); 3319 3320 /* forcibly terminate the VM process */ 3321 if (mData->mSession.mPid != NIL_RTPROCESS) 3322 RTProcTerminate(mData->mSession.mPid); 3323 3324 /* signal the client watcher thread, as most likely the client has 3325 * been terminated */ 3326 mParent->updateClientWatcher(); 3327 } 3328 3329 return rc; 3330 } 3331 3332 STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice) 3333 { 3334 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 3335 return setError(E_INVALIDARG, 3336 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 3337 aPosition, SchemaDefs::MaxBootPosition); 3338 3339 if (aDevice == DeviceType_USB) 3340 return setError(E_NOTIMPL, 3341 tr("Booting from USB device is currently not supported")); 3342 3343 AutoCaller autoCaller(this); 3344 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3345 3346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3347 3348 HRESULT rc = checkStateDependency(MutableStateDep); 3349 if (FAILED(rc)) return rc; 3350 3351 setModified(IsModified_MachineData); 3352 mHWData.backup(); 3353 mHWData->mBootOrder[aPosition - 1] = aDevice; 3354 3355 return S_OK; 3356 } 3357 3358 STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice) 3359 { 3360 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 3361 return setError(E_INVALIDARG, 3362 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 3363 aPosition, SchemaDefs::MaxBootPosition); 3364 3365 AutoCaller autoCaller(this); 3366 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3367 3368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3369 3370 *aDevice = mHWData->mBootOrder[aPosition - 1]; 3371 3372 return S_OK; 3373 } 3374 3375 STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, 3376 LONG aControllerPort, 3377 LONG aDevice, 3378 DeviceType_T aType, 3379 IMedium *aMedium) 3380 { 3381 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n", 3382 aControllerName, aControllerPort, aDevice, aType, aMedium)); 3383 3384 CheckComArgStrNotEmptyOrNull(aControllerName); 3385 3386 AutoCaller autoCaller(this); 3387 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3388 3389 // request the host lock first, since might be calling Host methods for getting host drives; 3390 // next, protect the media tree all the while we're in here, as well as our member variables 3391 AutoMultiWriteLock2 alock(mParent->host()->lockHandle(), 3392 this->lockHandle() COMMA_LOCKVAL_SRC_POS); 3393 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3394 3395 HRESULT rc = checkStateDependency(MutableStateDep); 3396 if (FAILED(rc)) return rc; 3397 3398 GuidList llRegistriesThatNeedSaving; 3399 3400 /// @todo NEWMEDIA implicit machine registration 3401 if (!mData->mRegistered) 3402 return setError(VBOX_E_INVALID_OBJECT_STATE, 3403 tr("Cannot attach storage devices to an unregistered machine")); 3404 3405 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3406 3407 /* Check for an existing controller. */ 3408 ComObjPtr<StorageController> ctl; 3409 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */); 3410 if (FAILED(rc)) return rc; 3411 3412 StorageControllerType_T ctrlType; 3413 rc = ctl->COMGETTER(ControllerType)(&ctrlType); 3414 if (FAILED(rc)) 3415 return setError(E_FAIL, 3416 tr("Could not get type of controller '%ls'"), 3417 aControllerName); 3418 3419 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */ 3420 bool fHotplug = false; 3421 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3422 fHotplug = true; 3423 3424 if (fHotplug && !isControllerHotplugCapable(ctrlType)) 3425 return setError(VBOX_E_INVALID_VM_STATE, 3426 tr("Invalid machine state: %s"), 3427 Global::stringifyMachineState(mData->mMachineState)); 3428 3429 // check that the port and device are not out of range 3430 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice); 3431 if (FAILED(rc)) return rc; 3432 3433 /* check if the device slot is already busy */ 3434 MediumAttachment *pAttachTemp; 3435 if ((pAttachTemp = findAttachment(mMediaData->mAttachments, 3436 aControllerName, 3437 aControllerPort, 3438 aDevice))) 3439 { 3440 Medium *pMedium = pAttachTemp->getMedium(); 3441 if (pMedium) 3442 { 3443 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 3444 return setError(VBOX_E_OBJECT_IN_USE, 3445 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 3446 pMedium->getLocationFull().c_str(), 3447 aControllerPort, 3448 aDevice, 3449 aControllerName); 3450 } 3451 else 3452 return setError(VBOX_E_OBJECT_IN_USE, 3453 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 3454 aControllerPort, aDevice, aControllerName); 3455 } 3456 3457 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium); 3458 if (aMedium && medium.isNull()) 3459 return setError(E_INVALIDARG, "The given medium pointer is invalid"); 3460 3461 AutoCaller mediumCaller(medium); 3462 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3463 3464 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS); 3465 3466 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium)) 3467 && !medium.isNull() 3468 ) 3469 return setError(VBOX_E_OBJECT_IN_USE, 3470 tr("Medium '%s' is already attached to this virtual machine"), 3471 medium->getLocationFull().c_str()); 3472 3473 if (!medium.isNull()) 3474 { 3475 MediumType_T mtype = medium->getType(); 3476 // MediumType_Readonly is also new, but only applies to DVDs and floppies. 3477 // For DVDs it's not written to the config file, so needs no global config 3478 // version bump. For floppies it's a new attribute "type", which is ignored 3479 // by older VirtualBox version, so needs no global config version bump either. 3480 // For hard disks this type is not accepted. 3481 if (mtype == MediumType_MultiAttach) 3482 { 3483 // This type is new with VirtualBox 4.0 and therefore requires settings 3484 // version 1.11 in the settings backend. Unfortunately it is not enough to do 3485 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for 3486 // two reasons: The medium type is a property of the media registry tree, which 3487 // can reside in the global config file (for pre-4.0 media); we would therefore 3488 // possibly need to bump the global config version. We don't want to do that though 3489 // because that might make downgrading to pre-4.0 impossible. 3490 // As a result, we can only use these two new types if the medium is NOT in the 3491 // global registry: 3492 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId(); 3493 if ( medium->isInRegistry(uuidGlobalRegistry) 3494 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry() 3495 ) 3496 return setError(VBOX_E_INVALID_OBJECT_STATE, 3497 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached " 3498 "to machines that were created with VirtualBox 4.0 or later"), 3499 medium->getLocationFull().c_str()); 3500 } 3501 } 3502 3503 bool fIndirect = false; 3504 if (!medium.isNull()) 3505 fIndirect = medium->isReadOnly(); 3506 bool associate = true; 3507 3508 do 3509 { 3510 if ( aType == DeviceType_HardDisk 3511 && mMediaData.isBackedUp()) 3512 { 3513 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 3514 3515 /* check if the medium was attached to the VM before we started 3516 * changing attachments in which case the attachment just needs to 3517 * be restored */ 3518 if ((pAttachTemp = findAttachment(oldAtts, medium))) 3519 { 3520 AssertReturn(!fIndirect, E_FAIL); 3521 3522 /* see if it's the same bus/channel/device */ 3523 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice)) 3524 { 3525 /* the simplest case: restore the whole attachment 3526 * and return, nothing else to do */ 3527 mMediaData->mAttachments.push_back(pAttachTemp); 3528 return S_OK; 3529 } 3530 3531 /* bus/channel/device differ; we need a new attachment object, 3532 * but don't try to associate it again */ 3533 associate = false; 3534 break; 3535 } 3536 } 3537 3538 /* go further only if the attachment is to be indirect */ 3539 if (!fIndirect) 3540 break; 3541 3542 /* perform the so called smart attachment logic for indirect 3543 * attachments. Note that smart attachment is only applicable to base 3544 * hard disks. */ 3545 3546 if (medium->getParent().isNull()) 3547 { 3548 /* first, investigate the backup copy of the current hard disk 3549 * attachments to make it possible to re-attach existing diffs to 3550 * another device slot w/o losing their contents */ 3551 if (mMediaData.isBackedUp()) 3552 { 3553 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 3554 3555 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end(); 3556 uint32_t foundLevel = 0; 3557 3558 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 3559 it != oldAtts.end(); 3560 ++it) 3561 { 3562 uint32_t level = 0; 3563 MediumAttachment *pAttach = *it; 3564 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 3565 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 3566 if (pMedium.isNull()) 3567 continue; 3568 3569 if (pMedium->getBase(&level) == medium) 3570 { 3571 /* skip the hard disk if its currently attached (we 3572 * cannot attach the same hard disk twice) */ 3573 if (findAttachment(mMediaData->mAttachments, 3574 pMedium)) 3575 continue; 3576 3577 /* matched device, channel and bus (i.e. attached to the 3578 * same place) will win and immediately stop the search; 3579 * otherwise the attachment that has the youngest 3580 * descendant of medium will be used 3581 */ 3582 if (pAttach->matches(aControllerName, aControllerPort, aDevice)) 3583 { 3584 /* the simplest case: restore the whole attachment 3585 * and return, nothing else to do */ 3586 mMediaData->mAttachments.push_back(*it); 3587 return S_OK; 3588 } 3589 else if ( foundIt == oldAtts.end() 3590 || level > foundLevel /* prefer younger */ 3591 ) 3592 { 3593 foundIt = it; 3594 foundLevel = level; 3595 } 3596 } 3597 } 3598 3599 if (foundIt != oldAtts.end()) 3600 { 3601 /* use the previously attached hard disk */ 3602 medium = (*foundIt)->getMedium(); 3603 mediumCaller.attach(medium); 3604 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3605 mediumLock.attach(medium); 3606 /* not implicit, doesn't require association with this VM */ 3607 fIndirect = false; 3608 associate = false; 3609 /* go right to the MediumAttachment creation */ 3610 break; 3611 } 3612 } 3613 3614 /* must give up the medium lock and medium tree lock as below we 3615 * go over snapshots, which needs a lock with higher lock order. */ 3616 mediumLock.release(); 3617 treeLock.release(); 3618 3619 /* then, search through snapshots for the best diff in the given 3620 * hard disk's chain to base the new diff on */ 3621 3622 ComObjPtr<Medium> base; 3623 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot; 3624 while (snap) 3625 { 3626 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS); 3627 3628 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments; 3629 3630 MediumAttachment *pAttachFound = NULL; 3631 uint32_t foundLevel = 0; 3632 3633 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); 3634 it != snapAtts.end(); 3635 ++it) 3636 { 3637 MediumAttachment *pAttach = *it; 3638 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 3639 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 3640 if (pMedium.isNull()) 3641 continue; 3642 3643 uint32_t level = 0; 3644 if (pMedium->getBase(&level) == medium) 3645 { 3646 /* matched device, channel and bus (i.e. attached to the 3647 * same place) will win and immediately stop the search; 3648 * otherwise the attachment that has the youngest 3649 * descendant of medium will be used 3650 */ 3651 if ( pAttach->getDevice() == aDevice 3652 && pAttach->getPort() == aControllerPort 3653 && pAttach->getControllerName() == aControllerName 3654 ) 3655 { 3656 pAttachFound = pAttach; 3657 break; 3658 } 3659 else if ( !pAttachFound 3660 || level > foundLevel /* prefer younger */ 3661 ) 3662 { 3663 pAttachFound = pAttach; 3664 foundLevel = level; 3665 } 3666 } 3667 } 3668 3669 if (pAttachFound) 3670 { 3671 base = pAttachFound->getMedium(); 3672 break; 3673 } 3674 3675 snap = snap->getParent(); 3676 } 3677 3678 /* re-lock medium tree and the medium, as we need it below */ 3679 treeLock.acquire(); 3680 mediumLock.acquire(); 3681 3682 /* found a suitable diff, use it as a base */ 3683 if (!base.isNull()) 3684 { 3685 medium = base; 3686 mediumCaller.attach(medium); 3687 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3688 mediumLock.attach(medium); 3689 } 3690 } 3691 3692 Utf8Str strFullSnapshotFolder; 3693 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 3694 3695 ComObjPtr<Medium> diff; 3696 diff.createObject(); 3697 // store this diff in the same registry as the parent 3698 Guid uuidRegistryParent; 3699 if (!medium->getFirstRegistryMachineId(uuidRegistryParent)) 3700 { 3701 // parent image has no registry: this can happen if we're attaching a new immutable 3702 // image that has not yet been attached (medium then points to the base and we're 3703 // creating the diff image for the immutable, and the parent is not yet registered); 3704 // put the parent in the machine registry then 3705 addMediumToRegistry(medium, llRegistriesThatNeedSaving, &uuidRegistryParent); 3706 } 3707 rc = diff->init(mParent, 3708 medium->getPreferredDiffFormat(), 3709 strFullSnapshotFolder.append(RTPATH_SLASH_STR), 3710 uuidRegistryParent, 3711 &llRegistriesThatNeedSaving); 3712 if (FAILED(rc)) return rc; 3713 3714 /* Apply the normal locking logic to the entire chain. */ 3715 MediumLockList *pMediumLockList(new MediumLockList()); 3716 rc = diff->createMediumLockList(true /* fFailIfInaccessible */, 3717 true /* fMediumLockWrite */, 3718 medium, 3719 *pMediumLockList); 3720 if (SUCCEEDED(rc)) 3721 { 3722 rc = pMediumLockList->Lock(); 3723 if (FAILED(rc)) 3724 setError(rc, 3725 tr("Could not lock medium when creating diff '%s'"), 3726 diff->getLocationFull().c_str()); 3727 else 3728 { 3729 /* will leave the lock before the potentially lengthy operation, so 3730 * protect with the special state */ 3731 MachineState_T oldState = mData->mMachineState; 3732 setMachineState(MachineState_SettingUp); 3733 3734 mediumLock.leave(); 3735 treeLock.leave(); 3736 alock.leave(); 3737 3738 rc = medium->createDiffStorage(diff, 3739 MediumVariant_Standard, 3740 pMediumLockList, 3741 NULL /* aProgress */, 3742 true /* aWait */, 3743 &llRegistriesThatNeedSaving); 3744 3745 alock.enter(); 3746 treeLock.enter(); 3747 mediumLock.enter(); 3748 3749 setMachineState(oldState); 3750 } 3751 } 3752 3753 /* Unlock the media and free the associated memory. */ 3754 delete pMediumLockList; 3755 3756 if (FAILED(rc)) return rc; 3757 3758 /* use the created diff for the actual attachment */ 3759 medium = diff; 3760 mediumCaller.attach(medium); 3761 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3762 mediumLock.attach(medium); 3763 } 3764 while (0); 3765 3766 ComObjPtr<MediumAttachment> attachment; 3767 attachment.createObject(); 3768 rc = attachment->init(this, 3769 medium, 3770 aControllerName, 3771 aControllerPort, 3772 aDevice, 3773 aType, 3774 fIndirect, 3775 Utf8Str::Empty); 3776 if (FAILED(rc)) return rc; 3777 3778 if (associate && !medium.isNull()) 3779 { 3780 // as the last step, associate the medium to the VM 3781 rc = medium->addBackReference(mData->mUuid); 3782 // here we can fail because of Deleting, or being in process of creating a Diff 3783 if (FAILED(rc)) return rc; 3784 3785 addMediumToRegistry(medium, 3786 llRegistriesThatNeedSaving, 3787 NULL /* Guid *puuid */); 3788 } 3789 3790 /* success: finally remember the attachment */ 3791 setModified(IsModified_Storage); 3792 mMediaData.backup(); 3793 mMediaData->mAttachments.push_back(attachment); 3794 3795 mediumLock.release(); 3796 treeLock.leave(); 3797 alock.release(); 3798 3799 if (fHotplug) 3800 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */); 3801 3802 mParent->saveRegistries(llRegistriesThatNeedSaving); 3803 3804 return rc; 3805 } 3806 3807 STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort, 3808 LONG aDevice) 3809 { 3810 CheckComArgStrNotEmptyOrNull(aControllerName); 3811 3812 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n", 3813 aControllerName, aControllerPort, aDevice)); 3814 3815 AutoCaller autoCaller(this); 3816 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3817 3818 GuidList llRegistriesThatNeedSaving; 3819 3820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3821 3822 HRESULT rc = checkStateDependency(MutableStateDep); 3823 if (FAILED(rc)) return rc; 3824 3825 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3826 3827 /* Check for an existing controller. */ 3828 ComObjPtr<StorageController> ctl; 3829 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */); 3830 if (FAILED(rc)) return rc; 3831 3832 StorageControllerType_T ctrlType; 3833 rc = ctl->COMGETTER(ControllerType)(&ctrlType); 3834 if (FAILED(rc)) 3835 return setError(E_FAIL, 3836 tr("Could not get type of controller '%ls'"), 3837 aControllerName); 3838 3839 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */ 3840 bool fHotplug = false; 3841 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3842 fHotplug = true; 3843 3844 if (fHotplug && !isControllerHotplugCapable(ctrlType)) 3845 return setError(VBOX_E_INVALID_VM_STATE, 3846 tr("Invalid machine state: %s"), 3847 Global::stringifyMachineState(mData->mMachineState)); 3848 3849 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 3850 aControllerName, 3851 aControllerPort, 3852 aDevice); 3853 if (!pAttach) 3854 return setError(VBOX_E_OBJECT_NOT_FOUND, 3855 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3856 aDevice, aControllerPort, aControllerName); 3857 3858 /* 3859 * The VM has to detach the device before we delete any implicit diffs. 3860 * If this fails we can roll back without loosing data. 3861 */ 3862 if (fHotplug) 3863 { 3864 alock.leave(); 3865 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */); 3866 alock.enter(); 3867 } 3868 if (FAILED(rc)) return rc; 3869 3870 /* If we are here everything went well and we can delete the implicit now. */ 3871 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &llRegistriesThatNeedSaving); 3872 3873 alock.release(); 3874 3875 if (SUCCEEDED(rc)) 3876 rc = mParent->saveRegistries(llRegistriesThatNeedSaving); 3877 3878 return rc; 3879 } 3880 3881 STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort, 3882 LONG aDevice, BOOL aPassthrough) 3883 { 3884 CheckComArgStrNotEmptyOrNull(aControllerName); 3885 3886 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n", 3887 aControllerName, aControllerPort, aDevice, aPassthrough)); 3888 3889 AutoCaller autoCaller(this); 3890 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3891 3892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3893 3894 HRESULT rc = checkStateDependency(MutableStateDep); 3895 if (FAILED(rc)) return rc; 3896 3897 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3898 3899 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3900 return setError(VBOX_E_INVALID_VM_STATE, 3901 tr("Invalid machine state: %s"), 3902 Global::stringifyMachineState(mData->mMachineState)); 3903 3904 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 3905 aControllerName, 3906 aControllerPort, 3907 aDevice); 3908 if (!pAttach) 3909 return setError(VBOX_E_OBJECT_NOT_FOUND, 3910 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3911 aDevice, aControllerPort, aControllerName); 3912 3913 3914 setModified(IsModified_Storage); 3915 mMediaData.backup(); 3916 3917 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 3918 3919 if (pAttach->getType() != DeviceType_DVD) 3920 return setError(E_INVALIDARG, 3921 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"), 3922 aDevice, aControllerPort, aControllerName); 3923 pAttach->updatePassthrough(!!aPassthrough); 3924 3925 return S_OK; 3926 } 3927 3928 STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort, 3929 LONG aDevice, IBandwidthGroup *aBandwidthGroup) 3930 { 3931 CheckComArgStrNotEmptyOrNull(aControllerName); 3932 3933 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n", 3934 aControllerName, aControllerPort, aDevice)); 3935 3936 AutoCaller autoCaller(this); 3937 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3938 3939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3940 3941 HRESULT rc = checkStateDependency(MutableStateDep); 3942 if (FAILED(rc)) return rc; 3943 3944 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3945 3946 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3947 return setError(VBOX_E_INVALID_VM_STATE, 3948 tr("Invalid machine state: %s"), 3949 Global::stringifyMachineState(mData->mMachineState)); 3950 3951 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 3952 aControllerName, 3953 aControllerPort, 3954 aDevice); 3955 if (!pAttach) 3956 return setError(VBOX_E_OBJECT_NOT_FOUND, 3957 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3958 aDevice, aControllerPort, aControllerName); 3959 3960 3961 setModified(IsModified_Storage); 3962 mMediaData.backup(); 3963 3964 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup); 3965 if (aBandwidthGroup && group.isNull()) 3966 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid"); 3967 3968 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 3969 3970 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup(); 3971 if (strBandwidthGroupOld.isNotEmpty()) 3972 { 3973 /* Get the bandwidth group object and release it - this must not fail. */ 3974 ComObjPtr<BandwidthGroup> pBandwidthGroupOld; 3975 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false); 3976 Assert(SUCCEEDED(rc)); 3977 3978 pBandwidthGroupOld->release(); 3979 pAttach->updateBandwidthGroup(Utf8Str::Empty); 3980 } 3981 3982 if (!group.isNull()) 3983 { 3984 group->reference(); 3985 pAttach->updateBandwidthGroup(group->getName()); 3986 } 3987 3988 return S_OK; 3989 } 3990 3991 3992 STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName, 3993 LONG aControllerPort, 3994 LONG aDevice, 3995 IMedium *aMedium, 3996 BOOL aForce) 3997 { 3998 int rc = S_OK; 3999 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n", 4000 aControllerName, aControllerPort, aDevice, aForce)); 4001 4002 CheckComArgStrNotEmptyOrNull(aControllerName); 4003 4004 AutoCaller autoCaller(this); 4005 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4006 4007 // request the host lock first, since might be calling Host methods for getting host drives; 4008 // next, protect the media tree all the while we're in here, as well as our member variables 4009 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(), 4010 this->lockHandle(), 4011 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4012 4013 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 4014 aControllerName, 4015 aControllerPort, 4016 aDevice); 4017 if (pAttach.isNull()) 4018 return setError(VBOX_E_OBJECT_NOT_FOUND, 4019 tr("No drive attached to device slot %d on port %d of controller '%ls'"), 4020 aDevice, aControllerPort, aControllerName); 4021 4022 /* Remember previously mounted medium. The medium before taking the 4023 * backup is not necessarily the same thing. */ 4024 ComObjPtr<Medium> oldmedium; 4025 oldmedium = pAttach->getMedium(); 4026 4027 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium); 4028 if (aMedium && pMedium.isNull()) 4029 return setError(E_INVALIDARG, "The given medium pointer is invalid"); 4030 4031 AutoCaller mediumCaller(pMedium); 4032 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 4033 4034 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 4035 if (pMedium) 4036 { 4037 DeviceType_T mediumType = pAttach->getType(); 4038 switch (mediumType) 4039 { 4040 case DeviceType_DVD: 4041 case DeviceType_Floppy: 4042 break; 4043 4044 default: 4045 return setError(VBOX_E_INVALID_OBJECT_STATE, 4046 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"), 4047 aControllerPort, 4048 aDevice, 4049 aControllerName); 4050 } 4051 } 4052 4053 setModified(IsModified_Storage); 4054 mMediaData.backup(); 4055 4056 GuidList llRegistriesThatNeedSaving; 4057 4058 { 4059 // The backup operation makes the pAttach reference point to the 4060 // old settings. Re-get the correct reference. 4061 pAttach = findAttachment(mMediaData->mAttachments, 4062 aControllerName, 4063 aControllerPort, 4064 aDevice); 4065 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4066 if (!oldmedium.isNull()) 4067 oldmedium->removeBackReference(mData->mUuid); 4068 if (!pMedium.isNull()) 4069 { 4070 pMedium->addBackReference(mData->mUuid); 4071 4072 addMediumToRegistry(pMedium, llRegistriesThatNeedSaving, NULL /* Guid *puuid */ ); 4073 } 4074 4075 pAttach->updateMedium(pMedium); 4076 } 4077 4078 setModified(IsModified_Storage); 4079 4080 mediumLock.release(); 4081 multiLock.release(); 4082 rc = onMediumChange(pAttach, aForce); 4083 multiLock.acquire(); 4084 mediumLock.acquire(); 4085 4086 /* On error roll back this change only. */ 4087 if (FAILED(rc)) 4088 { 4089 if (!pMedium.isNull()) 4090 pMedium->removeBackReference(mData->mUuid); 4091 pAttach = findAttachment(mMediaData->mAttachments, 4092 aControllerName, 4093 aControllerPort, 4094 aDevice); 4095 /* If the attachment is gone in the meantime, bail out. */ 4096 if (pAttach.isNull()) 4097 return rc; 4098 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4099 if (!oldmedium.isNull()) 4100 oldmedium->addBackReference(mData->mUuid); 4101 pAttach->updateMedium(oldmedium); 4102 } 4103 4104 mediumLock.release(); 4105 multiLock.release(); 4106 4107 mParent->saveRegistries(llRegistriesThatNeedSaving); 4108 4109 return rc; 4110 } 4111 4112 STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName, 4113 LONG aControllerPort, 4114 LONG aDevice, 4115 IMedium **aMedium) 4116 { 4117 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n", 4118 aControllerName, aControllerPort, aDevice)); 4119 4120 CheckComArgStrNotEmptyOrNull(aControllerName); 4121 CheckComArgOutPointerValid(aMedium); 4122 4123 AutoCaller autoCaller(this); 4124 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4125 4126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4127 4128 *aMedium = NULL; 4129 4130 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 4131 aControllerName, 4132 aControllerPort, 4133 aDevice); 4134 if (pAttach.isNull()) 4135 return setError(VBOX_E_OBJECT_NOT_FOUND, 4136 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4137 aDevice, aControllerPort, aControllerName); 4138 4139 pAttach->getMedium().queryInterfaceTo(aMedium); 4140 4141 return S_OK; 4142 } 4143 4144 STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port) 4145 { 4146 CheckComArgOutPointerValid(port); 4147 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts)); 4148 4149 AutoCaller autoCaller(this); 4150 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4151 4152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4153 4154 mSerialPorts[slot].queryInterfaceTo(port); 4155 4156 return S_OK; 4157 } 4158 4159 STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port) 4160 { 4161 CheckComArgOutPointerValid(port); 4162 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts)); 4163 4164 AutoCaller autoCaller(this); 4165 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4166 4167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4168 4169 mParallelPorts[slot].queryInterfaceTo(port); 4170 4171 return S_OK; 4172 } 4173 4174 STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter) 4175 { 4176 CheckComArgOutPointerValid(adapter); 4177 CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters)); 4178 4179 AutoCaller autoCaller(this); 4180 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4181 4182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4183 4184 mNetworkAdapters[slot].queryInterfaceTo(adapter); 4185 4186 return S_OK; 4187 } 4188 4189 STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys)) 4190 { 4191 if (ComSafeArrayOutIsNull(aKeys)) 4192 return E_POINTER; 4193 4194 AutoCaller autoCaller(this); 4195 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4196 4197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4198 4199 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size()); 4200 int i = 0; 4201 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin(); 4202 it != mData->pMachineConfigFile->mapExtraDataItems.end(); 4203 ++it, ++i) 4204 { 4205 const Utf8Str &strKey = it->first; 4206 strKey.cloneTo(&saKeys[i]); 4207 } 4208 saKeys.detachTo(ComSafeArrayOutArg(aKeys)); 4209 4210 return S_OK; 4211 } 4212 4213 /** 4214 * @note Locks this object for reading. 4215 */ 4216 STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey, 4217 BSTR *aValue) 4218 { 4219 CheckComArgStrNotEmptyOrNull(aKey); 4220 CheckComArgOutPointerValid(aValue); 4221 4222 AutoCaller autoCaller(this); 4223 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4224 4225 /* start with nothing found */ 4226 Bstr bstrResult(""); 4227 4228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4229 4230 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey)); 4231 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 4232 // found: 4233 bstrResult = it->second; // source is a Utf8Str 4234 4235 /* return the result to caller (may be empty) */ 4236 bstrResult.cloneTo(aValue); 4237 4238 return S_OK; 4239 } 4240 4241 /** 4242 * @note Locks mParent for writing + this object for writing. 4243 */ 4244 STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue) 4245 { 4246 CheckComArgStrNotEmptyOrNull(aKey); 4247 4248 AutoCaller autoCaller(this); 4249 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4250 4251 Utf8Str strKey(aKey); 4252 Utf8Str strValue(aValue); 4253 Utf8Str strOldValue; // empty 4254 4255 // locking note: we only hold the read lock briefly to look up the old value, 4256 // then release it and call the onExtraCanChange callbacks. There is a small 4257 // chance of a race insofar as the callback might be called twice if two callers 4258 // change the same key at the same time, but that's a much better solution 4259 // than the deadlock we had here before. The actual changing of the extradata 4260 // is then performed under the write lock and race-free. 4261 4262 // look up the old value first; if nothing has changed then we need not do anything 4263 { 4264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up 4265 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey); 4266 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 4267 strOldValue = it->second; 4268 } 4269 4270 bool fChanged; 4271 if ((fChanged = (strOldValue != strValue))) 4272 { 4273 // ask for permission from all listeners outside the locks; 4274 // onExtraDataCanChange() only briefly requests the VirtualBox 4275 // lock to copy the list of callbacks to invoke 4276 Bstr error; 4277 Bstr bstrValue(aValue); 4278 4279 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error)) 4280 { 4281 const char *sep = error.isEmpty() ? "" : ": "; 4282 CBSTR err = error.raw(); 4283 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n", 4284 sep, err)); 4285 return setError(E_ACCESSDENIED, 4286 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"), 4287 aKey, 4288 bstrValue.raw(), 4289 sep, 4290 err); 4291 } 4292 4293 // data is changing and change not vetoed: then write it out under the lock 4294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4295 4296 if (isSnapshotMachine()) 4297 { 4298 HRESULT rc = checkStateDependency(MutableStateDep); 4299 if (FAILED(rc)) return rc; 4300 } 4301 4302 if (strValue.isEmpty()) 4303 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey); 4304 else 4305 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue; 4306 // creates a new key if needed 4307 4308 bool fNeedsGlobalSaveSettings = false; 4309 saveSettings(&fNeedsGlobalSaveSettings); 4310 4311 if (fNeedsGlobalSaveSettings) 4312 { 4313 // save the global settings; for that we should hold only the VirtualBox lock 4314 alock.release(); 4315 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS); 4316 mParent->saveSettings(); 4317 } 4318 } 4319 4320 // fire notification outside the lock 4321 if (fChanged) 4322 mParent->onExtraDataChange(mData->mUuid, aKey, aValue); 4323 4324 return S_OK; 4325 } 4326 4327 STDMETHODIMP Machine::SaveSettings() 4328 { 4329 AutoCaller autoCaller(this); 4330 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4331 4332 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS); 4333 4334 /* when there was auto-conversion, we want to save the file even if 4335 * the VM is saved */ 4336 HRESULT rc = checkStateDependency(MutableStateDep); 4337 if (FAILED(rc)) return rc; 4338 4339 /* the settings file path may never be null */ 4340 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL); 4341 4342 /* save all VM data excluding snapshots */ 4343 bool fNeedsGlobalSaveSettings = false; 4344 rc = saveSettings(&fNeedsGlobalSaveSettings); 4345 mlock.release(); 4346 4347 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings) 4348 { 4349 // save the global settings; for that we should hold only the VirtualBox lock 4350 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS); 4351 rc = mParent->saveSettings(); 4352 } 4353 4354 return rc; 4355 } 4356 4357 STDMETHODIMP Machine::DiscardSettings() 4358 { 4359 AutoCaller autoCaller(this); 4360 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4361 4362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4363 4364 HRESULT rc = checkStateDependency(MutableStateDep); 4365 if (FAILED(rc)) return rc; 4366 4367 /* 4368 * during this rollback, the session will be notified if data has 4369 * been actually changed 4370 */ 4371 rollback(true /* aNotify */); 4372 4373 return S_OK; 4374 } 4375 4376 /** @note Locks objects! */ 4377 STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode, 4378 ComSafeArrayOut(IMedium*, aMedia)) 4379 { 4380 // use AutoLimitedCaller because this call is valid on inaccessible machines as well 4381 AutoLimitedCaller autoCaller(this); 4382 AssertComRCReturnRC(autoCaller.rc()); 4383 4384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4385 4386 Guid id(getId()); 4387 4388 if (mData->mSession.mState != SessionState_Unlocked) 4389 return setError(VBOX_E_INVALID_OBJECT_STATE, 4390 tr("Cannot unregister the machine '%s' while it is locked"), 4391 mUserData->s.strName.c_str()); 4392 4393 // wait for state dependents to drop to zero 4394 ensureNoStateDependencies(); 4395 4396 if (!mData->mAccessible) 4397 { 4398 // inaccessible maschines can only be unregistered; uninitialize ourselves 4399 // here because currently there may be no unregistered that are inaccessible 4400 // (this state combination is not supported). Note releasing the caller and 4401 // leaving the lock before calling uninit() 4402 alock.leave(); 4403 autoCaller.release(); 4404 4405 uninit(); 4406 4407 mParent->unregisterMachine(this, id); 4408 // calls VirtualBox::saveSettings() 4409 4410 return S_OK; 4411 } 4412 4413 HRESULT rc = S_OK; 4414 4415 // discard saved state 4416 if (mData->mMachineState == MachineState_Saved) 4417 { 4418 // add the saved state file to the list of files the caller should delete 4419 Assert(!mSSData->strStateFilePath.isEmpty()); 4420 mData->llFilesToDelete.push_back(mSSData->strStateFilePath); 4421 4422 mSSData->strStateFilePath.setNull(); 4423 4424 // unconditionally set the machine state to powered off, we now 4425 // know no session has locked the machine 4426 mData->mMachineState = MachineState_PoweredOff; 4427 } 4428 4429 size_t cSnapshots = 0; 4430 if (mData->mFirstSnapshot) 4431 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1; 4432 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly) 4433 // fail now before we start detaching media 4434 return setError(VBOX_E_INVALID_OBJECT_STATE, 4435 tr("Cannot unregister the machine '%s' because it has %d snapshots"), 4436 mUserData->s.strName.c_str(), cSnapshots); 4437 4438 // This list collects the medium objects from all medium attachments 4439 // which we will detach from the machine and its snapshots, in a specific 4440 // order which allows for closing all media without getting "media in use" 4441 // errors, simply by going through the list from the front to the back: 4442 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots 4443 // and must be closed before the parent media from the snapshots, or closing the parents 4444 // will fail because they still have children); 4445 // 2) media from the youngest snapshots followed by those from the parent snapshots until 4446 // the root ("first") snapshot of the machine. 4447 MediaList llMedia; 4448 4449 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible 4450 && mMediaData->mAttachments.size() 4451 ) 4452 { 4453 // we have media attachments: detach them all and add the Medium objects to our list 4454 if (cleanupMode != CleanupMode_UnregisterOnly) 4455 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia); 4456 else 4457 return setError(VBOX_E_INVALID_OBJECT_STATE, 4458 tr("Cannot unregister the machine '%s' because it has %d media attachments"), 4459 mUserData->s.strName.c_str(), mMediaData->mAttachments.size()); 4460 } 4461 4462 if (cSnapshots) 4463 { 4464 // autoCleanup must be true here, or we would have failed above 4465 4466 // add the media from the medium attachments of the snapshots to llMedia 4467 // as well, after the "main" machine media; Snapshot::uninitRecursively() 4468 // calls Machine::detachAllMedia() for the snapshot machine, recursing 4469 // into the children first 4470 4471 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this 4472 MachineState_T oldState = mData->mMachineState; 4473 mData->mMachineState = MachineState_DeletingSnapshot; 4474 4475 // make a copy of the first snapshot so the refcount does not drop to 0 4476 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs 4477 // because of the AutoCaller voodoo) 4478 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot; 4479 4480 // GO! 4481 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete); 4482 4483 mData->mMachineState = oldState; 4484 } 4485 4486 if (FAILED(rc)) 4487 { 4488 rollbackMedia(); 4489 return rc; 4490 } 4491 4492 // commit all the media changes made above 4493 commitMedia(); 4494 4495 mData->mRegistered = false; 4496 4497 // machine lock no longer needed 4498 alock.release(); 4499 4500 // return media to caller 4501 SafeIfaceArray<IMedium> sfaMedia(llMedia); 4502 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia)); 4503 4504 mParent->unregisterMachine(this, id); 4505 // calls VirtualBox::saveSettings() 4506 4507 return S_OK; 4508 } 4509 4510 struct Machine::DeleteTask 4511 { 4512 ComObjPtr<Machine> pMachine; 4513 std::list<Utf8Str> llFilesToDelete; 4514 ComObjPtr<Progress> pProgress; 4515 GuidList llRegistriesThatNeedSaving; 4516 }; 4517 4518 STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress) 4519 { 4520 LogFlowFuncEnter(); 4521 4522 AutoCaller autoCaller(this); 4523 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4524 4525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4526 4527 HRESULT rc = checkStateDependency(MutableStateDep); 4528 if (FAILED(rc)) return rc; 4529 4530 if (mData->mRegistered) 4531 return setError(VBOX_E_INVALID_VM_STATE, 4532 tr("Cannot delete settings of a registered machine")); 4533 4534 DeleteTask *pTask = new DeleteTask; 4535 pTask->pMachine = this; 4536 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia)); 4537 4538 // collect files to delete 4539 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister() 4540 4541 for (size_t i = 0; i < sfaMedia.size(); ++i) 4542 { 4543 IMedium *pIMedium(sfaMedia[i]); 4544 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium); 4545 if (pMedium.isNull()) 4546 return setError(E_INVALIDARG, "The given medium pointer %d is invalid", i); 4547 AutoCaller mediumAutoCaller(pMedium); 4548 if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc(); 4549 4550 Utf8Str bstrLocation = pMedium->getLocationFull(); 4551 4552 bool fDoesMediumNeedFileDeletion = pMedium->isMediumFormatFile(); 4553 4554 // close the medium now; if that succeeds, then that means the medium is no longer 4555 // in use and we can add it to the list of files to delete 4556 rc = pMedium->close(&pTask->llRegistriesThatNeedSaving, 4557 mediumAutoCaller); 4558 if (SUCCEEDED(rc) && fDoesMediumNeedFileDeletion) 4559 pTask->llFilesToDelete.push_back(bstrLocation); 4560 } 4561 if (mData->pMachineConfigFile->fileExists()) 4562 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull); 4563 4564 pTask->pProgress.createObject(); 4565 pTask->pProgress->init(getVirtualBox(), 4566 static_cast<IMachine*>(this) /* aInitiator */, 4567 Bstr(tr("Deleting files")).raw(), 4568 true /* fCancellable */, 4569 pTask->llFilesToDelete.size() + 1, // cOperations 4570 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw()); 4571 4572 int vrc = RTThreadCreate(NULL, 4573 Machine::deleteThread, 4574 (void*)pTask, 4575 0, 4576 RTTHREADTYPE_MAIN_WORKER, 4577 0, 4578 "MachineDelete"); 4579 4580 pTask->pProgress.queryInterfaceTo(aProgress); 4581 4582 if (RT_FAILURE(vrc)) 4583 { 4584 delete pTask; 4585 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc); 4586 } 4587 4588 LogFlowFuncLeave(); 4589 4590 return S_OK; 4591 } 4592 4593 /** 4594 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then 4595 * calls Machine::deleteTaskWorker() on the actual machine object. 4596 * @param Thread 4597 * @param pvUser 4598 * @return 4599 */ 4600 /*static*/ 4601 DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser) 4602 { 4603 LogFlowFuncEnter(); 4604 4605 DeleteTask *pTask = (DeleteTask*)pvUser; 4606 Assert(pTask); 4607 Assert(pTask->pMachine); 4608 Assert(pTask->pProgress); 4609 4610 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask); 4611 pTask->pProgress->notifyComplete(rc); 4612 4613 delete pTask; 4614 4615 LogFlowFuncLeave(); 4616 4617 NOREF(Thread); 4618 4619 return VINF_SUCCESS; 4620 } 4621 4622 /** 4623 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread(). 4624 * @param task 4625 * @return 4626 */ 4627 HRESULT Machine::deleteTaskWorker(DeleteTask &task) 4628 { 4629 AutoCaller autoCaller(this); 4630 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4631 4632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4633 4634 ULONG uLogHistoryCount = 3; 4635 ComPtr<ISystemProperties> systemProperties; 4636 mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam()); 4637 if (!systemProperties.isNull()) 4638 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount); 4639 4640 // delete the files pushed on the task list by Machine::Delete() 4641 // (this includes saved states of the machine and snapshots and 4642 // medium storage files from the IMedium list passed in, and the 4643 // machine XML file) 4644 std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin(); 4645 while (it != task.llFilesToDelete.end()) 4646 { 4647 const Utf8Str &strFile = *it; 4648 LogFunc(("Deleting file %s\n", strFile.c_str())); 4649 RTFileDelete(strFile.c_str()); 4650 4651 ++it; 4652 if (it == task.llFilesToDelete.end()) 4653 { 4654 task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1); 4655 break; 4656 } 4657 4658 task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1); 4659 } 4660 4661 /* delete the settings only when the file actually exists */ 4662 if (mData->pMachineConfigFile->fileExists()) 4663 { 4664 /* Delete any backup or uncommitted XML files. Ignore failures. 4665 See the fSafe parameter of xml::XmlFileWriter::write for details. */ 4666 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */ 4667 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff); 4668 RTFileDelete(otherXml.c_str()); 4669 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff); 4670 RTFileDelete(otherXml.c_str()); 4671 4672 /* delete the Logs folder, nothing important should be left 4673 * there (we don't check for errors because the user might have 4674 * some private files there that we don't want to delete) */ 4675 Utf8Str logFolder; 4676 getLogFolder(logFolder); 4677 Assert(logFolder.length()); 4678 if (RTDirExists(logFolder.c_str())) 4679 { 4680 /* Delete all VBox.log[.N] files from the Logs folder 4681 * (this must be in sync with the rotation logic in 4682 * Console::powerUpThread()). Also, delete the VBox.png[.N] 4683 * files that may have been created by the GUI. */ 4684 Utf8Str log = Utf8StrFmt("%s%cVBox.log", 4685 logFolder.c_str(), RTPATH_DELIMITER); 4686 RTFileDelete(log.c_str()); 4687 log = Utf8StrFmt("%s%cVBox.png", 4688 logFolder.c_str(), RTPATH_DELIMITER); 4689 RTFileDelete(log.c_str()); 4690 for (int i = uLogHistoryCount; i > 0; i--) 4691 { 4692 log = Utf8StrFmt("%s%cVBox.log.%d", 4693 logFolder.c_str(), RTPATH_DELIMITER, i); 4694 RTFileDelete(log.c_str()); 4695 log = Utf8StrFmt("%s%cVBox.png.%d", 4696 logFolder.c_str(), RTPATH_DELIMITER, i); 4697 RTFileDelete(log.c_str()); 4698 } 4699 4700 RTDirRemove(logFolder.c_str()); 4701 } 4702 4703 /* delete the Snapshots folder, nothing important should be left 4704 * there (we don't check for errors because the user might have 4705 * some private files there that we don't want to delete) */ 4706 Utf8Str strFullSnapshotFolder; 4707 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 4708 Assert(!strFullSnapshotFolder.isEmpty()); 4709 if (RTDirExists(strFullSnapshotFolder.c_str())) 4710 RTDirRemove(strFullSnapshotFolder.c_str()); 4711 4712 // delete the directory that contains the settings file, but only 4713 // if it matches the VM name 4714 Utf8Str settingsDir; 4715 if (isInOwnDir(&settingsDir)) 4716 RTDirRemove(settingsDir.c_str()); 4717 } 4718 4719 alock.release(); 4720 4721 mParent->saveRegistries(task.llRegistriesThatNeedSaving); 4722 4723 return S_OK; 4724 } 4725 4726 STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot) 4727 { 4728 CheckComArgOutPointerValid(aSnapshot); 4729 4730 AutoCaller autoCaller(this); 4731 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4732 4733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4734 4735 ComObjPtr<Snapshot> pSnapshot; 4736 HRESULT rc; 4737 4738 if (!aNameOrId || !*aNameOrId) 4739 // null case (caller wants root snapshot): findSnapshotById() handles this 4740 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */); 4741 else 4742 { 4743 Guid uuid(aNameOrId); 4744 if (!uuid.isEmpty()) 4745 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */); 4746 else 4747 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */); 4748 } 4749 pSnapshot.queryInterfaceTo(aSnapshot); 4750 4751 return rc; 4752 } 4753 4754 STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount) 4755 { 4756 CheckComArgStrNotEmptyOrNull(aName); 4757 CheckComArgStrNotEmptyOrNull(aHostPath); 4758 4759 AutoCaller autoCaller(this); 4760 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4761 4762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4763 4764 HRESULT rc = checkStateDependency(MutableStateDep); 4765 if (FAILED(rc)) return rc; 4766 4767 Utf8Str strName(aName); 4768 4769 ComObjPtr<SharedFolder> sharedFolder; 4770 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */); 4771 if (SUCCEEDED(rc)) 4772 return setError(VBOX_E_OBJECT_IN_USE, 4773 tr("Shared folder named '%s' already exists"), 4774 strName.c_str()); 4775 4776 sharedFolder.createObject(); 4777 rc = sharedFolder->init(getMachine(), 4778 strName, 4779 aHostPath, 4780 !!aWritable, 4781 !!aAutoMount, 4782 true /* fFailOnError */); 4783 if (FAILED(rc)) return rc; 4784 4785 setModified(IsModified_SharedFolders); 4786 mHWData.backup(); 4787 mHWData->mSharedFolders.push_back(sharedFolder); 4788 4789 /* inform the direct session if any */ 4790 alock.leave(); 4791 onSharedFolderChange(); 4792 4793 return S_OK; 4794 } 4795 4796 STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName) 4797 { 4798 CheckComArgStrNotEmptyOrNull(aName); 4799 4800 AutoCaller autoCaller(this); 4801 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4802 4803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4804 4805 HRESULT rc = checkStateDependency(MutableStateDep); 4806 if (FAILED(rc)) return rc; 4807 4808 ComObjPtr<SharedFolder> sharedFolder; 4809 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */); 4810 if (FAILED(rc)) return rc; 4811 4812 setModified(IsModified_SharedFolders); 4813 mHWData.backup(); 4814 mHWData->mSharedFolders.remove(sharedFolder); 4815 4816 /* inform the direct session if any */ 4817 alock.leave(); 4818 onSharedFolderChange(); 4819 4820 return S_OK; 4821 } 4822 4823 STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow) 4824 { 4825 CheckComArgOutPointerValid(aCanShow); 4826 4827 /* start with No */ 4828 *aCanShow = FALSE; 4829 4830 AutoCaller autoCaller(this); 4831 AssertComRCReturnRC(autoCaller.rc()); 4832 4833 ComPtr<IInternalSessionControl> directControl; 4834 { 4835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4836 4837 if (mData->mSession.mState != SessionState_Locked) 4838 return setError(VBOX_E_INVALID_VM_STATE, 4839 tr("Machine is not locked for session (session state: %s)"), 4840 Global::stringifySessionState(mData->mSession.mState)); 4841 4842 directControl = mData->mSession.mDirectControl; 4843 } 4844 4845 /* ignore calls made after #OnSessionEnd() is called */ 4846 if (!directControl) 4847 return S_OK; 4848 4849 LONG64 dummy; 4850 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy); 4851 } 4852 4853 STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId) 4854 { 4855 CheckComArgOutPointerValid(aWinId); 4856 4857 AutoCaller autoCaller(this); 4858 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4859 4860 ComPtr<IInternalSessionControl> directControl; 4861 { 4862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4863 4864 if (mData->mSession.mState != SessionState_Locked) 4865 return setError(E_FAIL, 4866 tr("Machine is not locked for session (session state: %s)"), 4867 Global::stringifySessionState(mData->mSession.mState)); 4868 4869 directControl = mData->mSession.mDirectControl; 4870 } 4871 4872 /* ignore calls made after #OnSessionEnd() is called */ 4873 if (!directControl) 4874 return S_OK; 4875 4876 BOOL dummy; 4877 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId); 4878 } 4879 4880 #ifdef VBOX_WITH_GUEST_PROPS 4881 /** 4882 * Look up a guest property in VBoxSVC's internal structures. 4883 */ 4884 HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName, 4885 BSTR *aValue, 4886 LONG64 *aTimestamp, 4887 BSTR *aFlags) const 4888 { 4889 using namespace guestProp; 4890 4891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4892 Utf8Str strName(aName); 4893 HWData::GuestPropertyList::const_iterator it; 4894 4895 for (it = mHWData->mGuestProperties.begin(); 4896 it != mHWData->mGuestProperties.end(); ++it) 4897 { 4898 if (it->strName == strName) 4899 { 4900 char szFlags[MAX_FLAGS_LEN + 1]; 4901 it->strValue.cloneTo(aValue); 4902 *aTimestamp = it->mTimestamp; 4903 writeFlags(it->mFlags, szFlags); 4904 Bstr(szFlags).cloneTo(aFlags); 4905 break; 4906 } 4907 } 4908 return S_OK; 4909 } 4910 4911 /** 4912 * Query the VM that a guest property belongs to for the property. 4913 * @returns E_ACCESSDENIED if the VM process is not available or not 4914 * currently handling queries and the lookup should then be done in 4915 * VBoxSVC. 4916 */ 4917 HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName, 4918 BSTR *aValue, 4919 LONG64 *aTimestamp, 4920 BSTR *aFlags) const 4921 { 4922 HRESULT rc; 4923 ComPtr<IInternalSessionControl> directControl; 4924 directControl = mData->mSession.mDirectControl; 4925 4926 /* fail if we were called after #OnSessionEnd() is called. This is a 4927 * silly race condition. */ 4928 4929 if (!directControl) 4930 rc = E_ACCESSDENIED; 4931 else 4932 rc = directControl->AccessGuestProperty(aName, NULL, NULL, 4933 false /* isSetter */, 4934 aValue, aTimestamp, aFlags); 4935 return rc; 4936 } 4937 #endif // VBOX_WITH_GUEST_PROPS 4938 4939 STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName, 4940 BSTR *aValue, 4941 LONG64 *aTimestamp, 4942 BSTR *aFlags) 4943 { 4944 #ifndef VBOX_WITH_GUEST_PROPS 4945 ReturnComNotImplemented(); 4946 #else // VBOX_WITH_GUEST_PROPS 4947 CheckComArgStrNotEmptyOrNull(aName); 4948 CheckComArgOutPointerValid(aValue); 4949 CheckComArgOutPointerValid(aTimestamp); 4950 CheckComArgOutPointerValid(aFlags); 4951 4952 AutoCaller autoCaller(this); 4953 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4954 4955 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags); 4956 if (rc == E_ACCESSDENIED) 4957 /* The VM is not running or the service is not (yet) accessible */ 4958 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags); 4959 return rc; 4960 #endif // VBOX_WITH_GUEST_PROPS 4961 } 4962 4963 STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue) 4964 { 4965 LONG64 dummyTimestamp; 4966 Bstr dummyFlags; 4967 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam()); 4968 } 4969 4970 STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp) 4971 { 4972 Bstr dummyValue; 4973 Bstr dummyFlags; 4974 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam()); 4975 } 4976 4977 #ifdef VBOX_WITH_GUEST_PROPS 4978 /** 4979 * Set a guest property in VBoxSVC's internal structures. 4980 */ 4981 HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, 4982 IN_BSTR aFlags) 4983 { 4984 using namespace guestProp; 4985 4986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4987 HRESULT rc = S_OK; 4988 HWData::GuestProperty property; 4989 property.mFlags = NILFLAG; 4990 bool found = false; 4991 4992 rc = checkStateDependency(MutableStateDep); 4993 if (FAILED(rc)) return rc; 4994 4995 try 4996 { 4997 Utf8Str utf8Name(aName); 4998 Utf8Str utf8Flags(aFlags); 4999 uint32_t fFlags = NILFLAG; 5000 if ( (aFlags != NULL) 5001 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)) 5002 ) 5003 return setError(E_INVALIDARG, 5004 tr("Invalid flag values: '%ls'"), 5005 aFlags); 5006 5007 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I 5008 * know, this is simple and do an OK job atm.) */ 5009 HWData::GuestPropertyList::iterator it; 5010 for (it = mHWData->mGuestProperties.begin(); 5011 it != mHWData->mGuestProperties.end(); ++it) 5012 if (it->strName == utf8Name) 5013 { 5014 property = *it; 5015 if (it->mFlags & (RDONLYHOST)) 5016 rc = setError(E_ACCESSDENIED, 5017 tr("The property '%ls' cannot be changed by the host"), 5018 aName); 5019 else 5020 { 5021 setModified(IsModified_MachineData); 5022 mHWData.backup(); // @todo r=dj backup in a loop?!? 5023 5024 /* The backup() operation invalidates our iterator, so 5025 * get a new one. */ 5026 for (it = mHWData->mGuestProperties.begin(); 5027 it->strName != utf8Name; 5028 ++it) 5029 ; 5030 mHWData->mGuestProperties.erase(it); 5031 } 5032 found = true; 5033 break; 5034 } 5035 if (found && SUCCEEDED(rc)) 5036 { 5037 if (*aValue) 5038 { 5039 RTTIMESPEC time; 5040 property.strValue = aValue; 5041 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 5042 if (aFlags != NULL) 5043 property.mFlags = fFlags; 5044 mHWData->mGuestProperties.push_back(property); 5045 } 5046 } 5047 else if (SUCCEEDED(rc) && *aValue) 5048 { 5049 RTTIMESPEC time; 5050 setModified(IsModified_MachineData); 5051 mHWData.backup(); 5052 property.strName = aName; 5053 property.strValue = aValue; 5054 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 5055 property.mFlags = fFlags; 5056 mHWData->mGuestProperties.push_back(property); 5057 } 5058 if ( SUCCEEDED(rc) 5059 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty() 5060 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(), 5061 RTSTR_MAX, 5062 utf8Name.c_str(), 5063 RTSTR_MAX, 5064 NULL) 5065 ) 5066 ) 5067 { 5068 /** @todo r=bird: Why aren't we leaving the lock here? The 5069 * same code in PushGuestProperty does... */ 5070 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags); 5071 } 5072 } 5073 catch (std::bad_alloc &) 5074 { 5075 rc = E_OUTOFMEMORY; 5076 } 5077 5078 return rc; 5079 } 5080 5081 /** 5082 * Set a property on the VM that that property belongs to. 5083 * @returns E_ACCESSDENIED if the VM process is not available or not 5084 * currently handling queries and the setting should then be done in 5085 * VBoxSVC. 5086 */ 5087 HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue, 5088 IN_BSTR aFlags) 5089 { 5090 HRESULT rc; 5091 5092 try 5093 { 5094 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl; 5095 5096 BSTR dummy = NULL; /* will not be changed (setter) */ 5097 LONG64 dummy64; 5098 if (!directControl) 5099 rc = E_ACCESSDENIED; 5100 else 5101 /** @todo Fix when adding DeleteGuestProperty(), 5102 see defect. */ 5103 rc = directControl->AccessGuestProperty(aName, aValue, aFlags, 5104 true /* isSetter */, 5105 &dummy, &dummy64, &dummy); 5106 } 5107 catch (std::bad_alloc &) 5108 { 5109 rc = E_OUTOFMEMORY; 5110 } 5111 5112 return rc; 5113 } 5114 #endif // VBOX_WITH_GUEST_PROPS 5115 5116 STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue, 5117 IN_BSTR aFlags) 5118 { 5119 #ifndef VBOX_WITH_GUEST_PROPS 5120 ReturnComNotImplemented(); 5121 #else // VBOX_WITH_GUEST_PROPS 5122 CheckComArgStrNotEmptyOrNull(aName); 5123 CheckComArgMaybeNull(aFlags); 5124 CheckComArgMaybeNull(aValue); 5125 5126 AutoCaller autoCaller(this); 5127 if (FAILED(autoCaller.rc())) 5128 return autoCaller.rc(); 5129 5130 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags); 5131 if (rc == E_ACCESSDENIED) 5132 /* The VM is not running or the service is not (yet) accessible */ 5133 rc = setGuestPropertyToService(aName, aValue, aFlags); 5134 return rc; 5135 #endif // VBOX_WITH_GUEST_PROPS 5136 } 5137 5138 STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue) 5139 { 5140 return SetGuestProperty(aName, aValue, NULL); 5141 } 5142 5143 #ifdef VBOX_WITH_GUEST_PROPS 5144 /** 5145 * Enumerate the guest properties in VBoxSVC's internal structures. 5146 */ 5147 HRESULT Machine::enumerateGuestPropertiesInService 5148 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 5149 ComSafeArrayOut(BSTR, aValues), 5150 ComSafeArrayOut(LONG64, aTimestamps), 5151 ComSafeArrayOut(BSTR, aFlags)) 5152 { 5153 using namespace guestProp; 5154 5155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5156 Utf8Str strPatterns(aPatterns); 5157 5158 /* 5159 * Look for matching patterns and build up a list. 5160 */ 5161 HWData::GuestPropertyList propList; 5162 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin(); 5163 it != mHWData->mGuestProperties.end(); 5164 ++it) 5165 if ( strPatterns.isEmpty() 5166 || RTStrSimplePatternMultiMatch(strPatterns.c_str(), 5167 RTSTR_MAX, 5168 it->strName.c_str(), 5169 RTSTR_MAX, 5170 NULL) 5171 ) 5172 propList.push_back(*it); 5173 5174 /* 5175 * And build up the arrays for returning the property information. 5176 */ 5177 size_t cEntries = propList.size(); 5178 SafeArray<BSTR> names(cEntries); 5179 SafeArray<BSTR> values(cEntries); 5180 SafeArray<LONG64> timestamps(cEntries); 5181 SafeArray<BSTR> flags(cEntries); 5182 size_t iProp = 0; 5183 for (HWData::GuestPropertyList::iterator it = propList.begin(); 5184 it != propList.end(); 5185 ++it) 5186 { 5187 char szFlags[MAX_FLAGS_LEN + 1]; 5188 it->strName.cloneTo(&names[iProp]); 5189 it->strValue.cloneTo(&values[iProp]); 5190 timestamps[iProp] = it->mTimestamp; 5191 writeFlags(it->mFlags, szFlags); 5192 Bstr(szFlags).cloneTo(&flags[iProp]); 5193 ++iProp; 5194 } 5195 names.detachTo(ComSafeArrayOutArg(aNames)); 5196 values.detachTo(ComSafeArrayOutArg(aValues)); 5197 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps)); 5198 flags.detachTo(ComSafeArrayOutArg(aFlags)); 5199 return S_OK; 5200 } 5201 5202 /** 5203 * Enumerate the properties managed by a VM. 5204 * @returns E_ACCESSDENIED if the VM process is not available or not 5205 * currently handling queries and the setting should then be done in 5206 * VBoxSVC. 5207 */ 5208 HRESULT Machine::enumerateGuestPropertiesOnVM 5209 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 5210 ComSafeArrayOut(BSTR, aValues), 5211 ComSafeArrayOut(LONG64, aTimestamps), 5212 ComSafeArrayOut(BSTR, aFlags)) 5213 { 5214 HRESULT rc; 5215 ComPtr<IInternalSessionControl> directControl; 5216 directControl = mData->mSession.mDirectControl; 5217 5218 if (!directControl) 5219 rc = E_ACCESSDENIED; 5220 else 5221 rc = directControl->EnumerateGuestProperties 5222 (aPatterns, ComSafeArrayOutArg(aNames), 5223 ComSafeArrayOutArg(aValues), 5224 ComSafeArrayOutArg(aTimestamps), 5225 ComSafeArrayOutArg(aFlags)); 5226 return rc; 5227 } 5228 #endif // VBOX_WITH_GUEST_PROPS 5229 5230 STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns, 5231 ComSafeArrayOut(BSTR, aNames), 5232 ComSafeArrayOut(BSTR, aValues), 5233 ComSafeArrayOut(LONG64, aTimestamps), 5234 ComSafeArrayOut(BSTR, aFlags)) 5235 { 5236 #ifndef VBOX_WITH_GUEST_PROPS 5237 ReturnComNotImplemented(); 5238 #else // VBOX_WITH_GUEST_PROPS 5239 CheckComArgMaybeNull(aPatterns); 5240 CheckComArgOutSafeArrayPointerValid(aNames); 5241 CheckComArgOutSafeArrayPointerValid(aValues); 5242 CheckComArgOutSafeArrayPointerValid(aTimestamps); 5243 CheckComArgOutSafeArrayPointerValid(aFlags); 5244 5245 AutoCaller autoCaller(this); 5246 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5247 5248 HRESULT rc = enumerateGuestPropertiesOnVM 5249 (aPatterns, ComSafeArrayOutArg(aNames), 5250 ComSafeArrayOutArg(aValues), 5251 ComSafeArrayOutArg(aTimestamps), 5252 ComSafeArrayOutArg(aFlags)); 5253 if (rc == E_ACCESSDENIED) 5254 /* The VM is not running or the service is not (yet) accessible */ 5255 rc = enumerateGuestPropertiesInService 5256 (aPatterns, ComSafeArrayOutArg(aNames), 5257 ComSafeArrayOutArg(aValues), 5258 ComSafeArrayOutArg(aTimestamps), 5259 ComSafeArrayOutArg(aFlags)); 5260 return rc; 5261 #endif // VBOX_WITH_GUEST_PROPS 5262 } 5263 5264 STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName, 5265 ComSafeArrayOut(IMediumAttachment*, aAttachments)) 5266 { 5267 MediaData::AttachmentList atts; 5268 5269 HRESULT rc = getMediumAttachmentsOfController(aName, atts); 5270 if (FAILED(rc)) return rc; 5271 5272 SafeIfaceArray<IMediumAttachment> attachments(atts); 5273 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 5274 5275 return S_OK; 5276 } 5277 5278 STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName, 5279 LONG aControllerPort, 5280 LONG aDevice, 5281 IMediumAttachment **aAttachment) 5282 { 5283 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 5284 aControllerName, aControllerPort, aDevice)); 5285 5286 CheckComArgStrNotEmptyOrNull(aControllerName); 5287 CheckComArgOutPointerValid(aAttachment); 5288 5289 AutoCaller autoCaller(this); 5290 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5291 5292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5293 5294 *aAttachment = NULL; 5295 5296 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 5297 aControllerName, 5298 aControllerPort, 5299 aDevice); 5300 if (pAttach.isNull()) 5301 return setError(VBOX_E_OBJECT_NOT_FOUND, 5302 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 5303 aDevice, aControllerPort, aControllerName); 5304 5305 pAttach.queryInterfaceTo(aAttachment); 5306 5307 return S_OK; 5308 } 5309 5310 STDMETHODIMP Machine::AddStorageController(IN_BSTR aName, 5311 StorageBus_T aConnectionType, 5312 IStorageController **controller) 5313 { 5314 CheckComArgStrNotEmptyOrNull(aName); 5315 5316 if ( (aConnectionType <= StorageBus_Null) 5317 || (aConnectionType > StorageBus_SAS)) 5318 return setError(E_INVALIDARG, 5319 tr("Invalid connection type: %d"), 5320 aConnectionType); 5321 5322 AutoCaller autoCaller(this); 5323 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5324 5325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5326 5327 HRESULT rc = checkStateDependency(MutableStateDep); 5328 if (FAILED(rc)) return rc; 5329 5330 /* try to find one with the name first. */ 5331 ComObjPtr<StorageController> ctrl; 5332 5333 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */); 5334 if (SUCCEEDED(rc)) 5335 return setError(VBOX_E_OBJECT_IN_USE, 5336 tr("Storage controller named '%ls' already exists"), 5337 aName); 5338 5339 ctrl.createObject(); 5340 5341 /* get a new instance number for the storage controller */ 5342 ULONG ulInstance = 0; 5343 bool fBootable = true; 5344 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 5345 it != mStorageControllers->end(); 5346 ++it) 5347 { 5348 if ((*it)->getStorageBus() == aConnectionType) 5349 { 5350 ULONG ulCurInst = (*it)->getInstance(); 5351 5352 if (ulCurInst >= ulInstance) 5353 ulInstance = ulCurInst + 1; 5354 5355 /* Only one controller of each type can be marked as bootable. */ 5356 if ((*it)->getBootable()) 5357 fBootable = false; 5358 } 5359 } 5360 5361 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable); 5362 if (FAILED(rc)) return rc; 5363 5364 setModified(IsModified_Storage); 5365 mStorageControllers.backup(); 5366 mStorageControllers->push_back(ctrl); 5367 5368 ctrl.queryInterfaceTo(controller); 5369 5370 /* inform the direct session if any */ 5371 alock.leave(); 5372 onStorageControllerChange(); 5373 5374 return S_OK; 5375 } 5376 5377 STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName, 5378 IStorageController **aStorageController) 5379 { 5380 CheckComArgStrNotEmptyOrNull(aName); 5381 5382 AutoCaller autoCaller(this); 5383 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5384 5385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5386 5387 ComObjPtr<StorageController> ctrl; 5388 5389 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 5390 if (SUCCEEDED(rc)) 5391 ctrl.queryInterfaceTo(aStorageController); 5392 5393 return rc; 5394 } 5395 5396 STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance, 5397 IStorageController **aStorageController) 5398 { 5399 AutoCaller autoCaller(this); 5400 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5401 5402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5403 5404 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 5405 it != mStorageControllers->end(); 5406 ++it) 5407 { 5408 if ((*it)->getInstance() == aInstance) 5409 { 5410 (*it).queryInterfaceTo(aStorageController); 5411 return S_OK; 5412 } 5413 } 5414 5415 return setError(VBOX_E_OBJECT_NOT_FOUND, 5416 tr("Could not find a storage controller with instance number '%lu'"), 5417 aInstance); 5418 } 5419 5420 STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable) 5421 { 5422 AutoCaller autoCaller(this); 5423 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5424 5425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5426 5427 HRESULT rc = checkStateDependency(MutableStateDep); 5428 if (FAILED(rc)) return rc; 5429 5430 ComObjPtr<StorageController> ctrl; 5431 5432 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 5433 if (SUCCEEDED(rc)) 5434 { 5435 /* Ensure that only one controller of each type is marked as bootable. */ 5436 if (fBootable == TRUE) 5437 { 5438 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 5439 it != mStorageControllers->end(); 5440 ++it) 5441 { 5442 ComObjPtr<StorageController> aCtrl = (*it); 5443 5444 if ( (aCtrl->getName() != Utf8Str(aName)) 5445 && aCtrl->getBootable() == TRUE 5446 && aCtrl->getStorageBus() == ctrl->getStorageBus() 5447 && aCtrl->getControllerType() == ctrl->getControllerType()) 5448 { 5449 aCtrl->setBootable(FALSE); 5450 break; 5451 } 5452 } 5453 } 5454 5455 if (SUCCEEDED(rc)) 5456 { 5457 ctrl->setBootable(fBootable); 5458 setModified(IsModified_Storage); 5459 } 5460 } 5461 5462 if (SUCCEEDED(rc)) 5463 { 5464 /* inform the direct session if any */ 5465 alock.leave(); 5466 onStorageControllerChange(); 5467 } 5468 5469 return rc; 5470 } 5471 5472 STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName) 5473 { 5474 CheckComArgStrNotEmptyOrNull(aName); 5475 5476 AutoCaller autoCaller(this); 5477 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5478 5479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5480 5481 HRESULT rc = checkStateDependency(MutableStateDep); 5482 if (FAILED(rc)) return rc; 5483 5484 ComObjPtr<StorageController> ctrl; 5485 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 5486 if (FAILED(rc)) return rc; 5487 5488 /* We can remove the controller only if there is no device attached. */ 5489 /* check if the device slot is already busy */ 5490 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 5491 it != mMediaData->mAttachments.end(); 5492 ++it) 5493 { 5494 if ((*it)->getControllerName() == aName) 5495 return setError(VBOX_E_OBJECT_IN_USE, 5496 tr("Storage controller named '%ls' has still devices attached"), 5497 aName); 5498 } 5499 5500 /* We can remove it now. */ 5501 setModified(IsModified_Storage); 5502 mStorageControllers.backup(); 5503 5504 ctrl->unshare(); 5505 5506 mStorageControllers->remove(ctrl); 5507 5508 /* inform the direct session if any */ 5509 alock.leave(); 5510 onStorageControllerChange(); 5511 5512 return S_OK; 5513 } 5514 5515 STDMETHODIMP Machine::QuerySavedGuestSize(ULONG uScreenId, ULONG *puWidth, ULONG *puHeight) 5516 { 5517 LogFlowThisFunc(("\n")); 5518 5519 CheckComArgNotNull(puWidth); 5520 CheckComArgNotNull(puHeight); 5521 5522 uint32_t u32Width = 0; 5523 uint32_t u32Height = 0; 5524 5525 int vrc = readSavedGuestSize(mSSData->strStateFilePath, uScreenId, &u32Width, &u32Height); 5526 if (RT_FAILURE(vrc)) 5527 return setError(VBOX_E_IPRT_ERROR, 5528 tr("Saved guest size is not available (%Rrc)"), 5529 vrc); 5530 5531 *puWidth = u32Width; 5532 *puHeight = u32Height; 5533 5534 return S_OK; 5535 } 5536 5537 STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight) 5538 { 5539 LogFlowThisFunc(("\n")); 5540 5541 CheckComArgNotNull(aSize); 5542 CheckComArgNotNull(aWidth); 5543 CheckComArgNotNull(aHeight); 5544 5545 if (aScreenId != 0) 5546 return E_NOTIMPL; 5547 5548 AutoCaller autoCaller(this); 5549 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5550 5551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5552 5553 uint8_t *pu8Data = NULL; 5554 uint32_t cbData = 0; 5555 uint32_t u32Width = 0; 5556 uint32_t u32Height = 0; 5557 5558 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5559 5560 if (RT_FAILURE(vrc)) 5561 return setError(VBOX_E_IPRT_ERROR, 5562 tr("Saved screenshot data is not available (%Rrc)"), 5563 vrc); 5564 5565 *aSize = cbData; 5566 *aWidth = u32Width; 5567 *aHeight = u32Height; 5568 5569 freeSavedDisplayScreenshot(pu8Data); 5570 5571 return S_OK; 5572 } 5573 5574 STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5575 { 5576 LogFlowThisFunc(("\n")); 5577 5578 CheckComArgNotNull(aWidth); 5579 CheckComArgNotNull(aHeight); 5580 CheckComArgOutSafeArrayPointerValid(aData); 5581 5582 if (aScreenId != 0) 5583 return E_NOTIMPL; 5584 5585 AutoCaller autoCaller(this); 5586 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5587 5588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5589 5590 uint8_t *pu8Data = NULL; 5591 uint32_t cbData = 0; 5592 uint32_t u32Width = 0; 5593 uint32_t u32Height = 0; 5594 5595 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5596 5597 if (RT_FAILURE(vrc)) 5598 return setError(VBOX_E_IPRT_ERROR, 5599 tr("Saved screenshot data is not available (%Rrc)"), 5600 vrc); 5601 5602 *aWidth = u32Width; 5603 *aHeight = u32Height; 5604 5605 com::SafeArray<BYTE> bitmap(cbData); 5606 /* Convert pixels to format expected by the API caller. */ 5607 if (aBGR) 5608 { 5609 /* [0] B, [1] G, [2] R, [3] A. */ 5610 for (unsigned i = 0; i < cbData; i += 4) 5611 { 5612 bitmap[i] = pu8Data[i]; 5613 bitmap[i + 1] = pu8Data[i + 1]; 5614 bitmap[i + 2] = pu8Data[i + 2]; 5615 bitmap[i + 3] = 0xff; 5616 } 5617 } 5618 else 5619 { 5620 /* [0] R, [1] G, [2] B, [3] A. */ 5621 for (unsigned i = 0; i < cbData; i += 4) 5622 { 5623 bitmap[i] = pu8Data[i + 2]; 5624 bitmap[i + 1] = pu8Data[i + 1]; 5625 bitmap[i + 2] = pu8Data[i]; 5626 bitmap[i + 3] = 0xff; 5627 } 5628 } 5629 bitmap.detachTo(ComSafeArrayOutArg(aData)); 5630 5631 freeSavedDisplayScreenshot(pu8Data); 5632 5633 return S_OK; 5634 } 5635 5636 5637 STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5638 { 5639 LogFlowThisFunc(("\n")); 5640 5641 CheckComArgNotNull(aWidth); 5642 CheckComArgNotNull(aHeight); 5643 CheckComArgOutSafeArrayPointerValid(aData); 5644 5645 if (aScreenId != 0) 5646 return E_NOTIMPL; 5647 5648 AutoCaller autoCaller(this); 5649 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5650 5651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5652 5653 uint8_t *pu8Data = NULL; 5654 uint32_t cbData = 0; 5655 uint32_t u32Width = 0; 5656 uint32_t u32Height = 0; 5657 5658 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5659 5660 if (RT_FAILURE(vrc)) 5661 return setError(VBOX_E_IPRT_ERROR, 5662 tr("Saved screenshot data is not available (%Rrc)"), 5663 vrc); 5664 5665 *aWidth = u32Width; 5666 *aHeight = u32Height; 5667 5668 uint8_t *pu8PNG = NULL; 5669 uint32_t cbPNG = 0; 5670 uint32_t cxPNG = 0; 5671 uint32_t cyPNG = 0; 5672 5673 DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0); 5674 5675 com::SafeArray<BYTE> screenData(cbPNG); 5676 screenData.initFrom(pu8PNG, cbPNG); 5677 RTMemFree(pu8PNG); 5678 5679 screenData.detachTo(ComSafeArrayOutArg(aData)); 5680 5681 freeSavedDisplayScreenshot(pu8Data); 5682 5683 return S_OK; 5684 } 5685 5686 STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight) 5687 { 5688 LogFlowThisFunc(("\n")); 5689 5690 CheckComArgNotNull(aSize); 5691 CheckComArgNotNull(aWidth); 5692 CheckComArgNotNull(aHeight); 5693 5694 if (aScreenId != 0) 5695 return E_NOTIMPL; 5696 5697 AutoCaller autoCaller(this); 5698 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5699 5700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5701 5702 uint8_t *pu8Data = NULL; 5703 uint32_t cbData = 0; 5704 uint32_t u32Width = 0; 5705 uint32_t u32Height = 0; 5706 5707 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5708 5709 if (RT_FAILURE(vrc)) 5710 return setError(VBOX_E_IPRT_ERROR, 5711 tr("Saved screenshot data is not available (%Rrc)"), 5712 vrc); 5713 5714 *aSize = cbData; 5715 *aWidth = u32Width; 5716 *aHeight = u32Height; 5717 5718 freeSavedDisplayScreenshot(pu8Data); 5719 5720 return S_OK; 5721 } 5722 5723 STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5724 { 5725 LogFlowThisFunc(("\n")); 5726 5727 CheckComArgNotNull(aWidth); 5728 CheckComArgNotNull(aHeight); 5729 CheckComArgOutSafeArrayPointerValid(aData); 5730 5731 if (aScreenId != 0) 5732 return E_NOTIMPL; 5733 5734 AutoCaller autoCaller(this); 5735 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5736 5737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5738 5739 uint8_t *pu8Data = NULL; 5740 uint32_t cbData = 0; 5741 uint32_t u32Width = 0; 5742 uint32_t u32Height = 0; 5743 5744 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5745 5746 if (RT_FAILURE(vrc)) 5747 return setError(VBOX_E_IPRT_ERROR, 5748 tr("Saved screenshot thumbnail data is not available (%Rrc)"), 5749 vrc); 5750 5751 *aWidth = u32Width; 5752 *aHeight = u32Height; 5753 5754 com::SafeArray<BYTE> png(cbData); 5755 png.initFrom(pu8Data, cbData); 5756 png.detachTo(ComSafeArrayOutArg(aData)); 5757 5758 freeSavedDisplayScreenshot(pu8Data); 5759 5760 return S_OK; 5761 } 5762 5763 STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu) 5764 { 5765 HRESULT rc = S_OK; 5766 LogFlowThisFunc(("\n")); 5767 5768 AutoCaller autoCaller(this); 5769 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5770 5771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5772 5773 if (!mHWData->mCPUHotPlugEnabled) 5774 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 5775 5776 if (aCpu >= mHWData->mCPUCount) 5777 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1); 5778 5779 if (mHWData->mCPUAttached[aCpu]) 5780 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu); 5781 5782 alock.release(); 5783 rc = onCPUChange(aCpu, false); 5784 alock.acquire(); 5785 if (FAILED(rc)) return rc; 5786 5787 setModified(IsModified_MachineData); 5788 mHWData.backup(); 5789 mHWData->mCPUAttached[aCpu] = true; 5790 5791 /* Save settings if online */ 5792 if (Global::IsOnline(mData->mMachineState)) 5793 saveSettings(NULL); 5794 5795 return S_OK; 5796 } 5797 5798 STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu) 5799 { 5800 HRESULT rc = S_OK; 5801 LogFlowThisFunc(("\n")); 5802 5803 AutoCaller autoCaller(this); 5804 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5805 5806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5807 5808 if (!mHWData->mCPUHotPlugEnabled) 5809 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 5810 5811 if (aCpu >= SchemaDefs::MaxCPUCount) 5812 return setError(E_INVALIDARG, 5813 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"), 5814 SchemaDefs::MaxCPUCount); 5815 5816 if (!mHWData->mCPUAttached[aCpu]) 5817 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu); 5818 5819 /* CPU 0 can't be detached */ 5820 if (aCpu == 0) 5821 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0")); 5822 5823 alock.release(); 5824 rc = onCPUChange(aCpu, true); 5825 alock.acquire(); 5826 if (FAILED(rc)) return rc; 5827 5828 setModified(IsModified_MachineData); 5829 mHWData.backup(); 5830 mHWData->mCPUAttached[aCpu] = false; 5831 5832 /* Save settings if online */ 5833 if (Global::IsOnline(mData->mMachineState)) 5834 saveSettings(NULL); 5835 5836 return S_OK; 5837 } 5838 5839 STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached) 5840 { 5841 LogFlowThisFunc(("\n")); 5842 5843 CheckComArgNotNull(aCpuAttached); 5844 5845 *aCpuAttached = false; 5846 5847 AutoCaller autoCaller(this); 5848 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5849 5850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5851 5852 /* If hotplug is enabled the CPU is always enabled. */ 5853 if (!mHWData->mCPUHotPlugEnabled) 5854 { 5855 if (aCpu < mHWData->mCPUCount) 5856 *aCpuAttached = true; 5857 } 5858 else 5859 { 5860 if (aCpu < SchemaDefs::MaxCPUCount) 5861 *aCpuAttached = mHWData->mCPUAttached[aCpu]; 5862 } 5863 5864 return S_OK; 5865 } 5866 5867 STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName) 5868 { 5869 CheckComArgOutPointerValid(aName); 5870 5871 AutoCaller autoCaller(this); 5872 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5873 5874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5875 5876 Utf8Str log = queryLogFilename(aIdx); 5877 if (!RTFileExists(log.c_str())) 5878 log.setNull(); 5879 log.cloneTo(aName); 5880 5881 return S_OK; 5882 } 5883 5884 STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData)) 5885 { 5886 LogFlowThisFunc(("\n")); 5887 CheckComArgOutSafeArrayPointerValid(aData); 5888 if (aSize < 0) 5889 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize); 5890 5891 AutoCaller autoCaller(this); 5892 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5893 5894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5895 5896 HRESULT rc = S_OK; 5897 Utf8Str log = queryLogFilename(aIdx); 5898 5899 /* do not unnecessarily hold the lock while doing something which does 5900 * not need the lock and potentially takes a long time. */ 5901 alock.release(); 5902 5903 /* Limit the chunk size to 32K for now, as that gives better performance 5904 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice. 5905 * One byte expands to approx. 25 bytes of breathtaking XML. */ 5906 size_t cbData = (size_t)RT_MIN(aSize, 32768); 5907 com::SafeArray<BYTE> logData(cbData); 5908 5909 RTFILE LogFile; 5910 int vrc = RTFileOpen(&LogFile, log.c_str(), 5911 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); 5912 if (RT_SUCCESS(vrc)) 5913 { 5914 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData); 5915 if (RT_SUCCESS(vrc)) 5916 logData.resize(cbData); 5917 else 5918 rc = setError(VBOX_E_IPRT_ERROR, 5919 tr("Could not read log file '%s' (%Rrc)"), 5920 log.c_str(), vrc); 5921 RTFileClose(LogFile); 5922 } 5923 else 5924 rc = setError(VBOX_E_IPRT_ERROR, 5925 tr("Could not open log file '%s' (%Rrc)"), 5926 log.c_str(), vrc); 5927 5928 if (FAILED(rc)) 5929 logData.resize(0); 5930 logData.detachTo(ComSafeArrayOutArg(aData)); 5931 5932 return rc; 5933 } 5934 5935 5936 /** 5937 * Currently this method doesn't attach device to the running VM, 5938 * just makes sure it's plugged on next VM start. 5939 */ 5940 STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/) 5941 { 5942 AutoCaller autoCaller(this); 5943 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5944 5945 // lock scope 5946 { 5947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5948 5949 HRESULT rc = checkStateDependency(MutableStateDep); 5950 if (FAILED(rc)) return rc; 5951 5952 ChipsetType_T aChipset = ChipsetType_PIIX3; 5953 COMGETTER(ChipsetType)(&aChipset); 5954 5955 if (aChipset != ChipsetType_ICH9) 5956 { 5957 return setError(E_INVALIDARG, 5958 tr("Host PCI attachment only supported with ICH9 chipset")); 5959 } 5960 5961 // check if device with this host PCI address already attached 5962 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin(); 5963 it != mHWData->mPciDeviceAssignments.end(); 5964 ++it) 5965 { 5966 LONG iHostAddress = -1; 5967 ComPtr<PciDeviceAttachment> pAttach; 5968 pAttach = *it; 5969 pAttach->COMGETTER(HostAddress)(&iHostAddress); 5970 if (iHostAddress == hostAddress) 5971 return setError(E_INVALIDARG, 5972 tr("Device with host PCI address already attached to this VM")); 5973 } 5974 5975 ComObjPtr<PciDeviceAttachment> pda; 5976 char name[32]; 5977 5978 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7); 5979 Bstr bname(name); 5980 pda.createObject(); 5981 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE); 5982 setModified(IsModified_MachineData); 5983 mHWData.backup(); 5984 mHWData->mPciDeviceAssignments.push_back(pda); 5985 } 5986 5987 return S_OK; 5988 } 5989 5990 /** 5991 * Currently this method doesn't detach device from the running VM, 5992 * just makes sure it's not plugged on next VM start. 5993 */ 5994 STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress) 5995 { 5996 AutoCaller autoCaller(this); 5997 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5998 5999 ComObjPtr<PciDeviceAttachment> pAttach; 6000 bool fRemoved = false; 6001 HRESULT rc; 6002 6003 // lock scope 6004 { 6005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6006 6007 rc = checkStateDependency(MutableStateDep); 6008 if (FAILED(rc)) return rc; 6009 6010 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin(); 6011 it != mHWData->mPciDeviceAssignments.end(); 6012 ++it) 6013 { 6014 LONG iHostAddress = -1; 6015 pAttach = *it; 6016 pAttach->COMGETTER(HostAddress)(&iHostAddress); 6017 if (iHostAddress != -1 && iHostAddress == hostAddress) 6018 { 6019 setModified(IsModified_MachineData); 6020 mHWData.backup(); 6021 mHWData->mPciDeviceAssignments.remove(pAttach); 6022 fRemoved = true; 6023 break; 6024 } 6025 } 6026 } 6027 6028 6029 /* Fire event outside of the lock */ 6030 if (fRemoved) 6031 { 6032 Assert(!pAttach.isNull()); 6033 ComPtr<IEventSource> es; 6034 rc = mParent->COMGETTER(EventSource)(es.asOutParam()); 6035 Assert(SUCCEEDED(rc)); 6036 Bstr mid; 6037 rc = this->COMGETTER(Id)(mid.asOutParam()); 6038 Assert(SUCCEEDED(rc)); 6039 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL); 6040 } 6041 6042 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND, 6043 tr("No host PCI device %08x attached"), 6044 hostAddress 6045 ); 6046 } 6047 6048 STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments)) 6049 { 6050 CheckComArgOutSafeArrayPointerValid(aAssignments); 6051 6052 AutoCaller autoCaller(this); 6053 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6054 6055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6056 6057 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments); 6058 assignments.detachTo(ComSafeArrayOutArg(aAssignments)); 6059 6060 return S_OK; 6061 } 6062 6063 STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl) 6064 { 6065 CheckComArgOutPointerValid(aBandwidthControl); 6066 6067 AutoCaller autoCaller(this); 6068 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6069 6070 mBandwidthControl.queryInterfaceTo(aBandwidthControl); 6071 6072 return S_OK; 6073 } 6074 6075 struct Machine::CloneVMTask 6076 { 54 55 struct MachineCloneVMPrivate 56 { 57 MachineCloneVMPrivate(MachineCloneVM *a_q, ComObjPtr<Machine> &a_pSrcMachine, ComObjPtr<Machine> &a_pTrgMachine, CloneMode_T a_mode, bool a_fFullClone) 58 : q_ptr(a_q) 59 , p(a_pSrcMachine) 60 , pSrcMachine(a_pSrcMachine) 61 , pTrgMachine(a_pTrgMachine) 62 , mode(a_mode) 63 , fLinkDisks(!a_fFullClone) 64 {} 65 66 /* Thread management */ 67 int startWorker() 68 { 69 return RTThreadCreate(NULL, 70 MachineCloneVMPrivate::workerThread, 71 static_cast<void*>(this), 72 0, 73 RTTHREADTYPE_MAIN_WORKER, 74 0, 75 "MachineClone"); 76 } 77 78 static int workerThread(RTTHREAD /* Thread */, void *pvUser) 79 { 80 MachineCloneVMPrivate *pTask = static_cast<MachineCloneVMPrivate*>(pvUser); 81 AssertReturn(pTask, VERR_INVALID_POINTER); 82 83 HRESULT rc = pTask->q_ptr->run(); 84 85 pTask->pProgress->notifyComplete(rc); 86 87 pTask->q_ptr->destroy(); 88 89 return VINF_SUCCESS; 90 } 91 92 /* Private helper methods */ 93 HRESULT cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const; 94 settings::Snapshot cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const; 95 void cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const; 96 void cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const; 97 void cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const; 98 static int cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser); 99 100 /* Private q and parent pointer */ 101 MachineCloneVM *q_ptr; 102 ComObjPtr<Machine> p; 103 104 /* Private helper members */ 6077 105 ComObjPtr<Machine> pSrcMachine; 6078 106 ComObjPtr<Machine> pTrgMachine; … … 6082 110 CloneMode_T mode; 6083 111 bool fLinkDisks; 6084 typedef struct 6085 { 6086 ComPtr<IMedium> pMedium; 6087 uint64_t uSize; 6088 }MediumTask; 6089 typedef struct 6090 { 6091 RTCList<MediumTask> chain; 6092 bool fCreateDiffs; 6093 }MediumTaskChain; 6094 RTCList<MediumTaskChain> llMedias; 6095 typedef struct 6096 { 6097 Guid snapshotUuid; 6098 Utf8Str strSaveStateFile; 6099 uint64_t cbSize; 6100 }SaveStateTask; 6101 RTCList<SaveStateTask> llSaveStateFiles; /* Snapshot UUID -> File path */ 112 RTCList<MEDIUMTASKCHAIN> llMedias; 113 RTCList<SAVESTATETASK> llSaveStateFiles; /* Snapshot UUID -> File path */ 6102 114 }; 6103 115 6104 STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, BOOL fFullClone, IProgress **pProgress) 6105 { 6106 LogFlowFuncEnter(); 6107 6108 CheckComArgNotNull(pTarget); 6109 CheckComArgOutPointerValid(pProgress); 6110 6111 AutoCaller autoCaller(this); 6112 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6113 6114 AutoReadLock srcLock(this COMMA_LOCKVAL_SRC_POS); 6115 6116 CloneVMTask *pTask = NULL; 6117 6118 HRESULT rc; 6119 try 6120 { 6121 pTask = new CloneVMTask; 6122 pTask->pSrcMachine = this; 6123 pTask->pTrgMachine = static_cast<Machine*>(pTarget); 6124 pTask->fLinkDisks = !fFullClone; 6125 pTask->mode = mode; 6126 6127 /* Lock the target machine early (so nobody mess around with it in the meantime). */ 6128 AutoWriteLock trgLock(pTask->pTrgMachine COMMA_LOCKVAL_SRC_POS); 6129 6130 if (pTask->pSrcMachine->isSnapshotMachine()) 6131 pTask->snapshotId = pTask->pSrcMachine->getSnapshotId(); 6132 6133 /* Add the current machine and all snapshot machines below this machine 6134 * in a list for further processing. */ 6135 RTCList< ComObjPtr<Machine> > machineList; 6136 6137 /* Include current state? */ 6138 if ( pTask->mode == CloneMode_MachineState) 6139 // || pTask->mode == CloneMode_AllStates) 6140 machineList.append(pTask->pSrcMachine); 6141 /* Should be done a depth copy with all child snapshots? */ 6142 if ( pTask->mode == CloneMode_MachineAndChildStates 6143 || pTask->mode == CloneMode_AllStates) 6144 { 6145 ULONG cSnapshots = 0; 6146 rc = pTask->pSrcMachine->COMGETTER(SnapshotCount)(&cSnapshots); 6147 if (FAILED(rc)) throw rc; 6148 if (cSnapshots > 0) 6149 { 6150 Utf8Str id; 6151 if ( pTask->mode == CloneMode_MachineAndChildStates 6152 && !pTask->snapshotId.isEmpty()) 6153 id = pTask->snapshotId.toString(); 6154 ComPtr<ISnapshot> pSnapshot; 6155 rc = pTask->pSrcMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam()); 6156 if (FAILED(rc)) throw rc; 6157 rc = cloneCreateMachineList(pSnapshot, machineList); 6158 if (FAILED(rc)) throw rc; 6159 rc = pSnapshot->COMGETTER(Machine)(pTask->pOldMachineState.asOutParam()); 6160 if (FAILED(rc)) throw rc; 6161 } 6162 } 6163 6164 /* Go over every machine and walk over every attachment this machine has. */ 6165 ULONG uCount = 2; /* One init task and the machine creation. */ 6166 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */ 6167 for (size_t i = 0; i < machineList.size(); ++i) 6168 { 6169 ComObjPtr<Machine> machine = machineList.at(i); 6170 /* If this is the Snapshot Machine we want to clone, we need to 6171 * create a new diff file for the new "current state". */ 6172 bool fCreateDiffs = false; 6173 if (machine == pTask->pOldMachineState) 6174 fCreateDiffs = true; 6175 SafeIfaceArray<IMediumAttachment> sfaAttachments; 6176 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments)); 6177 if (FAILED(rc)) throw rc; 6178 /* Add all attachments (and there parents) of the different 6179 * machines to a worker list. */ 6180 for (size_t a = 0; a < sfaAttachments.size(); ++a) 6181 { 6182 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a]; 6183 DeviceType_T type; 6184 rc = pAtt->COMGETTER(Type)(&type); 6185 if (FAILED(rc)) throw rc; 6186 6187 /* Only harddisk's are of interest. */ 6188 if (type != DeviceType_HardDisk) 6189 continue; 6190 6191 /* Valid medium attached? */ 6192 ComPtr<IMedium> pSrcMedium; 6193 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam()); 6194 if (FAILED(rc)) throw rc; 6195 if (pSrcMedium.isNull()) 6196 continue; 6197 6198 /* Build up a child->parent list of this attachment. (Note: we are 6199 * not interested of any child's not attached to this VM. So this 6200 * will not create a full copy of the base/child relationship.) */ 6201 Machine::CloneVMTask::MediumTaskChain mtc; 6202 mtc.fCreateDiffs = fCreateDiffs; 6203 while(!pSrcMedium.isNull()) 6204 { 6205 /* Refresh the state so that the file size get read. */ 6206 MediumState_T e; 6207 rc = pSrcMedium->RefreshState(&e); 6208 if (FAILED(rc)) throw rc; 6209 LONG64 lSize; 6210 rc = pSrcMedium->COMGETTER(Size)(&lSize); 6211 if (FAILED(rc)) throw rc; 6212 6213 /* Save the current medium, for later cloning. */ 6214 Machine::CloneVMTask::MediumTask mt; 6215 mt.pMedium = pSrcMedium; 6216 mt.uSize = lSize; 6217 mtc.chain.append(mt); 6218 6219 /* Calculate progress data */ 6220 ++uCount; 6221 uTotalWeight += lSize; 6222 6223 /* Query next parent. */ 6224 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam()); 6225 if (FAILED(rc)) throw rc; 6226 }; 6227 pTask->llMedias.append(mtc); 6228 } 6229 Bstr bstrSrcSaveStatePath; 6230 rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam()); 6231 if (FAILED(rc)) throw rc; 6232 if (!bstrSrcSaveStatePath.isEmpty()) 6233 { 6234 Machine::CloneVMTask::SaveStateTask sst; 6235 sst.snapshotUuid = machine->getSnapshotId(); 6236 sst.strSaveStateFile = bstrSrcSaveStatePath; 6237 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &sst.cbSize); 6238 if (RT_FAILURE(vrc)) 6239 throw setError(VBOX_E_IPRT_ERROR, tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc); 6240 pTask->llSaveStateFiles.append(sst); 6241 ++uCount; 6242 uTotalWeight += sst.cbSize; 6243 } 6244 } 6245 6246 rc = pTask->pProgress.createObject(); 6247 if (FAILED(rc)) throw rc; 6248 rc = pTask->pProgress->init(getVirtualBox(), 6249 static_cast<IMachine*>(this) /* aInitiator */, 6250 Bstr(tr("Cloning Machine")).raw(), 6251 true /* fCancellable */, 6252 uCount, 6253 uTotalWeight, 6254 Bstr(tr("Initialize Cloning")).raw(), 6255 1); 6256 if (FAILED(rc)) throw rc; 6257 6258 int vrc = RTThreadCreate(NULL, 6259 Machine::cloneVMThread, 6260 static_cast<void*>(pTask), 6261 0, 6262 RTTHREADTYPE_MAIN_WORKER, 6263 0, 6264 "MachineClone"); 6265 if (RT_FAILURE(vrc)) 6266 throw setError(VBOX_E_IPRT_ERROR, "Could not create machine clone thread (%Rrc)", vrc); 6267 } 6268 catch (HRESULT rc2) 6269 { 6270 if (pTask) 6271 delete pTask; 6272 rc = rc2; 6273 } 6274 6275 if (SUCCEEDED(rc)) 6276 pTask->pProgress.queryInterfaceTo(pProgress); 6277 6278 LogFlowFuncLeave(); 6279 6280 return rc; 6281 } 6282 6283 /** 6284 * Static task wrapper passed to RTThreadCreate() in Machine::CloneTo() which then 6285 * calls Machine::cloneVMTaskWorker() on the actual machine object. 6286 * @param Thread 6287 * @param pvUser 6288 * @return 6289 */ 6290 /* static */ 6291 DECLCALLBACK(int) Machine::cloneVMThread(RTTHREAD /* Thread */, void *pvUser) 6292 { 6293 LogFlowFuncEnter(); 6294 6295 CloneVMTask *pTask = static_cast<CloneVMTask*>(pvUser); 6296 AssertReturn(pTask, VERR_INVALID_POINTER); 6297 AssertReturn(pTask->pSrcMachine, VERR_INVALID_POINTER); 6298 AssertReturn(pTask->pTrgMachine, VERR_INVALID_POINTER); 6299 AssertReturn(pTask->pProgress, VERR_INVALID_POINTER); 6300 6301 HRESULT rc = pTask->pSrcMachine->cloneVMTaskWorker(pTask); 6302 pTask->pProgress->notifyComplete(rc); 6303 6304 delete pTask; 6305 6306 LogFlowFuncLeave(); 6307 6308 return VINF_SUCCESS; 6309 } 6310 6311 HRESULT Machine::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const 116 HRESULT MachineCloneVMPrivate::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const 6312 117 { 6313 118 HRESULT rc = S_OK; … … 6333 138 } 6334 139 6335 settings::Snapshot Machine ::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const140 settings::Snapshot MachineCloneVMPrivate::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const 6336 141 { 6337 142 settings::SnapshotsList::const_iterator it; … … 6346 151 } 6347 152 6348 void Machine ::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const153 void MachineCloneVMPrivate::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const 6349 154 { 6350 155 settings::StorageControllersList::iterator it3; … … 6368 173 } 6369 174 6370 void Machine ::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const175 void MachineCloneVMPrivate::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const 6371 176 { 6372 177 settings::SnapshotsList::iterator it; … … 6381 186 } 6382 187 6383 void Machine ::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const188 void MachineCloneVMPrivate::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const 6384 189 { 6385 190 settings::SnapshotsList::iterator it; … … 6394 199 6395 200 /* static */ 6396 int Machine ::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser)201 int MachineCloneVMPrivate::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser) 6397 202 { 6398 203 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser); … … 6410 215 } 6411 216 6412 /** 6413 * Task thread implementation for Machine::CloneTo(), called from Machine::cloneVMThread(). 6414 * @param task 6415 * @return 6416 */ 6417 HRESULT Machine::cloneVMTaskWorker(CloneVMTask *pTask) 6418 { 6419 AutoCaller autoCaller(this); 217 218 // The public class 219 ///////////////////////////////////////////////////////////////////////////// 220 221 MachineCloneVM::MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode, bool fFullClone) 222 : d_ptr(new MachineCloneVMPrivate(this, pSrcMachine, pTrgMachine, mode, fFullClone)) 223 { 224 } 225 226 MachineCloneVM::~MachineCloneVM() 227 { 228 delete d_ptr; 229 } 230 231 HRESULT MachineCloneVM::start(IProgress **pProgress) 232 { 233 DPTR(MachineCloneVM); 234 ComObjPtr<Machine> &p = d->p; 235 236 HRESULT rc; 237 try 238 { 239 /* Lock the target machine early (so nobody mess around with it in the meantime). */ 240 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS); 241 242 if (d->pSrcMachine->isSnapshotMachine()) 243 d->snapshotId = d->pSrcMachine->getSnapshotId(); 244 245 /* Add the current machine and all snapshot machines below this machine 246 * in a list for further processing. */ 247 RTCList< ComObjPtr<Machine> > machineList; 248 249 /* Include current state? */ 250 if ( d->mode == CloneMode_MachineState) 251 // || d->mode == CloneMode_AllStates) 252 machineList.append(d->pSrcMachine); 253 /* Should be done a depth copy with all child snapshots? */ 254 if ( d->mode == CloneMode_MachineAndChildStates 255 || d->mode == CloneMode_AllStates) 256 { 257 ULONG cSnapshots = 0; 258 rc = d->pSrcMachine->COMGETTER(SnapshotCount)(&cSnapshots); 259 if (FAILED(rc)) throw rc; 260 if (cSnapshots > 0) 261 { 262 Utf8Str id; 263 if ( d->mode == CloneMode_MachineAndChildStates 264 && !d->snapshotId.isEmpty()) 265 id = d->snapshotId.toString(); 266 ComPtr<ISnapshot> pSnapshot; 267 rc = d->pSrcMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam()); 268 if (FAILED(rc)) throw rc; 269 rc = d->cloneCreateMachineList(pSnapshot, machineList); 270 if (FAILED(rc)) throw rc; 271 rc = pSnapshot->COMGETTER(Machine)(d->pOldMachineState.asOutParam()); 272 if (FAILED(rc)) throw rc; 273 } 274 } 275 276 /* Go over every machine and walk over every attachment this machine has. */ 277 ULONG uCount = 2; /* One init task and the machine creation. */ 278 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */ 279 for (size_t i = 0; i < machineList.size(); ++i) 280 { 281 ComObjPtr<Machine> machine = machineList.at(i); 282 /* If this is the Snapshot Machine we want to clone, we need to 283 * create a new diff file for the new "current state". */ 284 bool fCreateDiffs = false; 285 if (machine == d->pOldMachineState) 286 fCreateDiffs = true; 287 SafeIfaceArray<IMediumAttachment> sfaAttachments; 288 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments)); 289 if (FAILED(rc)) throw rc; 290 /* Add all attachments (and there parents) of the different 291 * machines to a worker list. */ 292 for (size_t a = 0; a < sfaAttachments.size(); ++a) 293 { 294 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a]; 295 DeviceType_T type; 296 rc = pAtt->COMGETTER(Type)(&type); 297 if (FAILED(rc)) throw rc; 298 299 /* Only harddisk's are of interest. */ 300 if (type != DeviceType_HardDisk) 301 continue; 302 303 /* Valid medium attached? */ 304 ComPtr<IMedium> pSrcMedium; 305 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam()); 306 if (FAILED(rc)) throw rc; 307 if (pSrcMedium.isNull()) 308 continue; 309 310 /* Build up a child->parent list of this attachment. (Note: we are 311 * not interested of any child's not attached to this VM. So this 312 * will not create a full copy of the base/child relationship.) */ 313 MEDIUMTASKCHAIN mtc; 314 mtc.fCreateDiffs = fCreateDiffs; 315 while(!pSrcMedium.isNull()) 316 { 317 /* Refresh the state so that the file size get read. */ 318 MediumState_T e; 319 rc = pSrcMedium->RefreshState(&e); 320 if (FAILED(rc)) throw rc; 321 LONG64 lSize; 322 rc = pSrcMedium->COMGETTER(Size)(&lSize); 323 if (FAILED(rc)) throw rc; 324 325 /* Save the current medium, for later cloning. */ 326 MEDIUMTASK mt; 327 mt.pMedium = pSrcMedium; 328 mt.uSize = lSize; 329 mtc.chain.append(mt); 330 331 /* Calculate progress data */ 332 ++uCount; 333 uTotalWeight += lSize; 334 335 /* Query next parent. */ 336 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam()); 337 if (FAILED(rc)) throw rc; 338 }; 339 d->llMedias.append(mtc); 340 } 341 Bstr bstrSrcSaveStatePath; 342 rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam()); 343 if (FAILED(rc)) throw rc; 344 if (!bstrSrcSaveStatePath.isEmpty()) 345 { 346 SAVESTATETASK sst; 347 sst.snapshotUuid = machine->getSnapshotId(); 348 sst.strSaveStateFile = bstrSrcSaveStatePath; 349 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &sst.cbSize); 350 if (RT_FAILURE(vrc)) 351 throw p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc); 352 d->llSaveStateFiles.append(sst); 353 ++uCount; 354 uTotalWeight += sst.cbSize; 355 } 356 } 357 358 rc = d->pProgress.createObject(); 359 if (FAILED(rc)) throw rc; 360 rc = d->pProgress->init(p->getVirtualBox(), 361 static_cast<IMachine*>(d->pSrcMachine) /* aInitiator */, 362 Bstr(p->tr("Cloning Machine")).raw(), 363 true /* fCancellable */, 364 uCount, 365 uTotalWeight, 366 Bstr(p->tr("Initialize Cloning")).raw(), 367 1); 368 if (FAILED(rc)) throw rc; 369 370 int vrc = d->startWorker(); 371 372 if (RT_FAILURE(vrc)) 373 p->setError(VBOX_E_IPRT_ERROR, "Could not create machine clone thread (%Rrc)", vrc); 374 } 375 catch (HRESULT rc2) 376 { 377 rc = rc2; 378 } 379 380 if (SUCCEEDED(rc)) 381 d->pProgress.queryInterfaceTo(pProgress); 382 383 return rc; 384 } 385 386 HRESULT MachineCloneVM::run() 387 { 388 DPTR(MachineCloneVM); 389 ComObjPtr<Machine> &p = d->p; 390 391 AutoCaller autoCaller(p); 6420 392 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6421 393 6422 AutoReadLock srcLock( thisCOMMA_LOCKVAL_SRC_POS);6423 AutoWriteLock trgLock( pTask->pTrgMachine COMMA_LOCKVAL_SRC_POS);394 AutoReadLock srcLock(p COMMA_LOCKVAL_SRC_POS); 395 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS); 6424 396 6425 397 MultiResult rc = S_OK; … … 6437 409 6438 410 /* Where should all the media go? */ 6439 Utf8Str strTrgMachineFolder = pTask->pTrgMachine->getSettingsFileFull();411 Utf8Str strTrgMachineFolder = d->pTrgMachine->getSettingsFileFull(); 6440 412 strTrgMachineFolder.stripFilename(); 6441 413 … … 6447 419 * configuration dataset. */ 6448 420 settings::MachineConfigFile *pTrgMCF = new settings::MachineConfigFile(0); 6449 *pTrgMCF = * mData->pMachineConfigFile;421 *pTrgMCF = *d->pSrcMachine->mData->pMachineConfigFile; 6450 422 6451 423 /* Reset media registry. */ … … 6454 426 * with the stuff from the snapshot. */ 6455 427 settings::Snapshot sn; 6456 if (! pTask->snapshotId.isEmpty())6457 sn = cloneFindSnapshot(pTrgMCF, pTrgMCF->llFirstSnapshot, pTask->snapshotId);6458 6459 if ( pTask->mode == CloneMode_MachineState)428 if (!d->snapshotId.isEmpty()) 429 sn = d->cloneFindSnapshot(pTrgMCF, pTrgMCF->llFirstSnapshot, d->snapshotId); 430 431 if (d->mode == CloneMode_MachineState) 6460 432 { 6461 433 if (!sn.uuid.isEmpty()) … … 6469 441 pTrgMCF->uuidCurrentSnapshot.clear(); 6470 442 }else 6471 if ( pTask->mode == CloneMode_MachineAndChildStates443 if ( d->mode == CloneMode_MachineAndChildStates 6472 444 && !sn.uuid.isEmpty()) 6473 445 { … … 6490 462 pTrgMCF->fCurrentStateModified = true; 6491 463 /* Set the new name. */ 6492 pTrgMCF->machineUserData.strName = pTask->pTrgMachine->mUserData->s.strName;6493 pTrgMCF->uuid = pTask->pTrgMachine->mData->mUuid;464 pTrgMCF->machineUserData.strName = d->pTrgMachine->mUserData->s.strName; 465 pTrgMCF->uuid = d->pTrgMachine->mData->mUuid; 6494 466 6495 467 Bstr bstrSrcSnapshotFolder; 6496 rc = pTask->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());468 rc = d->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam()); 6497 469 if (FAILED(rc)) throw rc; 6498 470 /* The absolute name of the snapshot folder. */ … … 6506 478 typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair; 6507 479 TStrMediumMap map; 6508 for (size_t i = 0; i < pTask->llMedias.size(); ++i)6509 { 6510 const M achine::CloneVMTask::MediumTaskChain &mtc = pTask->llMedias.at(i);480 for (size_t i = 0; i < d->llMedias.size(); ++i) 481 { 482 const MEDIUMTASKCHAIN &mtc = d->llMedias.at(i); 6511 483 ComObjPtr<Medium> pNewParent; 6512 484 for (size_t a = mtc.chain.size(); a > 0; --a) 6513 485 { 6514 const M achine::CloneVMTask::MediumTask&mt = mtc.chain.at(a - 1);486 const MEDIUMTASK &mt = mtc.chain.at(a - 1); 6515 487 ComPtr<IMedium> pMedium = mt.pMedium; 6516 488 … … 6519 491 if (FAILED(rc)) throw rc; 6520 492 6521 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize);493 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize); 6522 494 if (FAILED(rc)) throw rc; 6523 495 … … 6557 529 6558 530 Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw()); 6559 rc = pTarget->init( mParent,531 rc = pTarget->init(p->mParent, 6560 532 Utf8Str(bstrSrcFormat), 6561 533 strFile, 6562 pTask->pTrgMachine->mData->mUuid, /* media registry */6563 NULL 534 d->pTrgMachine->mData->mUuid, /* media registry */ 535 NULL /* llRegistriesThatNeedSaving */); 6564 536 if (FAILED(rc)) throw rc; 6565 537 … … 6574 546 /* Wait until the asynchrony process has finished. */ 6575 547 srcLock.release(); 6576 rc = pTask->pProgress->WaitForAsyncProgressCompletion(progress2);548 rc = d->pProgress->WaitForAsyncProgressCompletion(progress2); 6577 549 srcLock.acquire(); 6578 550 if (FAILED(rc)) throw rc; … … 6587 559 * retrieve the error info from there, or it'll be lost. */ 6588 560 ProgressErrorInfo info(progress2); 6589 throw setError(iRc, Utf8Str(info.getText()).c_str());561 throw p->setError(iRc, Utf8Str(info.getText()).c_str()); 6590 562 } 6591 563 … … 6609 581 ComObjPtr<Medium> diff; 6610 582 diff.createObject(); 6611 rc = diff->init( mParent,583 rc = diff->init(p->mParent, 6612 584 pNewParent->getPreferredDiffFormat(), 6613 585 strTrgSnapshotFolder, 6614 pTask->pTrgMachine->mData->mUuid,586 d->pTrgMachine->mData->mUuid, 6615 587 NULL); // pllRegistriesThatNeedSaving 6616 588 if (FAILED(rc)) throw rc; … … 6640 612 /* We have to patch the configuration, so it contains the new 6641 613 * medium uuid instead of the old one. */ 6642 cloneUpdateStorageLists(pTrgMCF->storageMachine.llStorageControllers, bstrSrcId, bstrTrgId);6643 cloneUpdateSnapshotStorageLists(pTrgMCF->llFirstSnapshot, bstrSrcId, bstrTrgId);614 d->cloneUpdateStorageLists(pTrgMCF->storageMachine.llStorageControllers, bstrSrcId, bstrTrgId); 615 d->cloneUpdateSnapshotStorageLists(pTrgMCF->llFirstSnapshot, bstrSrcId, bstrTrgId); 6644 616 } 6645 617 /* Clone all save state files. */ 6646 for (size_t i = 0; i < pTask->llSaveStateFiles.size(); ++i)6647 { 6648 Machine::CloneVMTask::SaveStateTask sst = pTask->llSaveStateFiles.at(i);618 for (size_t i = 0; i < d->llSaveStateFiles.size(); ++i) 619 { 620 SAVESTATETASK sst = d->llSaveStateFiles.at(i); 6649 621 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str())); 6650 622 6651 623 /* Move to next sub-operation. */ 6652 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize);624 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize); 6653 625 if (FAILED(rc)) throw rc; 6654 626 /* Copy the file only if it was not copied already. */ 6655 627 if (!newFiles.contains(strTrgSaveState.c_str())) 6656 628 { 6657 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, cloneCopyStateFileProgress, &pTask->pProgress);629 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, MachineCloneVMPrivate::cloneCopyStateFileProgress, &d->pProgress); 6658 630 if (RT_FAILURE(vrc)) 6659 throw setError(VBOX_E_IPRT_ERROR,6660 tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);631 throw p->setError(VBOX_E_IPRT_ERROR, 632 p->tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc); 6661 633 newFiles.append(strTrgSaveState); 6662 634 } … … 6666 638 pTrgMCF->strStateFile = strTrgSaveState; 6667 639 else 6668 cloneUpdateStateFile(pTrgMCF->llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);640 d->cloneUpdateStateFile(pTrgMCF->llFirstSnapshot, sst.snapshotUuid, strTrgSaveState); 6669 641 } 6670 642 6671 643 if (false) 6672 // if (! pTask->pOldMachineState.isNull())644 // if (!d->pOldMachineState.isNull()) 6673 645 { 6674 646 SafeIfaceArray<IMediumAttachment> sfaAttachments; 6675 rc = pTask->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));647 rc = d->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments)); 6676 648 if (FAILED(rc)) throw rc; 6677 649 for (size_t a = 0; a < sfaAttachments.size(); ++a) … … 6722 694 6723 695 { 6724 rc = pTask->pProgress->SetNextOperation(BstrFmt(tr("Create Machine Clone '%s' ..."), pTrgMCF->machineUserData.strName.c_str()).raw(), 1);696 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Create Machine Clone '%s' ..."), pTrgMCF->machineUserData.strName.c_str()).raw(), 1); 6725 697 if (FAILED(rc)) throw rc; 6726 698 /* After modifying the new machine config, we can copy the stuff 6727 699 * over to the new machine. The machine have to be mutable for 6728 700 * this. */ 6729 rc = pTask->pTrgMachine->checkStateDependency(MutableStateDep);6730 if (FAILED(rc)) throw rc; 6731 rc = pTask->pTrgMachine->loadMachineDataFromSettings(*pTrgMCF,6732 &pTask->pTrgMachine->mData->mUuid);701 rc = d->pTrgMachine->checkStateDependency(p->MutableStateDep); 702 if (FAILED(rc)) throw rc; 703 rc = d->pTrgMachine->loadMachineDataFromSettings(*pTrgMCF, 704 &d->pTrgMachine->mData->mUuid); 6733 705 if (FAILED(rc)) throw rc; 6734 706 } … … 6740 712 // for (size_t i = 0; i < newBaseMedias.size(); ++i) 6741 713 // { 6742 // rc = newBaseMedias.at(i)->addRegistry( pTask->pTrgMachine->mData->mUuid, true /* fRecursive */);714 // rc = newBaseMedias.at(i)->addRegistry(d->pTrgMachine->mData->mUuid, true /* fRecursive */); 6743 715 // if (FAILED(rc)) throw rc; 6744 716 // } 6745 717 6746 718 /* Now save the new configuration to disk. */ 6747 rc = pTask->pTrgMachine->SaveSettings();719 rc = d->pTrgMachine->SaveSettings(); 6748 720 if (FAILED(rc)) throw rc; 6749 721 } … … 6766 738 vrc = RTFileDelete(newFiles.at(i).c_str()); 6767 739 if (RT_FAILURE(vrc)) 6768 rc = setError(VBOX_E_IPRT_ERROR,tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);740 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc); 6769 741 } 6770 742 /* Delete all already created medias. (Reverse, cause there could be … … 6787 759 vrc = RTFileDelete(strLoc.c_str()); 6788 760 if (RT_FAILURE(vrc)) 6789 rc = setError(VBOX_E_IPRT_ERROR,tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);761 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc); 6790 762 } 6791 763 } … … 6797 769 } 6798 770 6799 // public methods for internal purposes 6800 ///////////////////////////////////////////////////////////////////////////// 6801 6802 /** 6803 * Adds the given IsModified_* flag to the dirty flags of the machine. 6804 * This must be called either during loadSettings or under the machine write lock. 6805 * @param fl 6806 */ 6807 void Machine::setModified(uint32_t fl) 6808 { 6809 mData->flModifications |= fl; 6810 } 6811 6812 /** 6813 * Adds the given IsModified_* flag to the dirty flags of the machine, taking 6814 * care of the write locking. 6815 * 6816 * @param fModifications The flag to add. 6817 */ 6818 void Machine::setModifiedLock(uint32_t fModification) 6819 { 6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6821 mData->flModifications |= fModification; 6822 } 6823 6824 /** 6825 * Saves the registry entry of this machine to the given configuration node. 6826 * 6827 * @param aEntryNode Node to save the registry entry to. 6828 * 6829 * @note locks this object for reading. 6830 */ 6831 HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data) 6832 { 6833 AutoLimitedCaller autoCaller(this); 6834 AssertComRCReturnRC(autoCaller.rc()); 6835 6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6837 6838 data.uuid = mData->mUuid; 6839 data.strSettingsFile = mData->m_strConfigFile; 6840 6841 return S_OK; 6842 } 6843 6844 /** 6845 * Calculates the absolute path of the given path taking the directory of the 6846 * machine settings file as the current directory. 6847 * 6848 * @param aPath Path to calculate the absolute path for. 6849 * @param aResult Where to put the result (used only on success, can be the 6850 * same Utf8Str instance as passed in @a aPath). 6851 * @return IPRT result. 6852 * 6853 * @note Locks this object for reading. 6854 */ 6855 int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult) 6856 { 6857 AutoCaller autoCaller(this); 6858 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 6859 6860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6861 6862 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE); 6863 6864 Utf8Str strSettingsDir = mData->m_strConfigFileFull; 6865 6866 strSettingsDir.stripFilename(); 6867 char folder[RTPATH_MAX]; 6868 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder)); 6869 if (RT_SUCCESS(vrc)) 6870 aResult = folder; 6871 6872 return vrc; 6873 } 6874 6875 /** 6876 * Copies strSource to strTarget, making it relative to the machine folder 6877 * if it is a subdirectory thereof, or simply copying it otherwise. 6878 * 6879 * @param strSource Path to evaluate and copy. 6880 * @param strTarget Buffer to receive target path. 6881 * 6882 * @note Locks this object for reading. 6883 */ 6884 void Machine::copyPathRelativeToMachine(const Utf8Str &strSource, 6885 Utf8Str &strTarget) 6886 { 6887 AutoCaller autoCaller(this); 6888 AssertComRCReturn(autoCaller.rc(), (void)0); 6889 6890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6891 6892 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty()); 6893 // use strTarget as a temporary buffer to hold the machine settings dir 6894 strTarget = mData->m_strConfigFileFull; 6895 strTarget.stripFilename(); 6896 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str())) 6897 { 6898 // is relative: then append what's left 6899 strTarget = strSource.substr(strTarget.length() + 1); // skip '/' 6900 // for empty paths (only possible for subdirs) use "." to avoid 6901 // triggering default settings for not present config attributes. 6902 if (strTarget.isEmpty()) 6903 strTarget = "."; 6904 } 6905 else 6906 // is not relative: then overwrite 6907 strTarget = strSource; 6908 } 6909 6910 /** 6911 * Returns the full path to the machine's log folder in the 6912 * \a aLogFolder argument. 6913 */ 6914 void Machine::getLogFolder(Utf8Str &aLogFolder) 6915 { 6916 AutoCaller autoCaller(this); 6917 AssertComRCReturnVoid(autoCaller.rc()); 6918 6919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6920 6921 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox 6922 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname 6923 aLogFolder.append(RTPATH_DELIMITER); 6924 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs 6925 } 6926 6927 /** 6928 * Returns the full path to the machine's log file for an given index. 6929 */ 6930 Utf8Str Machine::queryLogFilename(ULONG idx) 6931 { 6932 Utf8Str logFolder; 6933 getLogFolder(logFolder); 6934 Assert(logFolder.length()); 6935 Utf8Str log; 6936 if (idx == 0) 6937 log = Utf8StrFmt("%s%cVBox.log", 6938 logFolder.c_str(), RTPATH_DELIMITER); 6939 else 6940 log = Utf8StrFmt("%s%cVBox.log.%d", 6941 logFolder.c_str(), RTPATH_DELIMITER, idx); 6942 return log; 6943 } 6944 6945 /** 6946 * Composes a unique saved state filename based on the current system time. The filename is 6947 * granular to the second so this will work so long as no more than one snapshot is taken on 6948 * a machine per second. 6949 * 6950 * Before version 4.1, we used this formula for saved state files: 6951 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw()) 6952 * which no longer works because saved state files can now be shared between the saved state of the 6953 * "saved" machine and an online snapshot, and the following would cause problems: 6954 * 1) save machine 6955 * 2) create online snapshot from that machine state --> reusing saved state file 6956 * 3) save machine again --> filename would be reused, breaking the online snapshot 6957 * 6958 * So instead we now use a timestamp. 6959 * 6960 * @param str 6961 */ 6962 void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath) 6963 { 6964 AutoCaller autoCaller(this); 6965 AssertComRCReturnVoid(autoCaller.rc()); 6966 6967 { 6968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6969 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath); 6970 } 6971 6972 RTTIMESPEC ts; 6973 RTTimeNow(&ts); 6974 RTTIME time; 6975 RTTimeExplode(&time, &ts); 6976 6977 strStateFilePath += RTPATH_DELIMITER; 6978 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav", 6979 time.i32Year, time.u8Month, time.u8MonthDay, 6980 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond); 6981 } 6982 6983 /** 6984 * @note Locks this object for writing, calls the client process 6985 * (inside the lock). 6986 */ 6987 HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, 6988 const Utf8Str &strType, 6989 const Utf8Str &strEnvironment, 6990 ProgressProxy *aProgress) 6991 { 6992 LogFlowThisFuncEnter(); 6993 6994 AssertReturn(aControl, E_FAIL); 6995 AssertReturn(aProgress, E_FAIL); 6996 6997 AutoCaller autoCaller(this); 6998 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6999 7000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7001 7002 if (!mData->mRegistered) 7003 return setError(E_UNEXPECTED, 7004 tr("The machine '%s' is not registered"), 7005 mUserData->s.strName.c_str()); 7006 7007 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 7008 7009 if ( mData->mSession.mState == SessionState_Locked 7010 || mData->mSession.mState == SessionState_Spawning 7011 || mData->mSession.mState == SessionState_Unlocking) 7012 return setError(VBOX_E_INVALID_OBJECT_STATE, 7013 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"), 7014 mUserData->s.strName.c_str()); 7015 7016 /* may not be busy */ 7017 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 7018 7019 /* get the path to the executable */ 7020 char szPath[RTPATH_MAX]; 7021 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1); 7022 size_t sz = strlen(szPath); 7023 szPath[sz++] = RTPATH_DELIMITER; 7024 szPath[sz] = 0; 7025 char *cmd = szPath + sz; 7026 sz = RTPATH_MAX - sz; 7027 7028 int vrc = VINF_SUCCESS; 7029 RTPROCESS pid = NIL_RTPROCESS; 7030 7031 RTENV env = RTENV_DEFAULT; 7032 7033 if (!strEnvironment.isEmpty()) 7034 { 7035 char *newEnvStr = NULL; 7036 7037 do 7038 { 7039 /* clone the current environment */ 7040 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT); 7041 AssertRCBreakStmt(vrc2, vrc = vrc2); 7042 7043 newEnvStr = RTStrDup(strEnvironment.c_str()); 7044 AssertPtrBreakStmt(newEnvStr, vrc = vrc2); 7045 7046 /* put new variables to the environment 7047 * (ignore empty variable names here since RTEnv API 7048 * intentionally doesn't do that) */ 7049 char *var = newEnvStr; 7050 for (char *p = newEnvStr; *p; ++p) 7051 { 7052 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\')) 7053 { 7054 *p = '\0'; 7055 if (*var) 7056 { 7057 char *val = strchr(var, '='); 7058 if (val) 7059 { 7060 *val++ = '\0'; 7061 vrc2 = RTEnvSetEx(env, var, val); 7062 } 7063 else 7064 vrc2 = RTEnvUnsetEx(env, var); 7065 if (RT_FAILURE(vrc2)) 7066 break; 7067 } 7068 var = p + 1; 7069 } 7070 } 7071 if (RT_SUCCESS(vrc2) && *var) 7072 vrc2 = RTEnvPutEx(env, var); 7073 7074 AssertRCBreakStmt(vrc2, vrc = vrc2); 7075 } 7076 while (0); 7077 7078 if (newEnvStr != NULL) 7079 RTStrFree(newEnvStr); 7080 } 7081 7082 /* Qt is default */ 7083 #ifdef VBOX_WITH_QTGUI 7084 if (strType == "gui" || strType == "GUI/Qt") 7085 { 7086 # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */ 7087 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"; 7088 # else 7089 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE; 7090 # endif 7091 Assert(sz >= sizeof(VirtualBox_exe)); 7092 strcpy(cmd, VirtualBox_exe); 7093 7094 Utf8Str idStr = mData->mUuid.toString(); 7095 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 }; 7096 vrc = RTProcCreate(szPath, args, env, 0, &pid); 7097 } 7098 #else /* !VBOX_WITH_QTGUI */ 7099 if (0) 7100 ; 7101 #endif /* VBOX_WITH_QTGUI */ 7102 7103 else 7104 7105 #ifdef VBOX_WITH_VBOXSDL 7106 if (strType == "sdl" || strType == "GUI/SDL") 7107 { 7108 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE; 7109 Assert(sz >= sizeof(VBoxSDL_exe)); 7110 strcpy(cmd, VBoxSDL_exe); 7111 7112 Utf8Str idStr = mData->mUuid.toString(); 7113 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 }; 7114 fprintf(stderr, "SDL=%s\n", szPath); 7115 vrc = RTProcCreate(szPath, args, env, 0, &pid); 7116 } 7117 #else /* !VBOX_WITH_VBOXSDL */ 7118 if (0) 7119 ; 7120 #endif /* !VBOX_WITH_VBOXSDL */ 7121 7122 else 7123 7124 #ifdef VBOX_WITH_HEADLESS 7125 if ( strType == "headless" 7126 || strType == "capture" 7127 || strType == "vrdp" /* Deprecated. Same as headless. */ 7128 ) 7129 { 7130 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE, 7131 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional, 7132 * and a VM works even if the server has not been installed. 7133 * So in 4.0 the "headless" behavior remains the same for default VBox installations. 7134 * Only if a VRDE has been installed and the VM enables it, the "headless" will work 7135 * differently in 4.0 and 3.x. 7136 */ 7137 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE; 7138 Assert(sz >= sizeof(VBoxHeadless_exe)); 7139 strcpy(cmd, VBoxHeadless_exe); 7140 7141 Utf8Str idStr = mData->mUuid.toString(); 7142 /* Leave space for "--capture" arg. */ 7143 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), 7144 "--startvm", idStr.c_str(), 7145 "--vrde", "config", 7146 0, /* For "--capture". */ 7147 0 }; 7148 if (strType == "capture") 7149 { 7150 unsigned pos = RT_ELEMENTS(args) - 2; 7151 args[pos] = "--capture"; 7152 } 7153 vrc = RTProcCreate(szPath, args, env, 0, &pid); 7154 } 7155 #else /* !VBOX_WITH_HEADLESS */ 7156 if (0) 7157 ; 7158 #endif /* !VBOX_WITH_HEADLESS */ 7159 else 7160 { 7161 RTEnvDestroy(env); 7162 return setError(E_INVALIDARG, 7163 tr("Invalid session type: '%s'"), 7164 strType.c_str()); 7165 } 7166 7167 RTEnvDestroy(env); 7168 7169 if (RT_FAILURE(vrc)) 7170 return setError(VBOX_E_IPRT_ERROR, 7171 tr("Could not launch a process for the machine '%s' (%Rrc)"), 7172 mUserData->s.strName.c_str(), vrc); 7173 7174 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid)); 7175 7176 /* 7177 * Note that we don't leave the lock here before calling the client, 7178 * because it doesn't need to call us back if called with a NULL argument. 7179 * Leaving the lock here is dangerous because we didn't prepare the 7180 * launch data yet, but the client we've just started may happen to be 7181 * too fast and call openSession() that will fail (because of PID, etc.), 7182 * so that the Machine will never get out of the Spawning session state. 7183 */ 7184 7185 /* inform the session that it will be a remote one */ 7186 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n")); 7187 HRESULT rc = aControl->AssignMachine(NULL); 7188 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc)); 7189 7190 if (FAILED(rc)) 7191 { 7192 /* restore the session state */ 7193 mData->mSession.mState = SessionState_Unlocked; 7194 /* The failure may occur w/o any error info (from RPC), so provide one */ 7195 return setError(VBOX_E_VM_ERROR, 7196 tr("Failed to assign the machine to the session (%Rrc)"), rc); 7197 } 7198 7199 /* attach launch data to the machine */ 7200 Assert(mData->mSession.mPid == NIL_RTPROCESS); 7201 mData->mSession.mRemoteControls.push_back (aControl); 7202 mData->mSession.mProgress = aProgress; 7203 mData->mSession.mPid = pid; 7204 mData->mSession.mState = SessionState_Spawning; 7205 mData->mSession.mType = strType; 7206 7207 LogFlowThisFuncLeave(); 7208 return S_OK; 7209 } 7210 7211 /** 7212 * Returns @c true if the given machine has an open direct session and returns 7213 * the session machine instance and additional session data (on some platforms) 7214 * if so. 7215 * 7216 * Note that when the method returns @c false, the arguments remain unchanged. 7217 * 7218 * @param aMachine Session machine object. 7219 * @param aControl Direct session control object (optional). 7220 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional). 7221 * 7222 * @note locks this object for reading. 7223 */ 7224 #if defined(RT_OS_WINDOWS) 7225 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 7226 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 7227 HANDLE *aIPCSem /*= NULL*/, 7228 bool aAllowClosing /*= false*/) 7229 #elif defined(RT_OS_OS2) 7230 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 7231 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 7232 HMTX *aIPCSem /*= NULL*/, 7233 bool aAllowClosing /*= false*/) 7234 #else 7235 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 7236 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 7237 bool aAllowClosing /*= false*/) 7238 #endif 7239 { 7240 AutoLimitedCaller autoCaller(this); 7241 AssertComRCReturn(autoCaller.rc(), false); 7242 7243 /* just return false for inaccessible machines */ 7244 if (autoCaller.state() != Ready) 7245 return false; 7246 7247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7248 7249 if ( mData->mSession.mState == SessionState_Locked 7250 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking) 7251 ) 7252 { 7253 AssertReturn(!mData->mSession.mMachine.isNull(), false); 7254 7255 aMachine = mData->mSession.mMachine; 7256 7257 if (aControl != NULL) 7258 *aControl = mData->mSession.mDirectControl; 7259 7260 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 7261 /* Additional session data */ 7262 if (aIPCSem != NULL) 7263 *aIPCSem = aMachine->mIPCSem; 7264 #endif 7265 return true; 7266 } 7267 7268 return false; 7269 } 7270 7271 /** 7272 * Returns @c true if the given machine has an spawning direct session and 7273 * returns and additional session data (on some platforms) if so. 7274 * 7275 * Note that when the method returns @c false, the arguments remain unchanged. 7276 * 7277 * @param aPID PID of the spawned direct session process. 7278 * 7279 * @note locks this object for reading. 7280 */ 7281 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 7282 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/) 7283 #else 7284 bool Machine::isSessionSpawning() 7285 #endif 7286 { 7287 AutoLimitedCaller autoCaller(this); 7288 AssertComRCReturn(autoCaller.rc(), false); 7289 7290 /* just return false for inaccessible machines */ 7291 if (autoCaller.state() != Ready) 7292 return false; 7293 7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7295 7296 if (mData->mSession.mState == SessionState_Spawning) 7297 { 7298 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 7299 /* Additional session data */ 7300 if (aPID != NULL) 7301 { 7302 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false); 7303 *aPID = mData->mSession.mPid; 7304 } 7305 #endif 7306 return true; 7307 } 7308 7309 return false; 7310 } 7311 7312 /** 7313 * Called from the client watcher thread to check for unexpected client process 7314 * death during Session_Spawning state (e.g. before it successfully opened a 7315 * direct session). 7316 * 7317 * On Win32 and on OS/2, this method is called only when we've got the 7318 * direct client's process termination notification, so it always returns @c 7319 * true. 7320 * 7321 * On other platforms, this method returns @c true if the client process is 7322 * terminated and @c false if it's still alive. 7323 * 7324 * @note Locks this object for writing. 7325 */ 7326 bool Machine::checkForSpawnFailure() 7327 { 7328 AutoCaller autoCaller(this); 7329 if (!autoCaller.isOk()) 7330 { 7331 /* nothing to do */ 7332 LogFlowThisFunc(("Already uninitialized!\n")); 7333 return true; 7334 } 7335 7336 /* VirtualBox::addProcessToReap() needs a write lock */ 7337 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); 7338 7339 if (mData->mSession.mState != SessionState_Spawning) 7340 { 7341 /* nothing to do */ 7342 LogFlowThisFunc(("Not spawning any more!\n")); 7343 return true; 7344 } 7345 7346 HRESULT rc = S_OK; 7347 7348 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 7349 7350 /* the process was already unexpectedly terminated, we just need to set an 7351 * error and finalize session spawning */ 7352 rc = setError(E_FAIL, 7353 tr("The virtual machine '%s' has terminated unexpectedly during startup"), 7354 getName().c_str()); 7355 #else 7356 7357 /* PID not yet initialized, skip check. */ 7358 if (mData->mSession.mPid == NIL_RTPROCESS) 7359 return false; 7360 7361 RTPROCSTATUS status; 7362 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK, 7363 &status); 7364 7365 if (vrc != VERR_PROCESS_RUNNING) 7366 { 7367 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL) 7368 rc = setError(E_FAIL, 7369 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"), 7370 getName().c_str(), status.iStatus); 7371 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL) 7372 rc = setError(E_FAIL, 7373 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"), 7374 getName().c_str(), status.iStatus); 7375 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND) 7376 rc = setError(E_FAIL, 7377 tr("The virtual machine '%s' has terminated abnormally"), 7378 getName().c_str(), status.iStatus); 7379 else 7380 rc = setError(E_FAIL, 7381 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"), 7382 getName().c_str(), rc); 7383 } 7384 7385 #endif 7386 7387 if (FAILED(rc)) 7388 { 7389 /* Close the remote session, remove the remote control from the list 7390 * and reset session state to Closed (@note keep the code in sync with 7391 * the relevant part in checkForSpawnFailure()). */ 7392 7393 Assert(mData->mSession.mRemoteControls.size() == 1); 7394 if (mData->mSession.mRemoteControls.size() == 1) 7395 { 7396 ErrorInfoKeeper eik; 7397 mData->mSession.mRemoteControls.front()->Uninitialize(); 7398 } 7399 7400 mData->mSession.mRemoteControls.clear(); 7401 mData->mSession.mState = SessionState_Unlocked; 7402 7403 /* finalize the progress after setting the state */ 7404 if (!mData->mSession.mProgress.isNull()) 7405 { 7406 mData->mSession.mProgress->notifyComplete(rc); 7407 mData->mSession.mProgress.setNull(); 7408 } 7409 7410 mParent->addProcessToReap(mData->mSession.mPid); 7411 mData->mSession.mPid = NIL_RTPROCESS; 7412 7413 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); 7414 return true; 7415 } 7416 7417 return false; 7418 } 7419 7420 /** 7421 * Checks whether the machine can be registered. If so, commits and saves 7422 * all settings. 7423 * 7424 * @note Must be called from mParent's write lock. Locks this object and 7425 * children for writing. 7426 */ 7427 HRESULT Machine::prepareRegister() 7428 { 7429 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL); 7430 7431 AutoLimitedCaller autoCaller(this); 7432 AssertComRCReturnRC(autoCaller.rc()); 7433 7434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7435 7436 /* wait for state dependents to drop to zero */ 7437 ensureNoStateDependencies(); 7438 7439 if (!mData->mAccessible) 7440 return setError(VBOX_E_INVALID_OBJECT_STATE, 7441 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"), 7442 mUserData->s.strName.c_str(), 7443 mData->mUuid.toString().c_str()); 7444 7445 AssertReturn(autoCaller.state() == Ready, E_FAIL); 7446 7447 if (mData->mRegistered) 7448 return setError(VBOX_E_INVALID_OBJECT_STATE, 7449 tr("The machine '%s' with UUID {%s} is already registered"), 7450 mUserData->s.strName.c_str(), 7451 mData->mUuid.toString().c_str()); 7452 7453 HRESULT rc = S_OK; 7454 7455 // Ensure the settings are saved. If we are going to be registered and 7456 // no config file exists yet, create it by calling saveSettings() too. 7457 if ( (mData->flModifications) 7458 || (!mData->pMachineConfigFile->fileExists()) 7459 ) 7460 { 7461 rc = saveSettings(NULL); 7462 // no need to check whether VirtualBox.xml needs saving too since 7463 // we can't have a machine XML file rename pending 7464 if (FAILED(rc)) return rc; 7465 } 7466 7467 /* more config checking goes here */ 7468 7469 if (SUCCEEDED(rc)) 7470 { 7471 /* we may have had implicit modifications we want to fix on success */ 7472 commit(); 7473 7474 mData->mRegistered = true; 7475 } 7476 else 7477 { 7478 /* we may have had implicit modifications we want to cancel on failure*/ 7479 rollback(false /* aNotify */); 7480 } 7481 7482 return rc; 7483 } 7484 7485 /** 7486 * Increases the number of objects dependent on the machine state or on the 7487 * registered state. Guarantees that these two states will not change at least 7488 * until #releaseStateDependency() is called. 7489 * 7490 * Depending on the @a aDepType value, additional state checks may be made. 7491 * These checks will set extended error info on failure. See 7492 * #checkStateDependency() for more info. 7493 * 7494 * If this method returns a failure, the dependency is not added and the caller 7495 * is not allowed to rely on any particular machine state or registration state 7496 * value and may return the failed result code to the upper level. 7497 * 7498 * @param aDepType Dependency type to add. 7499 * @param aState Current machine state (NULL if not interested). 7500 * @param aRegistered Current registered state (NULL if not interested). 7501 * 7502 * @note Locks this object for writing. 7503 */ 7504 HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */, 7505 MachineState_T *aState /* = NULL */, 7506 BOOL *aRegistered /* = NULL */) 7507 { 7508 AutoCaller autoCaller(this); 7509 AssertComRCReturnRC(autoCaller.rc()); 7510 7511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7512 7513 HRESULT rc = checkStateDependency(aDepType); 7514 if (FAILED(rc)) return rc; 7515 7516 { 7517 if (mData->mMachineStateChangePending != 0) 7518 { 7519 /* ensureNoStateDependencies() is waiting for state dependencies to 7520 * drop to zero so don't add more. It may make sense to wait a bit 7521 * and retry before reporting an error (since the pending state 7522 * transition should be really quick) but let's just assert for 7523 * now to see if it ever happens on practice. */ 7524 7525 AssertFailed(); 7526 7527 return setError(E_ACCESSDENIED, 7528 tr("Machine state change is in progress. Please retry the operation later.")); 7529 } 7530 7531 ++mData->mMachineStateDeps; 7532 Assert(mData->mMachineStateDeps != 0 /* overflow */); 7533 } 7534 7535 if (aState) 7536 *aState = mData->mMachineState; 7537 if (aRegistered) 7538 *aRegistered = mData->mRegistered; 7539 7540 return S_OK; 7541 } 7542 7543 /** 7544 * Decreases the number of objects dependent on the machine state. 7545 * Must always complete the #addStateDependency() call after the state 7546 * dependency is no more necessary. 7547 */ 7548 void Machine::releaseStateDependency() 7549 { 7550 AutoCaller autoCaller(this); 7551 AssertComRCReturnVoid(autoCaller.rc()); 7552 7553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7554 7555 /* releaseStateDependency() w/o addStateDependency()? */ 7556 AssertReturnVoid(mData->mMachineStateDeps != 0); 7557 -- mData->mMachineStateDeps; 7558 7559 if (mData->mMachineStateDeps == 0) 7560 { 7561 /* inform ensureNoStateDependencies() that there are no more deps */ 7562 if (mData->mMachineStateChangePending != 0) 7563 { 7564 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI); 7565 RTSemEventMultiSignal (mData->mMachineStateDepsSem); 7566 } 7567 } 7568 } 7569 7570 // protected methods 7571 ///////////////////////////////////////////////////////////////////////////// 7572 7573 /** 7574 * Performs machine state checks based on the @a aDepType value. If a check 7575 * fails, this method will set extended error info, otherwise it will return 7576 * S_OK. It is supposed, that on failure, the caller will immediately return 7577 * the return value of this method to the upper level. 7578 * 7579 * When @a aDepType is AnyStateDep, this method always returns S_OK. 7580 * 7581 * When @a aDepType is MutableStateDep, this method returns S_OK only if the 7582 * current state of this machine object allows to change settings of the 7583 * machine (i.e. the machine is not registered, or registered but not running 7584 * and not saved). It is useful to call this method from Machine setters 7585 * before performing any change. 7586 * 7587 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same 7588 * as for MutableStateDep except that if the machine is saved, S_OK is also 7589 * returned. This is useful in setters which allow changing machine 7590 * properties when it is in the saved state. 7591 * 7592 * @param aDepType Dependency type to check. 7593 * 7594 * @note Non Machine based classes should use #addStateDependency() and 7595 * #releaseStateDependency() methods or the smart AutoStateDependency 7596 * template. 7597 * 7598 * @note This method must be called from under this object's read or write 7599 * lock. 7600 */ 7601 HRESULT Machine::checkStateDependency(StateDependency aDepType) 7602 { 7603 switch (aDepType) 7604 { 7605 case AnyStateDep: 7606 { 7607 break; 7608 } 7609 case MutableStateDep: 7610 { 7611 if ( mData->mRegistered 7612 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */ 7613 || ( mData->mMachineState != MachineState_Paused 7614 && mData->mMachineState != MachineState_Running 7615 && mData->mMachineState != MachineState_Aborted 7616 && mData->mMachineState != MachineState_Teleported 7617 && mData->mMachineState != MachineState_PoweredOff 7618 ) 7619 ) 7620 ) 7621 return setError(VBOX_E_INVALID_VM_STATE, 7622 tr("The machine is not mutable (state is %s)"), 7623 Global::stringifyMachineState(mData->mMachineState)); 7624 break; 7625 } 7626 case MutableOrSavedStateDep: 7627 { 7628 if ( mData->mRegistered 7629 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */ 7630 || ( mData->mMachineState != MachineState_Paused 7631 && mData->mMachineState != MachineState_Running 7632 && mData->mMachineState != MachineState_Aborted 7633 && mData->mMachineState != MachineState_Teleported 7634 && mData->mMachineState != MachineState_Saved 7635 && mData->mMachineState != MachineState_PoweredOff 7636 ) 7637 ) 7638 ) 7639 return setError(VBOX_E_INVALID_VM_STATE, 7640 tr("The machine is not mutable (state is %s)"), 7641 Global::stringifyMachineState(mData->mMachineState)); 7642 break; 7643 } 7644 } 7645 7646 return S_OK; 7647 } 7648 7649 /** 7650 * Helper to initialize all associated child objects and allocate data 7651 * structures. 7652 * 7653 * This method must be called as a part of the object's initialization procedure 7654 * (usually done in the #init() method). 7655 * 7656 * @note Must be called only from #init() or from #registeredInit(). 7657 */ 7658 HRESULT Machine::initDataAndChildObjects() 7659 { 7660 AutoCaller autoCaller(this); 7661 AssertComRCReturnRC(autoCaller.rc()); 7662 AssertComRCReturn(autoCaller.state() == InInit || 7663 autoCaller.state() == Limited, E_FAIL); 7664 7665 AssertReturn(!mData->mAccessible, E_FAIL); 7666 7667 /* allocate data structures */ 7668 mSSData.allocate(); 7669 mUserData.allocate(); 7670 mHWData.allocate(); 7671 mMediaData.allocate(); 7672 mStorageControllers.allocate(); 7673 7674 /* initialize mOSTypeId */ 7675 mUserData->s.strOsType = mParent->getUnknownOSType()->id(); 7676 7677 /* create associated BIOS settings object */ 7678 unconst(mBIOSSettings).createObject(); 7679 mBIOSSettings->init(this); 7680 7681 /* create an associated VRDE object (default is disabled) */ 7682 unconst(mVRDEServer).createObject(); 7683 mVRDEServer->init(this); 7684 7685 /* create associated serial port objects */ 7686 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 7687 { 7688 unconst(mSerialPorts[slot]).createObject(); 7689 mSerialPorts[slot]->init(this, slot); 7690 } 7691 7692 /* create associated parallel port objects */ 7693 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 7694 { 7695 unconst(mParallelPorts[slot]).createObject(); 7696 mParallelPorts[slot]->init(this, slot); 7697 } 7698 7699 /* create the audio adapter object (always present, default is disabled) */ 7700 unconst(mAudioAdapter).createObject(); 7701 mAudioAdapter->init(this); 7702 7703 /* create the USB controller object (always present, default is disabled) */ 7704 unconst(mUSBController).createObject(); 7705 mUSBController->init(this); 7706 7707 /* create associated network adapter objects */ 7708 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++) 7709 { 7710 unconst(mNetworkAdapters[slot]).createObject(); 7711 mNetworkAdapters[slot]->init(this, slot); 7712 } 7713 7714 /* create the bandwidth control */ 7715 unconst(mBandwidthControl).createObject(); 7716 mBandwidthControl->init(this); 7717 7718 return S_OK; 7719 } 7720 7721 /** 7722 * Helper to uninitialize all associated child objects and to free all data 7723 * structures. 7724 * 7725 * This method must be called as a part of the object's uninitialization 7726 * procedure (usually done in the #uninit() method). 7727 * 7728 * @note Must be called only from #uninit() or from #registeredInit(). 7729 */ 7730 void Machine::uninitDataAndChildObjects() 7731 { 7732 AutoCaller autoCaller(this); 7733 AssertComRCReturnVoid(autoCaller.rc()); 7734 AssertComRCReturnVoid( autoCaller.state() == InUninit 7735 || autoCaller.state() == Limited); 7736 7737 /* tell all our other child objects we've been uninitialized */ 7738 if (mBandwidthControl) 7739 { 7740 mBandwidthControl->uninit(); 7741 unconst(mBandwidthControl).setNull(); 7742 } 7743 7744 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 7745 { 7746 if (mNetworkAdapters[slot]) 7747 { 7748 mNetworkAdapters[slot]->uninit(); 7749 unconst(mNetworkAdapters[slot]).setNull(); 7750 } 7751 } 7752 7753 if (mUSBController) 7754 { 7755 mUSBController->uninit(); 7756 unconst(mUSBController).setNull(); 7757 } 7758 7759 if (mAudioAdapter) 7760 { 7761 mAudioAdapter->uninit(); 7762 unconst(mAudioAdapter).setNull(); 7763 } 7764 7765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 7766 { 7767 if (mParallelPorts[slot]) 7768 { 7769 mParallelPorts[slot]->uninit(); 7770 unconst(mParallelPorts[slot]).setNull(); 7771 } 7772 } 7773 7774 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 7775 { 7776 if (mSerialPorts[slot]) 7777 { 7778 mSerialPorts[slot]->uninit(); 7779 unconst(mSerialPorts[slot]).setNull(); 7780 } 7781 } 7782 7783 if (mVRDEServer) 7784 { 7785 mVRDEServer->uninit(); 7786 unconst(mVRDEServer).setNull(); 7787 } 7788 7789 if (mBIOSSettings) 7790 { 7791 mBIOSSettings->uninit(); 7792 unconst(mBIOSSettings).setNull(); 7793 } 7794 7795 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine 7796 * instance is uninitialized; SessionMachine instances refer to real 7797 * Machine hard disks). This is necessary for a clean re-initialization of 7798 * the VM after successfully re-checking the accessibility state. Note 7799 * that in case of normal Machine or SnapshotMachine uninitialization (as 7800 * a result of unregistering or deleting the snapshot), outdated hard 7801 * disk attachments will already be uninitialized and deleted, so this 7802 * code will not affect them. */ 7803 if ( !!mMediaData 7804 && (!isSessionMachine()) 7805 ) 7806 { 7807 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 7808 it != mMediaData->mAttachments.end(); 7809 ++it) 7810 { 7811 ComObjPtr<Medium> hd = (*it)->getMedium(); 7812 if (hd.isNull()) 7813 continue; 7814 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId()); 7815 AssertComRC(rc); 7816 } 7817 } 7818 7819 if (!isSessionMachine() && !isSnapshotMachine()) 7820 { 7821 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively) 7822 if (mData->mFirstSnapshot) 7823 { 7824 // snapshots tree is protected by media write lock; strictly 7825 // this isn't necessary here since we're deleting the entire 7826 // machine, but otherwise we assert in Snapshot::uninit() 7827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7828 mData->mFirstSnapshot->uninit(); 7829 mData->mFirstSnapshot.setNull(); 7830 } 7831 7832 mData->mCurrentSnapshot.setNull(); 7833 } 7834 7835 /* free data structures (the essential mData structure is not freed here 7836 * since it may be still in use) */ 7837 mMediaData.free(); 7838 mStorageControllers.free(); 7839 mHWData.free(); 7840 mUserData.free(); 7841 mSSData.free(); 7842 } 7843 7844 /** 7845 * Returns a pointer to the Machine object for this machine that acts like a 7846 * parent for complex machine data objects such as shared folders, etc. 7847 * 7848 * For primary Machine objects and for SnapshotMachine objects, returns this 7849 * object's pointer itself. For SessionMachine objects, returns the peer 7850 * (primary) machine pointer. 7851 */ 7852 Machine* Machine::getMachine() 7853 { 7854 if (isSessionMachine()) 7855 return (Machine*)mPeer; 7856 return this; 7857 } 7858 7859 /** 7860 * Makes sure that there are no machine state dependents. If necessary, waits 7861 * for the number of dependents to drop to zero. 7862 * 7863 * Make sure this method is called from under this object's write lock to 7864 * guarantee that no new dependents may be added when this method returns 7865 * control to the caller. 7866 * 7867 * @note Locks this object for writing. The lock will be released while waiting 7868 * (if necessary). 7869 * 7870 * @warning To be used only in methods that change the machine state! 7871 */ 7872 void Machine::ensureNoStateDependencies() 7873 { 7874 AssertReturnVoid(isWriteLockOnCurrentThread()); 7875 7876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7877 7878 /* Wait for all state dependents if necessary */ 7879 if (mData->mMachineStateDeps != 0) 7880 { 7881 /* lazy semaphore creation */ 7882 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI) 7883 RTSemEventMultiCreate(&mData->mMachineStateDepsSem); 7884 7885 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n", 7886 mData->mMachineStateDeps)); 7887 7888 ++mData->mMachineStateChangePending; 7889 7890 /* reset the semaphore before waiting, the last dependent will signal 7891 * it */ 7892 RTSemEventMultiReset(mData->mMachineStateDepsSem); 7893 7894 alock.leave(); 7895 7896 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT); 7897 7898 alock.enter(); 7899 7900 -- mData->mMachineStateChangePending; 7901 } 7902 } 7903 7904 /** 7905 * Changes the machine state and informs callbacks. 7906 * 7907 * This method is not intended to fail so it either returns S_OK or asserts (and 7908 * returns a failure). 7909 * 7910 * @note Locks this object for writing. 7911 */ 7912 HRESULT Machine::setMachineState(MachineState_T aMachineState) 7913 { 7914 LogFlowThisFuncEnter(); 7915 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) )); 7916 7917 AutoCaller autoCaller(this); 7918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 7919 7920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7921 7922 /* wait for state dependents to drop to zero */ 7923 ensureNoStateDependencies(); 7924 7925 if (mData->mMachineState != aMachineState) 7926 { 7927 mData->mMachineState = aMachineState; 7928 7929 RTTimeNow(&mData->mLastStateChange); 7930 7931 mParent->onMachineStateChange(mData->mUuid, aMachineState); 7932 } 7933 7934 LogFlowThisFuncLeave(); 7935 return S_OK; 7936 } 7937 7938 /** 7939 * Searches for a shared folder with the given logical name 7940 * in the collection of shared folders. 7941 * 7942 * @param aName logical name of the shared folder 7943 * @param aSharedFolder where to return the found object 7944 * @param aSetError whether to set the error info if the folder is 7945 * not found 7946 * @return 7947 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found 7948 * 7949 * @note 7950 * must be called from under the object's lock! 7951 */ 7952 HRESULT Machine::findSharedFolder(const Utf8Str &aName, 7953 ComObjPtr<SharedFolder> &aSharedFolder, 7954 bool aSetError /* = false */) 7955 { 7956 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND; 7957 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 7958 it != mHWData->mSharedFolders.end(); 7959 ++it) 7960 { 7961 SharedFolder *pSF = *it; 7962 AutoCaller autoCaller(pSF); 7963 if (pSF->getName() == aName) 7964 { 7965 aSharedFolder = pSF; 7966 rc = S_OK; 7967 break; 7968 } 7969 } 7970 7971 if (aSetError && FAILED(rc)) 7972 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str()); 7973 7974 return rc; 7975 } 7976 7977 /** 7978 * Initializes all machine instance data from the given settings structures 7979 * from XML. The exception is the machine UUID which needs special handling 7980 * depending on the caller's use case, so the caller needs to set that herself. 7981 * 7982 * This gets called in several contexts during machine initialization: 7983 * 7984 * -- When machine XML exists on disk already and needs to be loaded into memory, 7985 * for example, from registeredInit() to load all registered machines on 7986 * VirtualBox startup. In this case, puuidRegistry is NULL because the media 7987 * attached to the machine should be part of some media registry already. 7988 * 7989 * -- During OVF import, when a machine config has been constructed from an 7990 * OVF file. In this case, puuidRegistry is set to the machine UUID to 7991 * ensure that the media listed as attachments in the config (which have 7992 * been imported from the OVF) receive the correct registry ID. 7993 * 7994 * -- During VM cloning. 7995 * 7996 * @param config Machine settings from XML. 7997 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config. 7998 * @return 7999 */ 8000 HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config, 8001 const Guid *puuidRegistry) 8002 { 8003 // copy name, description, OS type, teleporter, UTC etc. 8004 mUserData->s = config.machineUserData; 8005 8006 // look up the object by Id to check it is valid 8007 ComPtr<IGuestOSType> guestOSType; 8008 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), 8009 guestOSType.asOutParam()); 8010 if (FAILED(rc)) return rc; 8011 8012 // stateFile (optional) 8013 if (config.strStateFile.isEmpty()) 8014 mSSData->strStateFilePath.setNull(); 8015 else 8016 { 8017 Utf8Str stateFilePathFull(config.strStateFile); 8018 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull); 8019 if (RT_FAILURE(vrc)) 8020 return setError(E_FAIL, 8021 tr("Invalid saved state file path '%s' (%Rrc)"), 8022 config.strStateFile.c_str(), 8023 vrc); 8024 mSSData->strStateFilePath = stateFilePathFull; 8025 } 8026 8027 // snapshot folder needs special processing so set it again 8028 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw()); 8029 if (FAILED(rc)) return rc; 8030 8031 /* currentStateModified (optional, default is true) */ 8032 mData->mCurrentStateModified = config.fCurrentStateModified; 8033 8034 mData->mLastStateChange = config.timeLastStateChange; 8035 8036 /* 8037 * note: all mUserData members must be assigned prior this point because 8038 * we need to commit changes in order to let mUserData be shared by all 8039 * snapshot machine instances. 8040 */ 8041 mUserData.commitCopy(); 8042 8043 // machine registry, if present (must be loaded before snapshots) 8044 if (config.canHaveOwnMediaRegistry()) 8045 { 8046 // determine machine folder 8047 Utf8Str strMachineFolder = getSettingsFileFull(); 8048 strMachineFolder.stripFilename(); 8049 rc = mParent->initMedia(getId(), // media registry ID == machine UUID 8050 config.mediaRegistry, 8051 strMachineFolder); 8052 if (FAILED(rc)) return rc; 8053 } 8054 8055 /* Snapshot node (optional) */ 8056 size_t cRootSnapshots; 8057 if ((cRootSnapshots = config.llFirstSnapshot.size())) 8058 { 8059 // there must be only one root snapshot 8060 Assert(cRootSnapshots == 1); 8061 8062 const settings::Snapshot &snap = config.llFirstSnapshot.front(); 8063 8064 rc = loadSnapshot(snap, 8065 config.uuidCurrentSnapshot, 8066 NULL); // no parent == first snapshot 8067 if (FAILED(rc)) return rc; 8068 } 8069 8070 // hardware data 8071 rc = loadHardware(config.hardwareMachine); 8072 if (FAILED(rc)) return rc; 8073 8074 // load storage controllers 8075 rc = loadStorageControllers(config.storageMachine, 8076 puuidRegistry, 8077 NULL /* puuidSnapshot */); 8078 if (FAILED(rc)) return rc; 8079 8080 /* 8081 * NOTE: the assignment below must be the last thing to do, 8082 * otherwise it will be not possible to change the settings 8083 * somewhere in the code above because all setters will be 8084 * blocked by checkStateDependency(MutableStateDep). 8085 */ 8086 8087 /* set the machine state to Aborted or Saved when appropriate */ 8088 if (config.fAborted) 8089 { 8090 Assert(!mSSData->strStateFilePath.isEmpty()); 8091 mSSData->strStateFilePath.setNull(); 8092 8093 /* no need to use setMachineState() during init() */ 8094 mData->mMachineState = MachineState_Aborted; 8095 } 8096 else if (!mSSData->strStateFilePath.isEmpty()) 8097 { 8098 /* no need to use setMachineState() during init() */ 8099 mData->mMachineState = MachineState_Saved; 8100 } 8101 8102 // after loading settings, we are no longer different from the XML on disk 8103 mData->flModifications = 0; 8104 8105 return S_OK; 8106 } 8107 8108 /** 8109 * Recursively loads all snapshots starting from the given. 8110 * 8111 * @param aNode <Snapshot> node. 8112 * @param aCurSnapshotId Current snapshot ID from the settings file. 8113 * @param aParentSnapshot Parent snapshot. 8114 */ 8115 HRESULT Machine::loadSnapshot(const settings::Snapshot &data, 8116 const Guid &aCurSnapshotId, 8117 Snapshot *aParentSnapshot) 8118 { 8119 AssertReturn(!isSnapshotMachine(), E_FAIL); 8120 AssertReturn(!isSessionMachine(), E_FAIL); 8121 8122 HRESULT rc = S_OK; 8123 8124 Utf8Str strStateFile; 8125 if (!data.strStateFile.isEmpty()) 8126 { 8127 /* optional */ 8128 strStateFile = data.strStateFile; 8129 int vrc = calculateFullPath(strStateFile, strStateFile); 8130 if (RT_FAILURE(vrc)) 8131 return setError(E_FAIL, 8132 tr("Invalid saved state file path '%s' (%Rrc)"), 8133 strStateFile.c_str(), 8134 vrc); 8135 } 8136 8137 /* create a snapshot machine object */ 8138 ComObjPtr<SnapshotMachine> pSnapshotMachine; 8139 pSnapshotMachine.createObject(); 8140 rc = pSnapshotMachine->init(this, 8141 data.hardware, 8142 data.storage, 8143 data.uuid.ref(), 8144 strStateFile); 8145 if (FAILED(rc)) return rc; 8146 8147 /* create a snapshot object */ 8148 ComObjPtr<Snapshot> pSnapshot; 8149 pSnapshot.createObject(); 8150 /* initialize the snapshot */ 8151 rc = pSnapshot->init(mParent, // VirtualBox object 8152 data.uuid, 8153 data.strName, 8154 data.strDescription, 8155 data.timestamp, 8156 pSnapshotMachine, 8157 aParentSnapshot); 8158 if (FAILED(rc)) return rc; 8159 8160 /* memorize the first snapshot if necessary */ 8161 if (!mData->mFirstSnapshot) 8162 mData->mFirstSnapshot = pSnapshot; 8163 8164 /* memorize the current snapshot when appropriate */ 8165 if ( !mData->mCurrentSnapshot 8166 && pSnapshot->getId() == aCurSnapshotId 8167 ) 8168 mData->mCurrentSnapshot = pSnapshot; 8169 8170 // now create the children 8171 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin(); 8172 it != data.llChildSnapshots.end(); 8173 ++it) 8174 { 8175 const settings::Snapshot &childData = *it; 8176 // recurse 8177 rc = loadSnapshot(childData, 8178 aCurSnapshotId, 8179 pSnapshot); // parent = the one we created above 8180 if (FAILED(rc)) return rc; 8181 } 8182 8183 return rc; 8184 } 8185 8186 /** 8187 * @param aNode <Hardware> node. 8188 */ 8189 HRESULT Machine::loadHardware(const settings::Hardware &data) 8190 { 8191 AssertReturn(!isSessionMachine(), E_FAIL); 8192 8193 HRESULT rc = S_OK; 8194 8195 try 8196 { 8197 /* The hardware version attribute (optional). */ 8198 mHWData->mHWVersion = data.strVersion; 8199 mHWData->mHardwareUUID = data.uuid; 8200 8201 mHWData->mHWVirtExEnabled = data.fHardwareVirt; 8202 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive; 8203 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging; 8204 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages; 8205 mHWData->mHWVirtExVPIDEnabled = data.fVPID; 8206 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce; 8207 mHWData->mPAEEnabled = data.fPAE; 8208 mHWData->mSyntheticCpu = data.fSyntheticCpu; 8209 8210 mHWData->mCPUCount = data.cCPUs; 8211 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug; 8212 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap; 8213 8214 // cpu 8215 if (mHWData->mCPUHotPlugEnabled) 8216 { 8217 for (settings::CpuList::const_iterator it = data.llCpus.begin(); 8218 it != data.llCpus.end(); 8219 ++it) 8220 { 8221 const settings::Cpu &cpu = *it; 8222 8223 mHWData->mCPUAttached[cpu.ulId] = true; 8224 } 8225 } 8226 8227 // cpuid leafs 8228 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin(); 8229 it != data.llCpuIdLeafs.end(); 8230 ++it) 8231 { 8232 const settings::CpuIdLeaf &leaf = *it; 8233 8234 switch (leaf.ulId) 8235 { 8236 case 0x0: 8237 case 0x1: 8238 case 0x2: 8239 case 0x3: 8240 case 0x4: 8241 case 0x5: 8242 case 0x6: 8243 case 0x7: 8244 case 0x8: 8245 case 0x9: 8246 case 0xA: 8247 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf; 8248 break; 8249 8250 case 0x80000000: 8251 case 0x80000001: 8252 case 0x80000002: 8253 case 0x80000003: 8254 case 0x80000004: 8255 case 0x80000005: 8256 case 0x80000006: 8257 case 0x80000007: 8258 case 0x80000008: 8259 case 0x80000009: 8260 case 0x8000000A: 8261 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf; 8262 break; 8263 8264 default: 8265 /* just ignore */ 8266 break; 8267 } 8268 } 8269 8270 mHWData->mMemorySize = data.ulMemorySizeMB; 8271 mHWData->mPageFusionEnabled = data.fPageFusionEnabled; 8272 8273 // boot order 8274 for (size_t i = 0; 8275 i < RT_ELEMENTS(mHWData->mBootOrder); 8276 i++) 8277 { 8278 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i); 8279 if (it == data.mapBootOrder.end()) 8280 mHWData->mBootOrder[i] = DeviceType_Null; 8281 else 8282 mHWData->mBootOrder[i] = it->second; 8283 } 8284 8285 mHWData->mVRAMSize = data.ulVRAMSizeMB; 8286 mHWData->mMonitorCount = data.cMonitors; 8287 mHWData->mAccelerate3DEnabled = data.fAccelerate3D; 8288 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo; 8289 mHWData->mFirmwareType = data.firmwareType; 8290 mHWData->mPointingHidType = data.pointingHidType; 8291 mHWData->mKeyboardHidType = data.keyboardHidType; 8292 mHWData->mChipsetType = data.chipsetType; 8293 mHWData->mHpetEnabled = data.fHpetEnabled; 8294 8295 /* VRDEServer */ 8296 rc = mVRDEServer->loadSettings(data.vrdeSettings); 8297 if (FAILED(rc)) return rc; 8298 8299 /* BIOS */ 8300 rc = mBIOSSettings->loadSettings(data.biosSettings); 8301 if (FAILED(rc)) return rc; 8302 8303 // Bandwidth control (must come before network adapters) 8304 rc = mBandwidthControl->loadSettings(data.ioSettings); 8305 if (FAILED(rc)) return rc; 8306 8307 /* USB Controller */ 8308 rc = mUSBController->loadSettings(data.usbController); 8309 if (FAILED(rc)) return rc; 8310 8311 // network adapters 8312 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin(); 8313 it != data.llNetworkAdapters.end(); 8314 ++it) 8315 { 8316 const settings::NetworkAdapter &nic = *it; 8317 8318 /* slot unicity is guaranteed by XML Schema */ 8319 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters)); 8320 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic); 8321 if (FAILED(rc)) return rc; 8322 } 8323 8324 // serial ports 8325 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin(); 8326 it != data.llSerialPorts.end(); 8327 ++it) 8328 { 8329 const settings::SerialPort &s = *it; 8330 8331 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts)); 8332 rc = mSerialPorts[s.ulSlot]->loadSettings(s); 8333 if (FAILED(rc)) return rc; 8334 } 8335 8336 // parallel ports (optional) 8337 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin(); 8338 it != data.llParallelPorts.end(); 8339 ++it) 8340 { 8341 const settings::ParallelPort &p = *it; 8342 8343 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts)); 8344 rc = mParallelPorts[p.ulSlot]->loadSettings(p); 8345 if (FAILED(rc)) return rc; 8346 } 8347 8348 /* AudioAdapter */ 8349 rc = mAudioAdapter->loadSettings(data.audioAdapter); 8350 if (FAILED(rc)) return rc; 8351 8352 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin(); 8353 it != data.llSharedFolders.end(); 8354 ++it) 8355 { 8356 const settings::SharedFolder &sf = *it; 8357 rc = CreateSharedFolder(Bstr(sf.strName).raw(), 8358 Bstr(sf.strHostPath).raw(), 8359 sf.fWritable, sf.fAutoMount); 8360 if (FAILED(rc)) return rc; 8361 } 8362 8363 // Clipboard 8364 mHWData->mClipboardMode = data.clipboardMode; 8365 8366 // guest settings 8367 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize; 8368 8369 // IO settings 8370 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled; 8371 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize; 8372 8373 // Host PCI devices 8374 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin(); 8375 it != data.pciAttachments.end(); 8376 ++it) 8377 { 8378 const settings::HostPciDeviceAttachment &hpda = *it; 8379 ComObjPtr<PciDeviceAttachment> pda; 8380 8381 pda.createObject(); 8382 pda->loadSettings(this, hpda); 8383 mHWData->mPciDeviceAssignments.push_back(pda); 8384 } 8385 8386 #ifdef VBOX_WITH_GUEST_PROPS 8387 /* Guest properties (optional) */ 8388 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin(); 8389 it != data.llGuestProperties.end(); 8390 ++it) 8391 { 8392 const settings::GuestProperty &prop = *it; 8393 uint32_t fFlags = guestProp::NILFLAG; 8394 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags); 8395 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags }; 8396 mHWData->mGuestProperties.push_back(property); 8397 } 8398 8399 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns; 8400 #endif /* VBOX_WITH_GUEST_PROPS defined */ 8401 } 8402 catch(std::bad_alloc &) 8403 { 8404 return E_OUTOFMEMORY; 8405 } 8406 8407 AssertComRC(rc); 8408 return rc; 8409 } 8410 8411 /** 8412 * Called from loadMachineDataFromSettings() for the storage controller data, including media. 8413 * 8414 * @param data 8415 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings() 8416 * @param puuidSnapshot 8417 * @return 8418 */ 8419 HRESULT Machine::loadStorageControllers(const settings::Storage &data, 8420 const Guid *puuidRegistry, 8421 const Guid *puuidSnapshot) 8422 { 8423 AssertReturn(!isSessionMachine(), E_FAIL); 8424 8425 HRESULT rc = S_OK; 8426 8427 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin(); 8428 it != data.llStorageControllers.end(); 8429 ++it) 8430 { 8431 const settings::StorageController &ctlData = *it; 8432 8433 ComObjPtr<StorageController> pCtl; 8434 /* Try to find one with the name first. */ 8435 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */); 8436 if (SUCCEEDED(rc)) 8437 return setError(VBOX_E_OBJECT_IN_USE, 8438 tr("Storage controller named '%s' already exists"), 8439 ctlData.strName.c_str()); 8440 8441 pCtl.createObject(); 8442 rc = pCtl->init(this, 8443 ctlData.strName, 8444 ctlData.storageBus, 8445 ctlData.ulInstance, 8446 ctlData.fBootable); 8447 if (FAILED(rc)) return rc; 8448 8449 mStorageControllers->push_back(pCtl); 8450 8451 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType); 8452 if (FAILED(rc)) return rc; 8453 8454 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount); 8455 if (FAILED(rc)) return rc; 8456 8457 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache); 8458 if (FAILED(rc)) return rc; 8459 8460 /* Set IDE emulation settings (only for AHCI controller). */ 8461 if (ctlData.controllerType == StorageControllerType_IntelAhci) 8462 { 8463 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort))) 8464 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort))) 8465 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort))) 8466 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort))) 8467 ) 8468 return rc; 8469 } 8470 8471 /* Load the attached devices now. */ 8472 rc = loadStorageDevices(pCtl, 8473 ctlData, 8474 puuidRegistry, 8475 puuidSnapshot); 8476 if (FAILED(rc)) return rc; 8477 } 8478 8479 return S_OK; 8480 } 8481 8482 /** 8483 * Called from loadStorageControllers for a controller's devices. 8484 * 8485 * @param aStorageController 8486 * @param data 8487 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings() 8488 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine 8489 * @return 8490 */ 8491 HRESULT Machine::loadStorageDevices(StorageController *aStorageController, 8492 const settings::StorageController &data, 8493 const Guid *puuidRegistry, 8494 const Guid *puuidSnapshot) 8495 { 8496 HRESULT rc = S_OK; 8497 8498 /* paranoia: detect duplicate attachments */ 8499 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 8500 it != data.llAttachedDevices.end(); 8501 ++it) 8502 { 8503 const settings::AttachedDevice &ad = *it; 8504 8505 for (settings::AttachedDevicesList::const_iterator it2 = it; 8506 it2 != data.llAttachedDevices.end(); 8507 ++it2) 8508 { 8509 if (it == it2) 8510 continue; 8511 8512 const settings::AttachedDevice &ad2 = *it2; 8513 8514 if ( ad.lPort == ad2.lPort 8515 && ad.lDevice == ad2.lDevice) 8516 { 8517 return setError(E_FAIL, 8518 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"), 8519 aStorageController->getName().c_str(), 8520 ad.lPort, 8521 ad.lDevice, 8522 mUserData->s.strName.c_str()); 8523 } 8524 } 8525 } 8526 8527 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 8528 it != data.llAttachedDevices.end(); 8529 ++it) 8530 { 8531 const settings::AttachedDevice &dev = *it; 8532 ComObjPtr<Medium> medium; 8533 8534 switch (dev.deviceType) 8535 { 8536 case DeviceType_Floppy: 8537 case DeviceType_DVD: 8538 if (dev.strHostDriveSrc.isNotEmpty()) 8539 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium); 8540 else 8541 rc = mParent->findRemoveableMedium(dev.deviceType, 8542 dev.uuid, 8543 false /* fRefresh */, 8544 false /* aSetError */, 8545 medium); 8546 if (rc == VBOX_E_OBJECT_NOT_FOUND) 8547 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment 8548 rc = S_OK; 8549 break; 8550 8551 case DeviceType_HardDisk: 8552 { 8553 /* find a hard disk by UUID */ 8554 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium); 8555 if (FAILED(rc)) 8556 { 8557 if (isSnapshotMachine()) 8558 { 8559 // wrap another error message around the "cannot find hard disk" set by findHardDisk 8560 // so the user knows that the bad disk is in a snapshot somewhere 8561 com::ErrorInfo info; 8562 return setError(E_FAIL, 8563 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"), 8564 puuidSnapshot->raw(), 8565 info.getText().raw()); 8566 } 8567 else 8568 return rc; 8569 } 8570 8571 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS); 8572 8573 if (medium->getType() == MediumType_Immutable) 8574 { 8575 if (isSnapshotMachine()) 8576 return setError(E_FAIL, 8577 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} " 8578 "of the virtual machine '%s' ('%s')"), 8579 medium->getLocationFull().c_str(), 8580 dev.uuid.raw(), 8581 puuidSnapshot->raw(), 8582 mUserData->s.strName.c_str(), 8583 mData->m_strConfigFileFull.c_str()); 8584 8585 return setError(E_FAIL, 8586 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"), 8587 medium->getLocationFull().c_str(), 8588 dev.uuid.raw(), 8589 mUserData->s.strName.c_str(), 8590 mData->m_strConfigFileFull.c_str()); 8591 } 8592 8593 if (medium->getType() == MediumType_MultiAttach) 8594 { 8595 if (isSnapshotMachine()) 8596 return setError(E_FAIL, 8597 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} " 8598 "of the virtual machine '%s' ('%s')"), 8599 medium->getLocationFull().c_str(), 8600 dev.uuid.raw(), 8601 puuidSnapshot->raw(), 8602 mUserData->s.strName.c_str(), 8603 mData->m_strConfigFileFull.c_str()); 8604 8605 return setError(E_FAIL, 8606 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"), 8607 medium->getLocationFull().c_str(), 8608 dev.uuid.raw(), 8609 mUserData->s.strName.c_str(), 8610 mData->m_strConfigFileFull.c_str()); 8611 } 8612 8613 if ( !isSnapshotMachine() 8614 && medium->getChildren().size() != 0 8615 ) 8616 return setError(E_FAIL, 8617 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') " 8618 "because it has %d differencing child hard disks"), 8619 medium->getLocationFull().c_str(), 8620 dev.uuid.raw(), 8621 mUserData->s.strName.c_str(), 8622 mData->m_strConfigFileFull.c_str(), 8623 medium->getChildren().size()); 8624 8625 if (findAttachment(mMediaData->mAttachments, 8626 medium)) 8627 return setError(E_FAIL, 8628 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"), 8629 medium->getLocationFull().c_str(), 8630 dev.uuid.raw(), 8631 mUserData->s.strName.c_str(), 8632 mData->m_strConfigFileFull.c_str()); 8633 8634 break; 8635 } 8636 8637 default: 8638 return setError(E_FAIL, 8639 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"), 8640 medium->getLocationFull().c_str(), 8641 mUserData->s.strName.c_str(), 8642 mData->m_strConfigFileFull.c_str()); 8643 } 8644 8645 if (FAILED(rc)) 8646 break; 8647 8648 /* Bandwidth groups are loaded at this point. */ 8649 ComObjPtr<BandwidthGroup> pBwGroup; 8650 8651 if (!dev.strBwGroup.isEmpty()) 8652 { 8653 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */); 8654 if (FAILED(rc)) 8655 return setError(E_FAIL, 8656 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"), 8657 medium->getLocationFull().c_str(), 8658 dev.strBwGroup.c_str(), 8659 mUserData->s.strName.c_str(), 8660 mData->m_strConfigFileFull.c_str()); 8661 pBwGroup->reference(); 8662 } 8663 8664 const Bstr controllerName = aStorageController->getName(); 8665 ComObjPtr<MediumAttachment> pAttachment; 8666 pAttachment.createObject(); 8667 rc = pAttachment->init(this, 8668 medium, 8669 controllerName, 8670 dev.lPort, 8671 dev.lDevice, 8672 dev.deviceType, 8673 dev.fPassThrough, 8674 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName()); 8675 if (FAILED(rc)) break; 8676 8677 /* associate the medium with this machine and snapshot */ 8678 if (!medium.isNull()) 8679 { 8680 AutoCaller medCaller(medium); 8681 if (FAILED(medCaller.rc())) return medCaller.rc(); 8682 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS); 8683 8684 if (isSnapshotMachine()) 8685 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot); 8686 else 8687 rc = medium->addBackReference(mData->mUuid); 8688 /* If the medium->addBackReference fails it sets an appropriate 8689 * error message, so no need to do any guesswork here. */ 8690 8691 if (puuidRegistry) 8692 // caller wants registry ID to be set on all attached media (OVF import case) 8693 medium->addRegistry(*puuidRegistry, false /* fRecurse */); 8694 } 8695 8696 if (FAILED(rc)) 8697 break; 8698 8699 /* back up mMediaData to let registeredInit() properly rollback on failure 8700 * (= limited accessibility) */ 8701 setModified(IsModified_Storage); 8702 mMediaData.backup(); 8703 mMediaData->mAttachments.push_back(pAttachment); 8704 } 8705 8706 return rc; 8707 } 8708 8709 /** 8710 * Returns the snapshot with the given UUID or fails of no such snapshot exists. 8711 * 8712 * @param aId snapshot UUID to find (empty UUID refers the first snapshot) 8713 * @param aSnapshot where to return the found snapshot 8714 * @param aSetError true to set extended error info on failure 8715 */ 8716 HRESULT Machine::findSnapshotById(const Guid &aId, 8717 ComObjPtr<Snapshot> &aSnapshot, 8718 bool aSetError /* = false */) 8719 { 8720 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 8721 8722 if (!mData->mFirstSnapshot) 8723 { 8724 if (aSetError) 8725 return setError(E_FAIL, tr("This machine does not have any snapshots")); 8726 return E_FAIL; 8727 } 8728 8729 if (aId.isEmpty()) 8730 aSnapshot = mData->mFirstSnapshot; 8731 else 8732 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref()); 8733 8734 if (!aSnapshot) 8735 { 8736 if (aSetError) 8737 return setError(E_FAIL, 8738 tr("Could not find a snapshot with UUID {%s}"), 8739 aId.toString().c_str()); 8740 return E_FAIL; 8741 } 8742 8743 return S_OK; 8744 } 8745 8746 /** 8747 * Returns the snapshot with the given name or fails of no such snapshot. 8748 * 8749 * @param aName snapshot name to find 8750 * @param aSnapshot where to return the found snapshot 8751 * @param aSetError true to set extended error info on failure 8752 */ 8753 HRESULT Machine::findSnapshotByName(const Utf8Str &strName, 8754 ComObjPtr<Snapshot> &aSnapshot, 8755 bool aSetError /* = false */) 8756 { 8757 AssertReturn(!strName.isEmpty(), E_INVALIDARG); 8758 8759 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 8760 8761 if (!mData->mFirstSnapshot) 8762 { 8763 if (aSetError) 8764 return setError(VBOX_E_OBJECT_NOT_FOUND, 8765 tr("This machine does not have any snapshots")); 8766 return VBOX_E_OBJECT_NOT_FOUND; 8767 } 8768 8769 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName); 8770 8771 if (!aSnapshot) 8772 { 8773 if (aSetError) 8774 return setError(VBOX_E_OBJECT_NOT_FOUND, 8775 tr("Could not find a snapshot named '%s'"), strName.c_str()); 8776 return VBOX_E_OBJECT_NOT_FOUND; 8777 } 8778 8779 return S_OK; 8780 } 8781 8782 /** 8783 * Returns a storage controller object with the given name. 8784 * 8785 * @param aName storage controller name to find 8786 * @param aStorageController where to return the found storage controller 8787 * @param aSetError true to set extended error info on failure 8788 */ 8789 HRESULT Machine::getStorageControllerByName(const Utf8Str &aName, 8790 ComObjPtr<StorageController> &aStorageController, 8791 bool aSetError /* = false */) 8792 { 8793 AssertReturn(!aName.isEmpty(), E_INVALIDARG); 8794 8795 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 8796 it != mStorageControllers->end(); 8797 ++it) 8798 { 8799 if ((*it)->getName() == aName) 8800 { 8801 aStorageController = (*it); 8802 return S_OK; 8803 } 8804 } 8805 8806 if (aSetError) 8807 return setError(VBOX_E_OBJECT_NOT_FOUND, 8808 tr("Could not find a storage controller named '%s'"), 8809 aName.c_str()); 8810 return VBOX_E_OBJECT_NOT_FOUND; 8811 } 8812 8813 HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName, 8814 MediaData::AttachmentList &atts) 8815 { 8816 AutoCaller autoCaller(this); 8817 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 8818 8819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 8820 8821 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin(); 8822 it != mMediaData->mAttachments.end(); 8823 ++it) 8824 { 8825 const ComObjPtr<MediumAttachment> &pAtt = *it; 8826 8827 // should never happen, but deal with NULL pointers in the list. 8828 AssertStmt(!pAtt.isNull(), continue); 8829 8830 // getControllerName() needs caller+read lock 8831 AutoCaller autoAttCaller(pAtt); 8832 if (FAILED(autoAttCaller.rc())) 8833 { 8834 atts.clear(); 8835 return autoAttCaller.rc(); 8836 } 8837 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS); 8838 8839 if (pAtt->getControllerName() == aName) 8840 atts.push_back(pAtt); 8841 } 8842 8843 return S_OK; 8844 } 8845 8846 /** 8847 * Helper for #saveSettings. Cares about renaming the settings directory and 8848 * file if the machine name was changed and about creating a new settings file 8849 * if this is a new machine. 8850 * 8851 * @note Must be never called directly but only from #saveSettings(). 8852 */ 8853 HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings) 8854 { 8855 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 8856 8857 HRESULT rc = S_OK; 8858 8859 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists(); 8860 8861 /* attempt to rename the settings file if machine name is changed */ 8862 if ( mUserData->s.fNameSync 8863 && mUserData.isBackedUp() 8864 && mUserData.backedUpData()->s.strName != mUserData->s.strName 8865 ) 8866 { 8867 bool dirRenamed = false; 8868 bool fileRenamed = false; 8869 8870 Utf8Str configFile, newConfigFile; 8871 Utf8Str configFilePrev, newConfigFilePrev; 8872 Utf8Str configDir, newConfigDir; 8873 8874 do 8875 { 8876 int vrc = VINF_SUCCESS; 8877 8878 Utf8Str name = mUserData.backedUpData()->s.strName; 8879 Utf8Str newName = mUserData->s.strName; 8880 8881 configFile = mData->m_strConfigFileFull; 8882 8883 /* first, rename the directory if it matches the machine name */ 8884 configDir = configFile; 8885 configDir.stripFilename(); 8886 newConfigDir = configDir; 8887 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str())) 8888 { 8889 newConfigDir.stripFilename(); 8890 newConfigDir.append(RTPATH_DELIMITER); 8891 newConfigDir.append(newName); 8892 /* new dir and old dir cannot be equal here because of 'if' 8893 * above and because name != newName */ 8894 Assert(configDir != newConfigDir); 8895 if (!fSettingsFileIsNew) 8896 { 8897 /* perform real rename only if the machine is not new */ 8898 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0); 8899 if (RT_FAILURE(vrc)) 8900 { 8901 rc = setError(E_FAIL, 8902 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"), 8903 configDir.c_str(), 8904 newConfigDir.c_str(), 8905 vrc); 8906 break; 8907 } 8908 dirRenamed = true; 8909 } 8910 } 8911 8912 newConfigFile = Utf8StrFmt("%s%c%s.vbox", 8913 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str()); 8914 8915 /* then try to rename the settings file itself */ 8916 if (newConfigFile != configFile) 8917 { 8918 /* get the path to old settings file in renamed directory */ 8919 configFile = Utf8StrFmt("%s%c%s", 8920 newConfigDir.c_str(), 8921 RTPATH_DELIMITER, 8922 RTPathFilename(configFile.c_str())); 8923 if (!fSettingsFileIsNew) 8924 { 8925 /* perform real rename only if the machine is not new */ 8926 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0); 8927 if (RT_FAILURE(vrc)) 8928 { 8929 rc = setError(E_FAIL, 8930 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"), 8931 configFile.c_str(), 8932 newConfigFile.c_str(), 8933 vrc); 8934 break; 8935 } 8936 fileRenamed = true; 8937 configFilePrev = configFile; 8938 configFilePrev += "-prev"; 8939 newConfigFilePrev = newConfigFile; 8940 newConfigFilePrev += "-prev"; 8941 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0); 8942 } 8943 } 8944 8945 // update m_strConfigFileFull amd mConfigFile 8946 mData->m_strConfigFileFull = newConfigFile; 8947 // compute the relative path too 8948 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile); 8949 8950 // store the old and new so that VirtualBox::saveSettings() can update 8951 // the media registry 8952 if ( mData->mRegistered 8953 && configDir != newConfigDir) 8954 { 8955 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir); 8956 8957 if (pfNeedsGlobalSaveSettings) 8958 *pfNeedsGlobalSaveSettings = true; 8959 } 8960 8961 // in the saved state file path, replace the old directory with the new directory 8962 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str())) 8963 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length()); 8964 8965 // and do the same thing for the saved state file paths of all the online snapshots 8966 if (mData->mFirstSnapshot) 8967 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(), 8968 newConfigDir.c_str()); 8969 } 8970 while (0); 8971 8972 if (FAILED(rc)) 8973 { 8974 /* silently try to rename everything back */ 8975 if (fileRenamed) 8976 { 8977 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0); 8978 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0); 8979 } 8980 if (dirRenamed) 8981 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0); 8982 } 8983 8984 if (FAILED(rc)) return rc; 8985 } 8986 8987 if (fSettingsFileIsNew) 8988 { 8989 /* create a virgin config file */ 8990 int vrc = VINF_SUCCESS; 8991 8992 /* ensure the settings directory exists */ 8993 Utf8Str path(mData->m_strConfigFileFull); 8994 path.stripFilename(); 8995 if (!RTDirExists(path.c_str())) 8996 { 8997 vrc = RTDirCreateFullPath(path.c_str(), 0777); 8998 if (RT_FAILURE(vrc)) 8999 { 9000 return setError(E_FAIL, 9001 tr("Could not create a directory '%s' to save the settings file (%Rrc)"), 9002 path.c_str(), 9003 vrc); 9004 } 9005 } 9006 9007 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */ 9008 path = Utf8Str(mData->m_strConfigFileFull); 9009 RTFILE f = NIL_RTFILE; 9010 vrc = RTFileOpen(&f, path.c_str(), 9011 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE); 9012 if (RT_FAILURE(vrc)) 9013 return setError(E_FAIL, 9014 tr("Could not create the settings file '%s' (%Rrc)"), 9015 path.c_str(), 9016 vrc); 9017 RTFileClose(f); 9018 } 9019 9020 return rc; 9021 } 9022 9023 /** 9024 * Saves and commits machine data, user data and hardware data. 9025 * 9026 * Note that on failure, the data remains uncommitted. 9027 * 9028 * @a aFlags may combine the following flags: 9029 * 9030 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE. 9031 * Used when saving settings after an operation that makes them 100% 9032 * correspond to the settings from the current snapshot. 9033 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if 9034 * #isReallyModified() returns false. This is necessary for cases when we 9035 * change machine data directly, not through the backup()/commit() mechanism. 9036 * - SaveS_Force: settings will be saved without doing a deep compare of the 9037 * settings structures. This is used when this is called because snapshots 9038 * have changed to avoid the overhead of the deep compare. 9039 * 9040 * @note Must be called from under this object's write lock. Locks children for 9041 * writing. 9042 * 9043 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been 9044 * initialized to false and that will be set to true by this function if 9045 * the caller must invoke VirtualBox::saveSettings() because the global 9046 * settings have changed. This will happen if a machine rename has been 9047 * saved and the global machine and media registries will therefore need 9048 * updating. 9049 */ 9050 HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings, 9051 int aFlags /*= 0*/) 9052 { 9053 LogFlowThisFuncEnter(); 9054 9055 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 9056 9057 /* make sure child objects are unable to modify the settings while we are 9058 * saving them */ 9059 ensureNoStateDependencies(); 9060 9061 AssertReturn(!isSnapshotMachine(), 9062 E_FAIL); 9063 9064 HRESULT rc = S_OK; 9065 bool fNeedsWrite = false; 9066 9067 /* First, prepare to save settings. It will care about renaming the 9068 * settings directory and file if the machine name was changed and about 9069 * creating a new settings file if this is a new machine. */ 9070 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings); 9071 if (FAILED(rc)) return rc; 9072 9073 // keep a pointer to the current settings structures 9074 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile; 9075 settings::MachineConfigFile *pNewConfig = NULL; 9076 9077 try 9078 { 9079 // make a fresh one to have everyone write stuff into 9080 pNewConfig = new settings::MachineConfigFile(NULL); 9081 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile); 9082 9083 // now go and copy all the settings data from COM to the settings structures 9084 // (this calles saveSettings() on all the COM objects in the machine) 9085 copyMachineDataToSettings(*pNewConfig); 9086 9087 if (aFlags & SaveS_ResetCurStateModified) 9088 { 9089 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot() 9090 mData->mCurrentStateModified = FALSE; 9091 fNeedsWrite = true; // always, no need to compare 9092 } 9093 else if (aFlags & SaveS_Force) 9094 { 9095 fNeedsWrite = true; // always, no need to compare 9096 } 9097 else 9098 { 9099 if (!mData->mCurrentStateModified) 9100 { 9101 // do a deep compare of the settings that we just saved with the settings 9102 // previously stored in the config file; this invokes MachineConfigFile::operator== 9103 // which does a deep compare of all the settings, which is expensive but less expensive 9104 // than writing out XML in vain 9105 bool fAnySettingsChanged = (*pNewConfig == *pOldConfig); 9106 9107 // could still be modified if any settings changed 9108 mData->mCurrentStateModified = fAnySettingsChanged; 9109 9110 fNeedsWrite = fAnySettingsChanged; 9111 } 9112 else 9113 fNeedsWrite = true; 9114 } 9115 9116 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified; 9117 9118 if (fNeedsWrite) 9119 // now spit it all out! 9120 pNewConfig->write(mData->m_strConfigFileFull); 9121 9122 mData->pMachineConfigFile = pNewConfig; 9123 delete pOldConfig; 9124 commit(); 9125 9126 // after saving settings, we are no longer different from the XML on disk 9127 mData->flModifications = 0; 9128 } 9129 catch (HRESULT err) 9130 { 9131 // we assume that error info is set by the thrower 9132 rc = err; 9133 9134 // restore old config 9135 delete pNewConfig; 9136 mData->pMachineConfigFile = pOldConfig; 9137 } 9138 catch (...) 9139 { 9140 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 9141 } 9142 9143 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway)) 9144 { 9145 /* Fire the data change event, even on failure (since we've already 9146 * committed all data). This is done only for SessionMachines because 9147 * mutable Machine instances are always not registered (i.e. private 9148 * to the client process that creates them) and thus don't need to 9149 * inform callbacks. */ 9150 if (isSessionMachine()) 9151 mParent->onMachineDataChange(mData->mUuid); 9152 } 9153 9154 LogFlowThisFunc(("rc=%08X\n", rc)); 9155 LogFlowThisFuncLeave(); 9156 return rc; 9157 } 9158 9159 /** 9160 * Implementation for saving the machine settings into the given 9161 * settings::MachineConfigFile instance. This copies machine extradata 9162 * from the previous machine config file in the instance data, if any. 9163 * 9164 * This gets called from two locations: 9165 * 9166 * -- Machine::saveSettings(), during the regular XML writing; 9167 * 9168 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets 9169 * exported to OVF and we write the VirtualBox proprietary XML 9170 * into a <vbox:Machine> tag. 9171 * 9172 * This routine fills all the fields in there, including snapshots, *except* 9173 * for the following: 9174 * 9175 * -- fCurrentStateModified. There is some special logic associated with that. 9176 * 9177 * The caller can then call MachineConfigFile::write() or do something else 9178 * with it. 9179 * 9180 * Caller must hold the machine lock! 9181 * 9182 * This throws XML errors and HRESULT, so the caller must have a catch block! 9183 */ 9184 void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config) 9185 { 9186 // deep copy extradata 9187 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems; 9188 9189 config.uuid = mData->mUuid; 9190 9191 // copy name, description, OS type, teleport, UTC etc. 9192 config.machineUserData = mUserData->s; 9193 9194 if ( mData->mMachineState == MachineState_Saved 9195 || mData->mMachineState == MachineState_Restoring 9196 // when deleting a snapshot we may or may not have a saved state in the current state, 9197 // so let's not assert here please 9198 || ( ( mData->mMachineState == MachineState_DeletingSnapshot 9199 || mData->mMachineState == MachineState_DeletingSnapshotOnline 9200 || mData->mMachineState == MachineState_DeletingSnapshotPaused) 9201 && (!mSSData->strStateFilePath.isEmpty()) 9202 ) 9203 ) 9204 { 9205 Assert(!mSSData->strStateFilePath.isEmpty()); 9206 /* try to make the file name relative to the settings file dir */ 9207 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile); 9208 } 9209 else 9210 { 9211 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving); 9212 config.strStateFile.setNull(); 9213 } 9214 9215 if (mData->mCurrentSnapshot) 9216 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId(); 9217 else 9218 config.uuidCurrentSnapshot.clear(); 9219 9220 config.timeLastStateChange = mData->mLastStateChange; 9221 config.fAborted = (mData->mMachineState == MachineState_Aborted); 9222 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported); 9223 9224 HRESULT rc = saveHardware(config.hardwareMachine); 9225 if (FAILED(rc)) throw rc; 9226 9227 rc = saveStorageControllers(config.storageMachine); 9228 if (FAILED(rc)) throw rc; 9229 9230 // save machine's media registry if this is VirtualBox 4.0 or later 9231 if (config.canHaveOwnMediaRegistry()) 9232 { 9233 // determine machine folder 9234 Utf8Str strMachineFolder = getSettingsFileFull(); 9235 strMachineFolder.stripFilename(); 9236 mParent->saveMediaRegistry(config.mediaRegistry, 9237 getId(), // only media with registry ID == machine UUID 9238 strMachineFolder); 9239 // this throws HRESULT 9240 } 9241 9242 // save snapshots 9243 rc = saveAllSnapshots(config); 9244 if (FAILED(rc)) throw rc; 9245 } 9246 9247 /** 9248 * Saves all snapshots of the machine into the given machine config file. Called 9249 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler(). 9250 * @param config 9251 * @return 9252 */ 9253 HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config) 9254 { 9255 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 9256 9257 HRESULT rc = S_OK; 9258 9259 try 9260 { 9261 config.llFirstSnapshot.clear(); 9262 9263 if (mData->mFirstSnapshot) 9264 { 9265 settings::Snapshot snapNew; 9266 config.llFirstSnapshot.push_back(snapNew); 9267 9268 // get reference to the fresh copy of the snapshot on the list and 9269 // work on that copy directly to avoid excessive copying later 9270 settings::Snapshot &snap = config.llFirstSnapshot.front(); 9271 9272 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/); 9273 if (FAILED(rc)) throw rc; 9274 } 9275 9276 // if (mType == IsSessionMachine) 9277 // mParent->onMachineDataChange(mData->mUuid); @todo is this necessary? 9278 9279 } 9280 catch (HRESULT err) 9281 { 9282 /* we assume that error info is set by the thrower */ 9283 rc = err; 9284 } 9285 catch (...) 9286 { 9287 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 9288 } 9289 9290 return rc; 9291 } 9292 9293 /** 9294 * Saves the VM hardware configuration. It is assumed that the 9295 * given node is empty. 9296 * 9297 * @param aNode <Hardware> node to save the VM hardware configuration to. 9298 */ 9299 HRESULT Machine::saveHardware(settings::Hardware &data) 9300 { 9301 HRESULT rc = S_OK; 9302 9303 try 9304 { 9305 /* The hardware version attribute (optional). 9306 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */ 9307 if ( mHWData->mHWVersion == "1" 9308 && mSSData->strStateFilePath.isEmpty() 9309 ) 9310 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. */ 9311 9312 data.strVersion = mHWData->mHWVersion; 9313 data.uuid = mHWData->mHardwareUUID; 9314 9315 // CPU 9316 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled; 9317 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive; 9318 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled; 9319 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled; 9320 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled; 9321 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled; 9322 data.fPAE = !!mHWData->mPAEEnabled; 9323 data.fSyntheticCpu = !!mHWData->mSyntheticCpu; 9324 9325 /* Standard and Extended CPUID leafs. */ 9326 data.llCpuIdLeafs.clear(); 9327 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++) 9328 { 9329 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX) 9330 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]); 9331 } 9332 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++) 9333 { 9334 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX) 9335 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]); 9336 } 9337 9338 data.cCPUs = mHWData->mCPUCount; 9339 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled; 9340 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap; 9341 9342 data.llCpus.clear(); 9343 if (data.fCpuHotPlug) 9344 { 9345 for (unsigned idx = 0; idx < data.cCPUs; idx++) 9346 { 9347 if (mHWData->mCPUAttached[idx]) 9348 { 9349 settings::Cpu cpu; 9350 cpu.ulId = idx; 9351 data.llCpus.push_back(cpu); 9352 } 9353 } 9354 } 9355 9356 // memory 9357 data.ulMemorySizeMB = mHWData->mMemorySize; 9358 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled; 9359 9360 // firmware 9361 data.firmwareType = mHWData->mFirmwareType; 9362 9363 // HID 9364 data.pointingHidType = mHWData->mPointingHidType; 9365 data.keyboardHidType = mHWData->mKeyboardHidType; 9366 9367 // chipset 9368 data.chipsetType = mHWData->mChipsetType; 9369 9370 // HPET 9371 data.fHpetEnabled = !!mHWData->mHpetEnabled; 9372 9373 // boot order 9374 data.mapBootOrder.clear(); 9375 for (size_t i = 0; 9376 i < RT_ELEMENTS(mHWData->mBootOrder); 9377 ++i) 9378 data.mapBootOrder[i] = mHWData->mBootOrder[i]; 9379 9380 // display 9381 data.ulVRAMSizeMB = mHWData->mVRAMSize; 9382 data.cMonitors = mHWData->mMonitorCount; 9383 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled; 9384 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled; 9385 9386 /* VRDEServer settings (optional) */ 9387 rc = mVRDEServer->saveSettings(data.vrdeSettings); 9388 if (FAILED(rc)) throw rc; 9389 9390 /* BIOS (required) */ 9391 rc = mBIOSSettings->saveSettings(data.biosSettings); 9392 if (FAILED(rc)) throw rc; 9393 9394 /* USB Controller (required) */ 9395 rc = mUSBController->saveSettings(data.usbController); 9396 if (FAILED(rc)) throw rc; 9397 9398 /* Network adapters (required) */ 9399 data.llNetworkAdapters.clear(); 9400 for (ULONG slot = 0; 9401 slot < RT_ELEMENTS(mNetworkAdapters); 9402 ++slot) 9403 { 9404 settings::NetworkAdapter nic; 9405 nic.ulSlot = slot; 9406 rc = mNetworkAdapters[slot]->saveSettings(nic); 9407 if (FAILED(rc)) throw rc; 9408 9409 data.llNetworkAdapters.push_back(nic); 9410 } 9411 9412 /* Serial ports */ 9413 data.llSerialPorts.clear(); 9414 for (ULONG slot = 0; 9415 slot < RT_ELEMENTS(mSerialPorts); 9416 ++slot) 9417 { 9418 settings::SerialPort s; 9419 s.ulSlot = slot; 9420 rc = mSerialPorts[slot]->saveSettings(s); 9421 if (FAILED(rc)) return rc; 9422 9423 data.llSerialPorts.push_back(s); 9424 } 9425 9426 /* Parallel ports */ 9427 data.llParallelPorts.clear(); 9428 for (ULONG slot = 0; 9429 slot < RT_ELEMENTS(mParallelPorts); 9430 ++slot) 9431 { 9432 settings::ParallelPort p; 9433 p.ulSlot = slot; 9434 rc = mParallelPorts[slot]->saveSettings(p); 9435 if (FAILED(rc)) return rc; 9436 9437 data.llParallelPorts.push_back(p); 9438 } 9439 9440 /* Audio adapter */ 9441 rc = mAudioAdapter->saveSettings(data.audioAdapter); 9442 if (FAILED(rc)) return rc; 9443 9444 /* Shared folders */ 9445 data.llSharedFolders.clear(); 9446 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 9447 it != mHWData->mSharedFolders.end(); 9448 ++it) 9449 { 9450 SharedFolder *pSF = *it; 9451 AutoCaller sfCaller(pSF); 9452 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS); 9453 settings::SharedFolder sf; 9454 sf.strName = pSF->getName(); 9455 sf.strHostPath = pSF->getHostPath(); 9456 sf.fWritable = !!pSF->isWritable(); 9457 sf.fAutoMount = !!pSF->isAutoMounted(); 9458 9459 data.llSharedFolders.push_back(sf); 9460 } 9461 9462 // clipboard 9463 data.clipboardMode = mHWData->mClipboardMode; 9464 9465 /* Guest */ 9466 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize; 9467 9468 // IO settings 9469 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled; 9470 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize; 9471 9472 /* BandwidthControl (required) */ 9473 rc = mBandwidthControl->saveSettings(data.ioSettings); 9474 if (FAILED(rc)) throw rc; 9475 9476 /* Host PCI devices */ 9477 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin(); 9478 it != mHWData->mPciDeviceAssignments.end(); 9479 ++it) 9480 { 9481 ComObjPtr<PciDeviceAttachment> pda = *it; 9482 settings::HostPciDeviceAttachment hpda; 9483 9484 rc = pda->saveSettings(hpda); 9485 if (FAILED(rc)) throw rc; 9486 9487 data.pciAttachments.push_back(hpda); 9488 } 9489 9490 9491 // guest properties 9492 data.llGuestProperties.clear(); 9493 #ifdef VBOX_WITH_GUEST_PROPS 9494 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin(); 9495 it != mHWData->mGuestProperties.end(); 9496 ++it) 9497 { 9498 HWData::GuestProperty property = *it; 9499 9500 /* Remove transient guest properties at shutdown unless we 9501 * are saving state */ 9502 if ( ( mData->mMachineState == MachineState_PoweredOff 9503 || mData->mMachineState == MachineState_Aborted 9504 || mData->mMachineState == MachineState_Teleported) 9505 && ( property.mFlags & guestProp::TRANSIENT 9506 || property.mFlags & guestProp::TRANSRESET)) 9507 continue; 9508 settings::GuestProperty prop; 9509 prop.strName = property.strName; 9510 prop.strValue = property.strValue; 9511 prop.timestamp = property.mTimestamp; 9512 char szFlags[guestProp::MAX_FLAGS_LEN + 1]; 9513 guestProp::writeFlags(property.mFlags, szFlags); 9514 prop.strFlags = szFlags; 9515 9516 data.llGuestProperties.push_back(prop); 9517 } 9518 9519 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns; 9520 /* I presume this doesn't require a backup(). */ 9521 mData->mGuestPropertiesModified = FALSE; 9522 #endif /* VBOX_WITH_GUEST_PROPS defined */ 9523 } 9524 catch(std::bad_alloc &) 9525 { 9526 return E_OUTOFMEMORY; 9527 } 9528 9529 AssertComRC(rc); 9530 return rc; 9531 } 9532 9533 /** 9534 * Saves the storage controller configuration. 9535 * 9536 * @param aNode <StorageControllers> node to save the VM hardware configuration to. 9537 */ 9538 HRESULT Machine::saveStorageControllers(settings::Storage &data) 9539 { 9540 data.llStorageControllers.clear(); 9541 9542 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 9543 it != mStorageControllers->end(); 9544 ++it) 9545 { 9546 HRESULT rc; 9547 ComObjPtr<StorageController> pCtl = *it; 9548 9549 settings::StorageController ctl; 9550 ctl.strName = pCtl->getName(); 9551 ctl.controllerType = pCtl->getControllerType(); 9552 ctl.storageBus = pCtl->getStorageBus(); 9553 ctl.ulInstance = pCtl->getInstance(); 9554 ctl.fBootable = pCtl->getBootable(); 9555 9556 /* Save the port count. */ 9557 ULONG portCount; 9558 rc = pCtl->COMGETTER(PortCount)(&portCount); 9559 ComAssertComRCRet(rc, rc); 9560 ctl.ulPortCount = portCount; 9561 9562 /* Save fUseHostIOCache */ 9563 BOOL fUseHostIOCache; 9564 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache); 9565 ComAssertComRCRet(rc, rc); 9566 ctl.fUseHostIOCache = !!fUseHostIOCache; 9567 9568 /* Save IDE emulation settings. */ 9569 if (ctl.controllerType == StorageControllerType_IntelAhci) 9570 { 9571 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort))) 9572 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort))) 9573 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort))) 9574 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort))) 9575 ) 9576 ComAssertComRCRet(rc, rc); 9577 } 9578 9579 /* save the devices now. */ 9580 rc = saveStorageDevices(pCtl, ctl); 9581 ComAssertComRCRet(rc, rc); 9582 9583 data.llStorageControllers.push_back(ctl); 9584 } 9585 9586 return S_OK; 9587 } 9588 9589 /** 9590 * Saves the hard disk configuration. 9591 */ 9592 HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController, 9593 settings::StorageController &data) 9594 { 9595 MediaData::AttachmentList atts; 9596 9597 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts); 9598 if (FAILED(rc)) return rc; 9599 9600 data.llAttachedDevices.clear(); 9601 for (MediaData::AttachmentList::const_iterator it = atts.begin(); 9602 it != atts.end(); 9603 ++it) 9604 { 9605 settings::AttachedDevice dev; 9606 9607 MediumAttachment *pAttach = *it; 9608 Medium *pMedium = pAttach->getMedium(); 9609 9610 dev.deviceType = pAttach->getType(); 9611 dev.lPort = pAttach->getPort(); 9612 dev.lDevice = pAttach->getDevice(); 9613 if (pMedium) 9614 { 9615 if (pMedium->isHostDrive()) 9616 dev.strHostDriveSrc = pMedium->getLocationFull(); 9617 else 9618 dev.uuid = pMedium->getId(); 9619 dev.fPassThrough = pAttach->getPassthrough(); 9620 } 9621 9622 dev.strBwGroup = pAttach->getBandwidthGroup(); 9623 9624 data.llAttachedDevices.push_back(dev); 9625 } 9626 9627 return S_OK; 9628 } 9629 9630 /** 9631 * Saves machine state settings as defined by aFlags 9632 * (SaveSTS_* values). 9633 * 9634 * @param aFlags Combination of SaveSTS_* flags. 9635 * 9636 * @note Locks objects for writing. 9637 */ 9638 HRESULT Machine::saveStateSettings(int aFlags) 9639 { 9640 if (aFlags == 0) 9641 return S_OK; 9642 9643 AutoCaller autoCaller(this); 9644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 9645 9646 /* This object's write lock is also necessary to serialize file access 9647 * (prevent concurrent reads and writes) */ 9648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 9649 9650 HRESULT rc = S_OK; 9651 9652 Assert(mData->pMachineConfigFile); 9653 9654 try 9655 { 9656 if (aFlags & SaveSTS_CurStateModified) 9657 mData->pMachineConfigFile->fCurrentStateModified = true; 9658 9659 if (aFlags & SaveSTS_StateFilePath) 9660 { 9661 if (!mSSData->strStateFilePath.isEmpty()) 9662 /* try to make the file name relative to the settings file dir */ 9663 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile); 9664 else 9665 mData->pMachineConfigFile->strStateFile.setNull(); 9666 } 9667 9668 if (aFlags & SaveSTS_StateTimeStamp) 9669 { 9670 Assert( mData->mMachineState != MachineState_Aborted 9671 || mSSData->strStateFilePath.isEmpty()); 9672 9673 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange; 9674 9675 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted); 9676 //@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported); 9677 } 9678 9679 mData->pMachineConfigFile->write(mData->m_strConfigFileFull); 9680 } 9681 catch (...) 9682 { 9683 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 9684 } 9685 9686 return rc; 9687 } 9688 9689 /** 9690 * Ensures that the given medium is added to a media registry. If this machine 9691 * was created with 4.0 or later, then the machine registry is used. Otherwise 9692 * the global VirtualBox media registry is used. If the medium was actually 9693 * added to a registry (because it wasn't in the registry yet), the UUID of 9694 * that registry is added to the given list so that the caller can save the 9695 * registry. 9696 * 9697 * Caller must hold machine read lock! 9698 * 9699 * @param pMedium 9700 * @param llRegistriesThatNeedSaving 9701 * @param puuid Optional buffer that receives the registry UUID that was used. 9702 */ 9703 void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium, 9704 GuidList &llRegistriesThatNeedSaving, 9705 Guid *puuid) 9706 { 9707 // decide which medium registry to use now that the medium is attached: 9708 Guid uuid; 9709 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry()) 9710 // machine XML is VirtualBox 4.0 or higher: 9711 uuid = getId(); // machine UUID 9712 else 9713 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID 9714 9715 AutoCaller autoCaller(pMedium); 9716 if (FAILED(autoCaller.rc())) return; 9717 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 9718 9719 if (pMedium->addRegistry(uuid, false /* fRecurse */)) 9720 // registry actually changed: 9721 mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid); 9722 9723 if (puuid) 9724 *puuid = uuid; 9725 } 9726 9727 /** 9728 * Creates differencing hard disks for all normal hard disks attached to this 9729 * machine and a new set of attachments to refer to created disks. 9730 * 9731 * Used when taking a snapshot or when deleting the current state. Gets called 9732 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler(). 9733 * 9734 * This method assumes that mMediaData contains the original hard disk attachments 9735 * it needs to create diffs for. On success, these attachments will be replaced 9736 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly 9737 * called to delete created diffs which will also rollback mMediaData and restore 9738 * whatever was backed up before calling this method. 9739 * 9740 * Attachments with non-normal hard disks are left as is. 9741 * 9742 * If @a aOnline is @c false then the original hard disks that require implicit 9743 * diffs will be locked for reading. Otherwise it is assumed that they are 9744 * already locked for writing (when the VM was started). Note that in the latter 9745 * case it is responsibility of the caller to lock the newly created diffs for 9746 * writing if this method succeeds. 9747 * 9748 * @param aProgress Progress object to run (must contain at least as 9749 * many operations left as the number of hard disks 9750 * attached). 9751 * @param aOnline Whether the VM was online prior to this operation. 9752 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving 9753 * 9754 * @note The progress object is not marked as completed, neither on success nor 9755 * on failure. This is a responsibility of the caller. 9756 * 9757 * @note Locks this object for writing. 9758 */ 9759 HRESULT Machine::createImplicitDiffs(IProgress *aProgress, 9760 ULONG aWeight, 9761 bool aOnline, 9762 GuidList *pllRegistriesThatNeedSaving) 9763 { 9764 LogFlowThisFunc(("aOnline=%d\n", aOnline)); 9765 9766 AutoCaller autoCaller(this); 9767 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 9768 9769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 9770 9771 /* must be in a protective state because we leave the lock below */ 9772 AssertReturn( mData->mMachineState == MachineState_Saving 9773 || mData->mMachineState == MachineState_LiveSnapshotting 9774 || mData->mMachineState == MachineState_RestoringSnapshot 9775 || mData->mMachineState == MachineState_DeletingSnapshot 9776 , E_FAIL); 9777 9778 HRESULT rc = S_OK; 9779 9780 MediumLockListMap lockedMediaOffline; 9781 MediumLockListMap *lockedMediaMap; 9782 if (aOnline) 9783 lockedMediaMap = &mData->mSession.mLockedMedia; 9784 else 9785 lockedMediaMap = &lockedMediaOffline; 9786 9787 try 9788 { 9789 if (!aOnline) 9790 { 9791 /* lock all attached hard disks early to detect "in use" 9792 * situations before creating actual diffs */ 9793 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 9794 it != mMediaData->mAttachments.end(); 9795 ++it) 9796 { 9797 MediumAttachment* pAtt = *it; 9798 if (pAtt->getType() == DeviceType_HardDisk) 9799 { 9800 Medium* pMedium = pAtt->getMedium(); 9801 Assert(pMedium); 9802 9803 MediumLockList *pMediumLockList(new MediumLockList()); 9804 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */, 9805 false /* fMediumLockWrite */, 9806 NULL, 9807 *pMediumLockList); 9808 if (FAILED(rc)) 9809 { 9810 delete pMediumLockList; 9811 throw rc; 9812 } 9813 rc = lockedMediaMap->Insert(pAtt, pMediumLockList); 9814 if (FAILED(rc)) 9815 { 9816 throw setError(rc, 9817 tr("Collecting locking information for all attached media failed")); 9818 } 9819 } 9820 } 9821 9822 /* Now lock all media. If this fails, nothing is locked. */ 9823 rc = lockedMediaMap->Lock(); 9824 if (FAILED(rc)) 9825 { 9826 throw setError(rc, 9827 tr("Locking of attached media failed")); 9828 } 9829 } 9830 9831 /* remember the current list (note that we don't use backup() since 9832 * mMediaData may be already backed up) */ 9833 MediaData::AttachmentList atts = mMediaData->mAttachments; 9834 9835 /* start from scratch */ 9836 mMediaData->mAttachments.clear(); 9837 9838 /* go through remembered attachments and create diffs for normal hard 9839 * disks and attach them */ 9840 for (MediaData::AttachmentList::const_iterator it = atts.begin(); 9841 it != atts.end(); 9842 ++it) 9843 { 9844 MediumAttachment* pAtt = *it; 9845 9846 DeviceType_T devType = pAtt->getType(); 9847 Medium* pMedium = pAtt->getMedium(); 9848 9849 if ( devType != DeviceType_HardDisk 9850 || pMedium == NULL 9851 || pMedium->getType() != MediumType_Normal) 9852 { 9853 /* copy the attachment as is */ 9854 9855 /** @todo the progress object created in Console::TakeSnaphot 9856 * only expects operations for hard disks. Later other 9857 * device types need to show up in the progress as well. */ 9858 if (devType == DeviceType_HardDisk) 9859 { 9860 if (pMedium == NULL) 9861 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(), 9862 aWeight); // weight 9863 else 9864 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"), 9865 pMedium->getBase()->getName().c_str()).raw(), 9866 aWeight); // weight 9867 } 9868 9869 mMediaData->mAttachments.push_back(pAtt); 9870 continue; 9871 } 9872 9873 /* need a diff */ 9874 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"), 9875 pMedium->getBase()->getName().c_str()).raw(), 9876 aWeight); // weight 9877 9878 Utf8Str strFullSnapshotFolder; 9879 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 9880 9881 ComObjPtr<Medium> diff; 9882 diff.createObject(); 9883 // store the diff in the same registry as the parent 9884 // (this cannot fail here because we can't create implicit diffs for 9885 // unregistered images) 9886 Guid uuidRegistryParent; 9887 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent); 9888 Assert(fInRegistry); NOREF(fInRegistry); 9889 rc = diff->init(mParent, 9890 pMedium->getPreferredDiffFormat(), 9891 strFullSnapshotFolder.append(RTPATH_SLASH_STR), 9892 uuidRegistryParent, 9893 pllRegistriesThatNeedSaving); 9894 if (FAILED(rc)) throw rc; 9895 9896 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before 9897 * the push_back? Looks like we're going to leave medium with the 9898 * wrong kind of lock (general issue with if we fail anywhere at all) 9899 * and an orphaned VDI in the snapshots folder. */ 9900 9901 /* update the appropriate lock list */ 9902 MediumLockList *pMediumLockList; 9903 rc = lockedMediaMap->Get(pAtt, pMediumLockList); 9904 AssertComRCThrowRC(rc); 9905 if (aOnline) 9906 { 9907 rc = pMediumLockList->Update(pMedium, false); 9908 AssertComRCThrowRC(rc); 9909 } 9910 9911 /* leave the lock before the potentially lengthy operation */ 9912 alock.leave(); 9913 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard, 9914 pMediumLockList, 9915 NULL /* aProgress */, 9916 true /* aWait */, 9917 pllRegistriesThatNeedSaving); 9918 alock.enter(); 9919 if (FAILED(rc)) throw rc; 9920 9921 rc = lockedMediaMap->Unlock(); 9922 AssertComRCThrowRC(rc); 9923 rc = pMediumLockList->Append(diff, true); 9924 AssertComRCThrowRC(rc); 9925 rc = lockedMediaMap->Lock(); 9926 AssertComRCThrowRC(rc); 9927 9928 rc = diff->addBackReference(mData->mUuid); 9929 AssertComRCThrowRC(rc); 9930 9931 /* add a new attachment */ 9932 ComObjPtr<MediumAttachment> attachment; 9933 attachment.createObject(); 9934 rc = attachment->init(this, 9935 diff, 9936 pAtt->getControllerName(), 9937 pAtt->getPort(), 9938 pAtt->getDevice(), 9939 DeviceType_HardDisk, 9940 true /* aImplicit */, 9941 pAtt->getBandwidthGroup()); 9942 if (FAILED(rc)) throw rc; 9943 9944 rc = lockedMediaMap->ReplaceKey(pAtt, attachment); 9945 AssertComRCThrowRC(rc); 9946 mMediaData->mAttachments.push_back(attachment); 9947 } 9948 } 9949 catch (HRESULT aRC) { rc = aRC; } 9950 9951 /* unlock all hard disks we locked */ 9952 if (!aOnline) 9953 { 9954 ErrorInfoKeeper eik; 9955 9956 HRESULT rc1 = lockedMediaMap->Clear(); 9957 AssertComRC(rc1); 9958 } 9959 9960 if (FAILED(rc)) 9961 { 9962 MultiResult mrc = rc; 9963 9964 mrc = deleteImplicitDiffs(pllRegistriesThatNeedSaving); 9965 } 9966 9967 return rc; 9968 } 9969 9970 /** 9971 * Deletes implicit differencing hard disks created either by 9972 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData. 9973 * 9974 * Note that to delete hard disks created by #AttachMedium() this method is 9975 * called from #fixupMedia() when the changes are rolled back. 9976 * 9977 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving 9978 * 9979 * @note Locks this object for writing. 9980 */ 9981 HRESULT Machine::deleteImplicitDiffs(GuidList *pllRegistriesThatNeedSaving) 9982 { 9983 AutoCaller autoCaller(this); 9984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 9985 9986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 9987 LogFlowThisFuncEnter(); 9988 9989 AssertReturn(mMediaData.isBackedUp(), E_FAIL); 9990 9991 HRESULT rc = S_OK; 9992 9993 MediaData::AttachmentList implicitAtts; 9994 9995 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 9996 9997 /* enumerate new attachments */ 9998 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 9999 it != mMediaData->mAttachments.end(); 10000 ++it) 10001 { 10002 ComObjPtr<Medium> hd = (*it)->getMedium(); 10003 if (hd.isNull()) 10004 continue; 10005 10006 if ((*it)->isImplicit()) 10007 { 10008 /* deassociate and mark for deletion */ 10009 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName())); 10010 rc = hd->removeBackReference(mData->mUuid); 10011 AssertComRC(rc); 10012 implicitAtts.push_back(*it); 10013 continue; 10014 } 10015 10016 /* was this hard disk attached before? */ 10017 if (!findAttachment(oldAtts, hd)) 10018 { 10019 /* no: de-associate */ 10020 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName())); 10021 rc = hd->removeBackReference(mData->mUuid); 10022 AssertComRC(rc); 10023 continue; 10024 } 10025 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName())); 10026 } 10027 10028 /* rollback hard disk changes */ 10029 mMediaData.rollback(); 10030 10031 MultiResult mrc(S_OK); 10032 10033 /* delete unused implicit diffs */ 10034 if (implicitAtts.size() != 0) 10035 { 10036 /* will leave the lock before the potentially lengthy 10037 * operation, so protect with the special state (unless already 10038 * protected) */ 10039 MachineState_T oldState = mData->mMachineState; 10040 if ( oldState != MachineState_Saving 10041 && oldState != MachineState_LiveSnapshotting 10042 && oldState != MachineState_RestoringSnapshot 10043 && oldState != MachineState_DeletingSnapshot 10044 && oldState != MachineState_DeletingSnapshotOnline 10045 && oldState != MachineState_DeletingSnapshotPaused 10046 ) 10047 setMachineState(MachineState_SettingUp); 10048 10049 alock.leave(); 10050 10051 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); 10052 it != implicitAtts.end(); 10053 ++it) 10054 { 10055 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName())); 10056 ComObjPtr<Medium> hd = (*it)->getMedium(); 10057 10058 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/, 10059 pllRegistriesThatNeedSaving); 10060 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() )); 10061 mrc = rc; 10062 } 10063 10064 alock.enter(); 10065 10066 if (mData->mMachineState == MachineState_SettingUp) 10067 setMachineState(oldState); 10068 } 10069 10070 return mrc; 10071 } 10072 10073 /** 10074 * Looks through the given list of media attachments for one with the given parameters 10075 * and returns it, or NULL if not found. The list is a parameter so that backup lists 10076 * can be searched as well if needed. 10077 * 10078 * @param list 10079 * @param aControllerName 10080 * @param aControllerPort 10081 * @param aDevice 10082 * @return 10083 */ 10084 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 10085 IN_BSTR aControllerName, 10086 LONG aControllerPort, 10087 LONG aDevice) 10088 { 10089 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 10090 it != ll.end(); 10091 ++it) 10092 { 10093 MediumAttachment *pAttach = *it; 10094 if (pAttach->matches(aControllerName, aControllerPort, aDevice)) 10095 return pAttach; 10096 } 10097 10098 return NULL; 10099 } 10100 10101 /** 10102 * Looks through the given list of media attachments for one with the given parameters 10103 * and returns it, or NULL if not found. The list is a parameter so that backup lists 10104 * can be searched as well if needed. 10105 * 10106 * @param list 10107 * @param aControllerName 10108 * @param aControllerPort 10109 * @param aDevice 10110 * @return 10111 */ 10112 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 10113 ComObjPtr<Medium> pMedium) 10114 { 10115 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 10116 it != ll.end(); 10117 ++it) 10118 { 10119 MediumAttachment *pAttach = *it; 10120 ComObjPtr<Medium> pMediumThis = pAttach->getMedium(); 10121 if (pMediumThis == pMedium) 10122 return pAttach; 10123 } 10124 10125 return NULL; 10126 } 10127 10128 /** 10129 * Looks through the given list of media attachments for one with the given parameters 10130 * and returns it, or NULL if not found. The list is a parameter so that backup lists 10131 * can be searched as well if needed. 10132 * 10133 * @param list 10134 * @param aControllerName 10135 * @param aControllerPort 10136 * @param aDevice 10137 * @return 10138 */ 10139 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 10140 Guid &id) 10141 { 10142 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 10143 it != ll.end(); 10144 ++it) 10145 { 10146 MediumAttachment *pAttach = *it; 10147 ComObjPtr<Medium> pMediumThis = pAttach->getMedium(); 10148 if (pMediumThis->getId() == id) 10149 return pAttach; 10150 } 10151 10152 return NULL; 10153 } 10154 10155 /** 10156 * Main implementation for Machine::DetachDevice. This also gets called 10157 * from Machine::prepareUnregister() so it has been taken out for simplicity. 10158 * 10159 * @param pAttach Medium attachment to detach. 10160 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here. 10161 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot. 10162 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving 10163 * @return 10164 */ 10165 HRESULT Machine::detachDevice(MediumAttachment *pAttach, 10166 AutoWriteLock &writeLock, 10167 Snapshot *pSnapshot, 10168 GuidList *pllRegistriesThatNeedSaving) 10169 { 10170 ComObjPtr<Medium> oldmedium = pAttach->getMedium(); 10171 DeviceType_T mediumType = pAttach->getType(); 10172 10173 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL")); 10174 10175 if (pAttach->isImplicit()) 10176 { 10177 /* attempt to implicitly delete the implicitly created diff */ 10178 10179 /// @todo move the implicit flag from MediumAttachment to Medium 10180 /// and forbid any hard disk operation when it is implicit. Or maybe 10181 /// a special media state for it to make it even more simple. 10182 10183 Assert(mMediaData.isBackedUp()); 10184 10185 /* will leave the lock before the potentially lengthy operation, so 10186 * protect with the special state */ 10187 MachineState_T oldState = mData->mMachineState; 10188 setMachineState(MachineState_SettingUp); 10189 10190 writeLock.release(); 10191 10192 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, 10193 true /*aWait*/, 10194 pllRegistriesThatNeedSaving); 10195 10196 writeLock.acquire(); 10197 10198 setMachineState(oldState); 10199 10200 if (FAILED(rc)) return rc; 10201 } 10202 10203 setModified(IsModified_Storage); 10204 mMediaData.backup(); 10205 10206 // we cannot use erase (it) below because backup() above will create 10207 // a copy of the list and make this copy active, but the iterator 10208 // still refers to the original and is not valid for the copy 10209 mMediaData->mAttachments.remove(pAttach); 10210 10211 if (!oldmedium.isNull()) 10212 { 10213 // if this is from a snapshot, do not defer detachment to commitMedia() 10214 if (pSnapshot) 10215 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId()); 10216 // else if non-hard disk media, do not defer detachment to commitMedia() either 10217 else if (mediumType != DeviceType_HardDisk) 10218 oldmedium->removeBackReference(mData->mUuid); 10219 } 10220 10221 return S_OK; 10222 } 10223 10224 /** 10225 * Goes thru all media of the given list and 10226 * 10227 * 1) calls detachDevice() on each of them for this machine and 10228 * 2) adds all Medium objects found in the process to the given list, 10229 * depending on cleanupMode. 10230 * 10231 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only 10232 * adds hard disks to the list. If it is CleanupMode_Full, this adds all 10233 * media to the list. 10234 * 10235 * This gets called from Machine::Unregister, both for the actual Machine and 10236 * the SnapshotMachine objects that might be found in the snapshots. 10237 * 10238 * Requires caller and locking. The machine lock must be passed in because it 10239 * will be passed on to detachDevice which needs it for temporary unlocking. 10240 * 10241 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice. 10242 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine. 10243 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added; 10244 * otherwise no media get added. 10245 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode. 10246 * @return 10247 */ 10248 HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock, 10249 Snapshot *pSnapshot, 10250 CleanupMode_T cleanupMode, 10251 MediaList &llMedia) 10252 { 10253 Assert(isWriteLockOnCurrentThread()); 10254 10255 HRESULT rc; 10256 10257 // make a temporary list because detachDevice invalidates iterators into 10258 // mMediaData->mAttachments 10259 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments; 10260 10261 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); 10262 it != llAttachments2.end(); 10263 ++it) 10264 { 10265 ComObjPtr<MediumAttachment> &pAttach = *it; 10266 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 10267 10268 if (!pMedium.isNull()) 10269 { 10270 DeviceType_T devType = pMedium->getDeviceType(); 10271 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly 10272 && devType == DeviceType_HardDisk) 10273 || (cleanupMode == CleanupMode_Full) 10274 ) 10275 llMedia.push_back(pMedium); 10276 } 10277 10278 // real machine: then we need to use the proper method 10279 rc = detachDevice(pAttach, 10280 writeLock, 10281 pSnapshot, 10282 NULL /* pfNeedsSaveSettings */); 10283 10284 if (FAILED(rc)) 10285 return rc; 10286 } 10287 10288 return S_OK; 10289 } 10290 10291 /** 10292 * Perform deferred hard disk detachments. 10293 * 10294 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not 10295 * backed up). 10296 * 10297 * If @a aOnline is @c true then this method will also unlock the old hard disks 10298 * for which the new implicit diffs were created and will lock these new diffs for 10299 * writing. 10300 * 10301 * @param aOnline Whether the VM was online prior to this operation. 10302 * 10303 * @note Locks this object for writing! 10304 */ 10305 void Machine::commitMedia(bool aOnline /*= false*/) 10306 { 10307 AutoCaller autoCaller(this); 10308 AssertComRCReturnVoid(autoCaller.rc()); 10309 10310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 10311 10312 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline)); 10313 10314 HRESULT rc = S_OK; 10315 10316 /* no attach/detach operations -- nothing to do */ 10317 if (!mMediaData.isBackedUp()) 10318 return; 10319 10320 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 10321 bool fMediaNeedsLocking = false; 10322 10323 /* enumerate new attachments */ 10324 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 10325 it != mMediaData->mAttachments.end(); 10326 ++it) 10327 { 10328 MediumAttachment *pAttach = *it; 10329 10330 pAttach->commit(); 10331 10332 Medium* pMedium = pAttach->getMedium(); 10333 bool fImplicit = pAttach->isImplicit(); 10334 10335 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n", 10336 (pMedium) ? pMedium->getName().c_str() : "NULL", 10337 fImplicit)); 10338 10339 /** @todo convert all this Machine-based voodoo to MediumAttachment 10340 * based commit logic. */ 10341 if (fImplicit) 10342 { 10343 /* convert implicit attachment to normal */ 10344 pAttach->setImplicit(false); 10345 10346 if ( aOnline 10347 && pMedium 10348 && pAttach->getType() == DeviceType_HardDisk 10349 ) 10350 { 10351 ComObjPtr<Medium> parent = pMedium->getParent(); 10352 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS); 10353 10354 /* update the appropriate lock list */ 10355 MediumLockList *pMediumLockList; 10356 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 10357 AssertComRC(rc); 10358 if (pMediumLockList) 10359 { 10360 /* unlock if there's a need to change the locking */ 10361 if (!fMediaNeedsLocking) 10362 { 10363 rc = mData->mSession.mLockedMedia.Unlock(); 10364 AssertComRC(rc); 10365 fMediaNeedsLocking = true; 10366 } 10367 rc = pMediumLockList->Update(parent, false); 10368 AssertComRC(rc); 10369 rc = pMediumLockList->Append(pMedium, true); 10370 AssertComRC(rc); 10371 } 10372 } 10373 10374 continue; 10375 } 10376 10377 if (pMedium) 10378 { 10379 /* was this medium attached before? */ 10380 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); 10381 oldIt != oldAtts.end(); 10382 ++oldIt) 10383 { 10384 MediumAttachment *pOldAttach = *oldIt; 10385 if (pOldAttach->getMedium() == pMedium) 10386 { 10387 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str())); 10388 10389 /* yes: remove from old to avoid de-association */ 10390 oldAtts.erase(oldIt); 10391 break; 10392 } 10393 } 10394 } 10395 } 10396 10397 /* enumerate remaining old attachments and de-associate from the 10398 * current machine state */ 10399 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 10400 it != oldAtts.end(); 10401 ++it) 10402 { 10403 MediumAttachment *pAttach = *it; 10404 Medium* pMedium = pAttach->getMedium(); 10405 10406 /* Detach only hard disks, since DVD/floppy media is detached 10407 * instantly in MountMedium. */ 10408 if (pAttach->getType() == DeviceType_HardDisk && pMedium) 10409 { 10410 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str())); 10411 10412 /* now de-associate from the current machine state */ 10413 rc = pMedium->removeBackReference(mData->mUuid); 10414 AssertComRC(rc); 10415 10416 if (aOnline) 10417 { 10418 /* unlock since medium is not used anymore */ 10419 MediumLockList *pMediumLockList; 10420 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 10421 AssertComRC(rc); 10422 if (pMediumLockList) 10423 { 10424 rc = mData->mSession.mLockedMedia.Remove(pAttach); 10425 AssertComRC(rc); 10426 } 10427 } 10428 } 10429 } 10430 10431 /* take media locks again so that the locking state is consistent */ 10432 if (fMediaNeedsLocking) 10433 { 10434 Assert(aOnline); 10435 rc = mData->mSession.mLockedMedia.Lock(); 10436 AssertComRC(rc); 10437 } 10438 10439 /* commit the hard disk changes */ 10440 mMediaData.commit(); 10441 10442 if (isSessionMachine()) 10443 { 10444 /* 10445 * Update the parent machine to point to the new owner. 10446 * This is necessary because the stored parent will point to the 10447 * session machine otherwise and cause crashes or errors later 10448 * when the session machine gets invalid. 10449 */ 10450 /** @todo Change the MediumAttachment class to behave like any other 10451 * class in this regard by creating peer MediumAttachment 10452 * objects for session machines and share the data with the peer 10453 * machine. 10454 */ 10455 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 10456 it != mMediaData->mAttachments.end(); 10457 ++it) 10458 { 10459 (*it)->updateParentMachine(mPeer); 10460 } 10461 10462 /* attach new data to the primary machine and reshare it */ 10463 mPeer->mMediaData.attach(mMediaData); 10464 } 10465 10466 return; 10467 } 10468 10469 /** 10470 * Perform deferred deletion of implicitly created diffs. 10471 * 10472 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not 10473 * backed up). 10474 * 10475 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 10476 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 10477 * 10478 * @note Locks this object for writing! 10479 * 10480 * @todo r=dj this needs a pllRegistriesThatNeedSaving as well 10481 */ 10482 void Machine::rollbackMedia() 10483 { 10484 AutoCaller autoCaller(this); 10485 AssertComRCReturnVoid (autoCaller.rc()); 10486 10487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 10488 10489 LogFlowThisFunc(("Entering\n")); 10490 10491 HRESULT rc = S_OK; 10492 10493 /* no attach/detach operations -- nothing to do */ 10494 if (!mMediaData.isBackedUp()) 10495 return; 10496 10497 /* enumerate new attachments */ 10498 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 10499 it != mMediaData->mAttachments.end(); 10500 ++it) 10501 { 10502 MediumAttachment *pAttach = *it; 10503 /* Fix up the backrefs for DVD/floppy media. */ 10504 if (pAttach->getType() != DeviceType_HardDisk) 10505 { 10506 Medium* pMedium = pAttach->getMedium(); 10507 if (pMedium) 10508 { 10509 rc = pMedium->removeBackReference(mData->mUuid); 10510 AssertComRC(rc); 10511 } 10512 } 10513 10514 (*it)->rollback(); 10515 10516 pAttach = *it; 10517 /* Fix up the backrefs for DVD/floppy media. */ 10518 if (pAttach->getType() != DeviceType_HardDisk) 10519 { 10520 Medium* pMedium = pAttach->getMedium(); 10521 if (pMedium) 10522 { 10523 rc = pMedium->addBackReference(mData->mUuid); 10524 AssertComRC(rc); 10525 } 10526 } 10527 } 10528 10529 /** @todo convert all this Machine-based voodoo to MediumAttachment 10530 * based rollback logic. */ 10531 // @todo r=dj the below totally fails if this gets called from Machine::rollback(), 10532 // which gets called if Machine::registeredInit() fails... 10533 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/); 10534 10535 return; 10536 } 10537 10538 /** 10539 * Returns true if the settings file is located in the directory named exactly 10540 * as the machine; this means, among other things, that the machine directory 10541 * should be auto-renamed. 10542 * 10543 * @param aSettingsDir if not NULL, the full machine settings file directory 10544 * name will be assigned there. 10545 * 10546 * @note Doesn't lock anything. 10547 * @note Not thread safe (must be called from this object's lock). 10548 */ 10549 bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const 10550 { 10551 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox 10552 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname 10553 if (aSettingsDir) 10554 *aSettingsDir = strMachineDirName; 10555 strMachineDirName.stripPath(); // vmname 10556 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox 10557 strConfigFileOnly.stripPath() // vmname.vbox 10558 .stripExt(); // vmname 10559 10560 AssertReturn(!strMachineDirName.isEmpty(), false); 10561 AssertReturn(!strConfigFileOnly.isEmpty(), false); 10562 10563 return strMachineDirName == strConfigFileOnly; 10564 } 10565 10566 /** 10567 * Discards all changes to machine settings. 10568 * 10569 * @param aNotify Whether to notify the direct session about changes or not. 10570 * 10571 * @note Locks objects for writing! 10572 */ 10573 void Machine::rollback(bool aNotify) 10574 { 10575 AutoCaller autoCaller(this); 10576 AssertComRCReturn(autoCaller.rc(), (void)0); 10577 10578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 10579 10580 if (!mStorageControllers.isNull()) 10581 { 10582 if (mStorageControllers.isBackedUp()) 10583 { 10584 /* unitialize all new devices (absent in the backed up list). */ 10585 StorageControllerList::const_iterator it = mStorageControllers->begin(); 10586 StorageControllerList *backedList = mStorageControllers.backedUpData(); 10587 while (it != mStorageControllers->end()) 10588 { 10589 if ( std::find(backedList->begin(), backedList->end(), *it) 10590 == backedList->end() 10591 ) 10592 { 10593 (*it)->uninit(); 10594 } 10595 ++it; 10596 } 10597 10598 /* restore the list */ 10599 mStorageControllers.rollback(); 10600 } 10601 10602 /* rollback any changes to devices after restoring the list */ 10603 if (mData->flModifications & IsModified_Storage) 10604 { 10605 StorageControllerList::const_iterator it = mStorageControllers->begin(); 10606 while (it != mStorageControllers->end()) 10607 { 10608 (*it)->rollback(); 10609 ++it; 10610 } 10611 } 10612 } 10613 10614 mUserData.rollback(); 10615 10616 mHWData.rollback(); 10617 10618 if (mData->flModifications & IsModified_Storage) 10619 rollbackMedia(); 10620 10621 if (mBIOSSettings) 10622 mBIOSSettings->rollback(); 10623 10624 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer)) 10625 mVRDEServer->rollback(); 10626 10627 if (mAudioAdapter) 10628 mAudioAdapter->rollback(); 10629 10630 if (mUSBController && (mData->flModifications & IsModified_USB)) 10631 mUSBController->rollback(); 10632 10633 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl)) 10634 mBandwidthControl->rollback(); 10635 10636 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)]; 10637 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)]; 10638 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)]; 10639 10640 if (mData->flModifications & IsModified_NetworkAdapters) 10641 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 10642 if ( mNetworkAdapters[slot] 10643 && mNetworkAdapters[slot]->isModified()) 10644 { 10645 mNetworkAdapters[slot]->rollback(); 10646 networkAdapters[slot] = mNetworkAdapters[slot]; 10647 } 10648 10649 if (mData->flModifications & IsModified_SerialPorts) 10650 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 10651 if ( mSerialPorts[slot] 10652 && mSerialPorts[slot]->isModified()) 10653 { 10654 mSerialPorts[slot]->rollback(); 10655 serialPorts[slot] = mSerialPorts[slot]; 10656 } 10657 10658 if (mData->flModifications & IsModified_ParallelPorts) 10659 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 10660 if ( mParallelPorts[slot] 10661 && mParallelPorts[slot]->isModified()) 10662 { 10663 mParallelPorts[slot]->rollback(); 10664 parallelPorts[slot] = mParallelPorts[slot]; 10665 } 10666 10667 if (aNotify) 10668 { 10669 /* inform the direct session about changes */ 10670 10671 ComObjPtr<Machine> that = this; 10672 uint32_t flModifications = mData->flModifications; 10673 alock.leave(); 10674 10675 if (flModifications & IsModified_SharedFolders) 10676 that->onSharedFolderChange(); 10677 10678 if (flModifications & IsModified_VRDEServer) 10679 that->onVRDEServerChange(/* aRestart */ TRUE); 10680 if (flModifications & IsModified_USB) 10681 that->onUSBControllerChange(); 10682 10683 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++) 10684 if (networkAdapters[slot]) 10685 that->onNetworkAdapterChange(networkAdapters[slot], FALSE); 10686 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++) 10687 if (serialPorts[slot]) 10688 that->onSerialPortChange(serialPorts[slot]); 10689 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++) 10690 if (parallelPorts[slot]) 10691 that->onParallelPortChange(parallelPorts[slot]); 10692 10693 if (flModifications & IsModified_Storage) 10694 that->onStorageControllerChange(); 10695 10696 #if 0 10697 if (flModifications & IsModified_BandwidthControl) 10698 that->onBandwidthControlChange(); 10699 #endif 10700 } 10701 } 10702 10703 /** 10704 * Commits all the changes to machine settings. 10705 * 10706 * Note that this operation is supposed to never fail. 10707 * 10708 * @note Locks this object and children for writing. 10709 */ 10710 void Machine::commit() 10711 { 10712 AutoCaller autoCaller(this); 10713 AssertComRCReturnVoid(autoCaller.rc()); 10714 10715 AutoCaller peerCaller(mPeer); 10716 AssertComRCReturnVoid(peerCaller.rc()); 10717 10718 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS); 10719 10720 /* 10721 * use safe commit to ensure Snapshot machines (that share mUserData) 10722 * will still refer to a valid memory location 10723 */ 10724 mUserData.commitCopy(); 10725 10726 mHWData.commit(); 10727 10728 if (mMediaData.isBackedUp()) 10729 commitMedia(); 10730 10731 mBIOSSettings->commit(); 10732 mVRDEServer->commit(); 10733 mAudioAdapter->commit(); 10734 mUSBController->commit(); 10735 mBandwidthControl->commit(); 10736 10737 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 10738 mNetworkAdapters[slot]->commit(); 10739 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 10740 mSerialPorts[slot]->commit(); 10741 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 10742 mParallelPorts[slot]->commit(); 10743 10744 bool commitStorageControllers = false; 10745 10746 if (mStorageControllers.isBackedUp()) 10747 { 10748 mStorageControllers.commit(); 10749 10750 if (mPeer) 10751 { 10752 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS); 10753 10754 /* Commit all changes to new controllers (this will reshare data with 10755 * peers for those who have peers) */ 10756 StorageControllerList *newList = new StorageControllerList(); 10757 StorageControllerList::const_iterator it = mStorageControllers->begin(); 10758 while (it != mStorageControllers->end()) 10759 { 10760 (*it)->commit(); 10761 10762 /* look if this controller has a peer device */ 10763 ComObjPtr<StorageController> peer = (*it)->getPeer(); 10764 if (!peer) 10765 { 10766 /* no peer means the device is a newly created one; 10767 * create a peer owning data this device share it with */ 10768 peer.createObject(); 10769 peer->init(mPeer, *it, true /* aReshare */); 10770 } 10771 else 10772 { 10773 /* remove peer from the old list */ 10774 mPeer->mStorageControllers->remove(peer); 10775 } 10776 /* and add it to the new list */ 10777 newList->push_back(peer); 10778 10779 ++it; 10780 } 10781 10782 /* uninit old peer's controllers that are left */ 10783 it = mPeer->mStorageControllers->begin(); 10784 while (it != mPeer->mStorageControllers->end()) 10785 { 10786 (*it)->uninit(); 10787 ++it; 10788 } 10789 10790 /* attach new list of controllers to our peer */ 10791 mPeer->mStorageControllers.attach(newList); 10792 } 10793 else 10794 { 10795 /* we have no peer (our parent is the newly created machine); 10796 * just commit changes to devices */ 10797 commitStorageControllers = true; 10798 } 10799 } 10800 else 10801 { 10802 /* the list of controllers itself is not changed, 10803 * just commit changes to controllers themselves */ 10804 commitStorageControllers = true; 10805 } 10806 10807 if (commitStorageControllers) 10808 { 10809 StorageControllerList::const_iterator it = mStorageControllers->begin(); 10810 while (it != mStorageControllers->end()) 10811 { 10812 (*it)->commit(); 10813 ++it; 10814 } 10815 } 10816 10817 if (isSessionMachine()) 10818 { 10819 /* attach new data to the primary machine and reshare it */ 10820 mPeer->mUserData.attach(mUserData); 10821 mPeer->mHWData.attach(mHWData); 10822 /* mMediaData is reshared by fixupMedia */ 10823 // mPeer->mMediaData.attach(mMediaData); 10824 Assert(mPeer->mMediaData.data() == mMediaData.data()); 10825 } 10826 } 10827 10828 /** 10829 * Copies all the hardware data from the given machine. 10830 * 10831 * Currently, only called when the VM is being restored from a snapshot. In 10832 * particular, this implies that the VM is not running during this method's 10833 * call. 10834 * 10835 * @note This method must be called from under this object's lock. 10836 * 10837 * @note This method doesn't call #commit(), so all data remains backed up and 10838 * unsaved. 10839 */ 10840 void Machine::copyFrom(Machine *aThat) 10841 { 10842 AssertReturnVoid(!isSnapshotMachine()); 10843 AssertReturnVoid(aThat->isSnapshotMachine()); 10844 10845 AssertReturnVoid(!Global::IsOnline(mData->mMachineState)); 10846 10847 mHWData.assignCopy(aThat->mHWData); 10848 10849 // create copies of all shared folders (mHWData after attaching a copy 10850 // contains just references to original objects) 10851 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin(); 10852 it != mHWData->mSharedFolders.end(); 10853 ++it) 10854 { 10855 ComObjPtr<SharedFolder> folder; 10856 folder.createObject(); 10857 HRESULT rc = folder->initCopy(getMachine(), *it); 10858 AssertComRC(rc); 10859 *it = folder; 10860 } 10861 10862 mBIOSSettings->copyFrom(aThat->mBIOSSettings); 10863 mVRDEServer->copyFrom(aThat->mVRDEServer); 10864 mAudioAdapter->copyFrom(aThat->mAudioAdapter); 10865 mUSBController->copyFrom(aThat->mUSBController); 10866 mBandwidthControl->copyFrom(aThat->mBandwidthControl); 10867 10868 /* create private copies of all controllers */ 10869 mStorageControllers.backup(); 10870 mStorageControllers->clear(); 10871 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin(); 10872 it != aThat->mStorageControllers->end(); 10873 ++it) 10874 { 10875 ComObjPtr<StorageController> ctrl; 10876 ctrl.createObject(); 10877 ctrl->initCopy(this, *it); 10878 mStorageControllers->push_back(ctrl); 10879 } 10880 10881 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 10882 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]); 10883 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 10884 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]); 10885 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 10886 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]); 10887 } 10888 10889 /** 10890 * Returns whether the given storage controller is hotplug capable. 10891 * 10892 * @returns true if the controller supports hotplugging 10893 * false otherwise. 10894 * @param enmCtrlType The controller type to check for. 10895 */ 10896 bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType) 10897 { 10898 switch (enmCtrlType) 10899 { 10900 case StorageControllerType_IntelAhci: 10901 return true; 10902 case StorageControllerType_LsiLogic: 10903 case StorageControllerType_LsiLogicSas: 10904 case StorageControllerType_BusLogic: 10905 case StorageControllerType_PIIX3: 10906 case StorageControllerType_PIIX4: 10907 case StorageControllerType_ICH6: 10908 case StorageControllerType_I82078: 10909 default: 10910 return false; 10911 } 10912 } 10913 10914 #ifdef VBOX_WITH_RESOURCE_USAGE_API 10915 10916 void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid) 10917 { 10918 AssertReturnVoid(isWriteLockOnCurrentThread()); 10919 AssertPtrReturnVoid(aCollector); 10920 10921 pm::CollectorHAL *hal = aCollector->getHAL(); 10922 /* Create sub metrics */ 10923 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User", 10924 "Percentage of processor time spent in user mode by the VM process."); 10925 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel", 10926 "Percentage of processor time spent in kernel mode by the VM process."); 10927 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used", 10928 "Size of resident portion of VM process in memory."); 10929 /* Create and register base metrics */ 10930 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid, 10931 cpuLoadUser, cpuLoadKernel); 10932 aCollector->registerBaseMetric(cpuLoad); 10933 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid, 10934 ramUsageUsed); 10935 aCollector->registerBaseMetric(ramUsage); 10936 10937 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0)); 10938 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 10939 new pm::AggregateAvg())); 10940 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 10941 new pm::AggregateMin())); 10942 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 10943 new pm::AggregateMax())); 10944 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0)); 10945 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 10946 new pm::AggregateAvg())); 10947 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 10948 new pm::AggregateMin())); 10949 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 10950 new pm::AggregateMax())); 10951 10952 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0)); 10953 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 10954 new pm::AggregateAvg())); 10955 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 10956 new pm::AggregateMin())); 10957 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 10958 new pm::AggregateMax())); 10959 10960 10961 /* Guest metrics collector */ 10962 mCollectorGuest = new pm::CollectorGuest(aMachine, pid); 10963 aCollector->registerGuest(mCollectorGuest); 10964 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", 10965 this, __PRETTY_FUNCTION__, mCollectorGuest)); 10966 10967 /* Create sub metrics */ 10968 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User", 10969 "Percentage of processor time spent in user mode as seen by the guest."); 10970 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel", 10971 "Percentage of processor time spent in kernel mode as seen by the guest."); 10972 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle", 10973 "Percentage of processor time spent idling as seen by the guest."); 10974 10975 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */ 10976 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM."); 10977 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM."); 10978 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM."); 10979 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM."); 10980 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory."); 10981 10982 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file."); 10983 10984 /* Create and register base metrics */ 10985 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine, 10986 guestLoadUser, guestLoadKernel, guestLoadIdle); 10987 aCollector->registerBaseMetric(guestCpuLoad); 10988 10989 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine, 10990 guestMemTotal, guestMemFree, 10991 guestMemBalloon, guestMemShared, 10992 guestMemCache, guestPagedTotal); 10993 aCollector->registerBaseMetric(guestCpuMem); 10994 10995 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0)); 10996 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg())); 10997 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin())); 10998 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax())); 10999 11000 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0)); 11001 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg())); 11002 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin())); 11003 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax())); 11004 11005 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0)); 11006 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg())); 11007 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin())); 11008 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax())); 11009 11010 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0)); 11011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg())); 11012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin())); 11013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax())); 11014 11015 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0)); 11016 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg())); 11017 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin())); 11018 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax())); 11019 11020 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0)); 11021 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg())); 11022 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin())); 11023 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax())); 11024 11025 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0)); 11026 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg())); 11027 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin())); 11028 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax())); 11029 11030 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0)); 11031 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg())); 11032 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin())); 11033 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax())); 11034 11035 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0)); 11036 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg())); 11037 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin())); 11038 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax())); 11039 } 11040 11041 void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine) 11042 { 11043 AssertReturnVoid(isWriteLockOnCurrentThread()); 11044 11045 if (aCollector) 11046 { 11047 aCollector->unregisterMetricsFor(aMachine); 11048 aCollector->unregisterBaseMetricsFor(aMachine); 11049 } 11050 } 11051 11052 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 11053 11054 11055 //////////////////////////////////////////////////////////////////////////////// 11056 11057 DEFINE_EMPTY_CTOR_DTOR(SessionMachine) 11058 11059 HRESULT SessionMachine::FinalConstruct() 11060 { 11061 LogFlowThisFunc(("\n")); 11062 11063 #if defined(RT_OS_WINDOWS) 11064 mIPCSem = NULL; 11065 #elif defined(RT_OS_OS2) 11066 mIPCSem = NULLHANDLE; 11067 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 11068 mIPCSem = -1; 11069 #else 11070 # error "Port me!" 11071 #endif 11072 11073 return BaseFinalConstruct(); 11074 } 11075 11076 void SessionMachine::FinalRelease() 11077 { 11078 LogFlowThisFunc(("\n")); 11079 11080 uninit(Uninit::Unexpected); 11081 11082 BaseFinalRelease(); 11083 } 11084 11085 /** 11086 * @note Must be called only by Machine::openSession() from its own write lock. 11087 */ 11088 HRESULT SessionMachine::init(Machine *aMachine) 11089 { 11090 LogFlowThisFuncEnter(); 11091 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str())); 11092 11093 AssertReturn(aMachine, E_INVALIDARG); 11094 11095 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 11096 11097 /* Enclose the state transition NotReady->InInit->Ready */ 11098 AutoInitSpan autoInitSpan(this); 11099 AssertReturn(autoInitSpan.isOk(), E_FAIL); 11100 11101 /* create the interprocess semaphore */ 11102 #if defined(RT_OS_WINDOWS) 11103 mIPCSemName = aMachine->mData->m_strConfigFileFull; 11104 for (size_t i = 0; i < mIPCSemName.length(); i++) 11105 if (mIPCSemName.raw()[i] == '\\') 11106 mIPCSemName.raw()[i] = '/'; 11107 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw()); 11108 ComAssertMsgRet(mIPCSem, 11109 ("Cannot create IPC mutex '%ls', err=%d", 11110 mIPCSemName.raw(), ::GetLastError()), 11111 E_FAIL); 11112 #elif defined(RT_OS_OS2) 11113 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}", 11114 aMachine->mData->mUuid.raw()); 11115 mIPCSemName = ipcSem; 11116 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE); 11117 ComAssertMsgRet(arc == NO_ERROR, 11118 ("Cannot create IPC mutex '%s', arc=%ld", 11119 ipcSem.c_str(), arc), 11120 E_FAIL); 11121 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 11122 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 11123 # if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64) 11124 /** @todo Check that this still works correctly. */ 11125 AssertCompileSize(key_t, 8); 11126 # else 11127 AssertCompileSize(key_t, 4); 11128 # endif 11129 key_t key; 11130 mIPCSem = -1; 11131 mIPCKey = "0"; 11132 for (uint32_t i = 0; i < 1 << 24; i++) 11133 { 11134 key = ((uint32_t)'V' << 24) | i; 11135 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL); 11136 if (sem >= 0 || (errno != EEXIST && errno != EACCES)) 11137 { 11138 mIPCSem = sem; 11139 if (sem >= 0) 11140 mIPCKey = BstrFmt("%u", key); 11141 break; 11142 } 11143 } 11144 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 11145 Utf8Str semName = aMachine->mData->m_strConfigFileFull; 11146 char *pszSemName = NULL; 11147 RTStrUtf8ToCurrentCP(&pszSemName, semName); 11148 key_t key = ::ftok(pszSemName, 'V'); 11149 RTStrFree(pszSemName); 11150 11151 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT); 11152 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 11153 11154 int errnoSave = errno; 11155 if (mIPCSem < 0 && errnoSave == ENOSYS) 11156 { 11157 setError(E_FAIL, 11158 tr("Cannot create IPC semaphore. Most likely your host kernel lacks " 11159 "support for SysV IPC. Check the host kernel configuration for " 11160 "CONFIG_SYSVIPC=y")); 11161 return E_FAIL; 11162 } 11163 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing 11164 * the IPC semaphores */ 11165 if (mIPCSem < 0 && errnoSave == ENOSPC) 11166 { 11167 #ifdef RT_OS_LINUX 11168 setError(E_FAIL, 11169 tr("Cannot create IPC semaphore because the system limit for the " 11170 "maximum number of semaphore sets (SEMMNI), or the system wide " 11171 "maximum number of semaphores (SEMMNS) would be exceeded. The " 11172 "current set of SysV IPC semaphores can be determined from " 11173 "the file /proc/sysvipc/sem")); 11174 #else 11175 setError(E_FAIL, 11176 tr("Cannot create IPC semaphore because the system-imposed limit " 11177 "on the maximum number of allowed semaphores or semaphore " 11178 "identifiers system-wide would be exceeded")); 11179 #endif 11180 return E_FAIL; 11181 } 11182 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave), 11183 E_FAIL); 11184 /* set the initial value to 1 */ 11185 int rv = ::semctl(mIPCSem, 0, SETVAL, 1); 11186 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno), 11187 E_FAIL); 11188 #else 11189 # error "Port me!" 11190 #endif 11191 11192 /* memorize the peer Machine */ 11193 unconst(mPeer) = aMachine; 11194 /* share the parent pointer */ 11195 unconst(mParent) = aMachine->mParent; 11196 11197 /* take the pointers to data to share */ 11198 mData.share(aMachine->mData); 11199 mSSData.share(aMachine->mSSData); 11200 11201 mUserData.share(aMachine->mUserData); 11202 mHWData.share(aMachine->mHWData); 11203 mMediaData.share(aMachine->mMediaData); 11204 11205 mStorageControllers.allocate(); 11206 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin(); 11207 it != aMachine->mStorageControllers->end(); 11208 ++it) 11209 { 11210 ComObjPtr<StorageController> ctl; 11211 ctl.createObject(); 11212 ctl->init(this, *it); 11213 mStorageControllers->push_back(ctl); 11214 } 11215 11216 unconst(mBIOSSettings).createObject(); 11217 mBIOSSettings->init(this, aMachine->mBIOSSettings); 11218 /* create another VRDEServer object that will be mutable */ 11219 unconst(mVRDEServer).createObject(); 11220 mVRDEServer->init(this, aMachine->mVRDEServer); 11221 /* create another audio adapter object that will be mutable */ 11222 unconst(mAudioAdapter).createObject(); 11223 mAudioAdapter->init(this, aMachine->mAudioAdapter); 11224 /* create a list of serial ports that will be mutable */ 11225 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 11226 { 11227 unconst(mSerialPorts[slot]).createObject(); 11228 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]); 11229 } 11230 /* create a list of parallel ports that will be mutable */ 11231 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 11232 { 11233 unconst(mParallelPorts[slot]).createObject(); 11234 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]); 11235 } 11236 /* create another USB controller object that will be mutable */ 11237 unconst(mUSBController).createObject(); 11238 mUSBController->init(this, aMachine->mUSBController); 11239 11240 /* create a list of network adapters that will be mutable */ 11241 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 11242 { 11243 unconst(mNetworkAdapters[slot]).createObject(); 11244 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]); 11245 } 11246 11247 /* create another bandwidth control object that will be mutable */ 11248 unconst(mBandwidthControl).createObject(); 11249 mBandwidthControl->init(this, aMachine->mBandwidthControl); 11250 11251 /* default is to delete saved state on Saved -> PoweredOff transition */ 11252 mRemoveSavedState = true; 11253 11254 /* Confirm a successful initialization when it's the case */ 11255 autoInitSpan.setSucceeded(); 11256 11257 LogFlowThisFuncLeave(); 11258 return S_OK; 11259 } 11260 11261 /** 11262 * Uninitializes this session object. If the reason is other than 11263 * Uninit::Unexpected, then this method MUST be called from #checkForDeath(). 11264 * 11265 * @param aReason uninitialization reason 11266 * 11267 * @note Locks mParent + this object for writing. 11268 */ 11269 void SessionMachine::uninit(Uninit::Reason aReason) 11270 { 11271 LogFlowThisFuncEnter(); 11272 LogFlowThisFunc(("reason=%d\n", aReason)); 11273 11274 /* 11275 * Strongly reference ourselves to prevent this object deletion after 11276 * mData->mSession.mMachine.setNull() below (which can release the last 11277 * reference and call the destructor). Important: this must be done before 11278 * accessing any members (and before AutoUninitSpan that does it as well). 11279 * This self reference will be released as the very last step on return. 11280 */ 11281 ComObjPtr<SessionMachine> selfRef = this; 11282 11283 /* Enclose the state transition Ready->InUninit->NotReady */ 11284 AutoUninitSpan autoUninitSpan(this); 11285 if (autoUninitSpan.uninitDone()) 11286 { 11287 LogFlowThisFunc(("Already uninitialized\n")); 11288 LogFlowThisFuncLeave(); 11289 return; 11290 } 11291 11292 if (autoUninitSpan.initFailed()) 11293 { 11294 /* We've been called by init() because it's failed. It's not really 11295 * necessary (nor it's safe) to perform the regular uninit sequence 11296 * below, the following is enough. 11297 */ 11298 LogFlowThisFunc(("Initialization failed.\n")); 11299 #if defined(RT_OS_WINDOWS) 11300 if (mIPCSem) 11301 ::CloseHandle(mIPCSem); 11302 mIPCSem = NULL; 11303 #elif defined(RT_OS_OS2) 11304 if (mIPCSem != NULLHANDLE) 11305 ::DosCloseMutexSem(mIPCSem); 11306 mIPCSem = NULLHANDLE; 11307 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 11308 if (mIPCSem >= 0) 11309 ::semctl(mIPCSem, 0, IPC_RMID); 11310 mIPCSem = -1; 11311 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 11312 mIPCKey = "0"; 11313 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 11314 #else 11315 # error "Port me!" 11316 #endif 11317 uninitDataAndChildObjects(); 11318 mData.free(); 11319 unconst(mParent) = NULL; 11320 unconst(mPeer) = NULL; 11321 LogFlowThisFuncLeave(); 11322 return; 11323 } 11324 11325 MachineState_T lastState; 11326 { 11327 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS); 11328 lastState = mData->mMachineState; 11329 } 11330 NOREF(lastState); 11331 11332 #ifdef VBOX_WITH_USB 11333 // release all captured USB devices, but do this before requesting the locks below 11334 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState)) 11335 { 11336 /* Console::captureUSBDevices() is called in the VM process only after 11337 * setting the machine state to Starting or Restoring. 11338 * Console::detachAllUSBDevices() will be called upon successful 11339 * termination. So, we need to release USB devices only if there was 11340 * an abnormal termination of a running VM. 11341 * 11342 * This is identical to SessionMachine::DetachAllUSBDevices except 11343 * for the aAbnormal argument. */ 11344 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */); 11345 AssertComRC(rc); 11346 NOREF(rc); 11347 11348 USBProxyService *service = mParent->host()->usbProxyService(); 11349 if (service) 11350 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */); 11351 } 11352 #endif /* VBOX_WITH_USB */ 11353 11354 // we need to lock this object in uninit() because the lock is shared 11355 // with mPeer (as well as data we modify below). mParent->addProcessToReap() 11356 // and others need mParent lock, and USB needs host lock. 11357 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS); 11358 11359 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", 11360 this, __PRETTY_FUNCTION__, mCollectorGuest)); 11361 if (mCollectorGuest) 11362 { 11363 mParent->performanceCollector()->unregisterGuest(mCollectorGuest); 11364 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered() 11365 mCollectorGuest = NULL; 11366 } 11367 #if 0 11368 // Trigger async cleanup tasks, avoid doing things here which are not 11369 // vital to be done immediately and maybe need more locks. This calls 11370 // Machine::unregisterMetrics(). 11371 mParent->onMachineUninit(mPeer); 11372 #else 11373 /* 11374 * It is safe to call Machine::unregisterMetrics() here because 11375 * PerformanceCollector::samplerCallback no longer accesses guest methods 11376 * holding the lock. 11377 */ 11378 unregisterMetrics(mParent->performanceCollector(), mPeer); 11379 #endif 11380 11381 if (aReason == Uninit::Abnormal) 11382 { 11383 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", 11384 Global::IsOnlineOrTransient(lastState))); 11385 11386 /* reset the state to Aborted */ 11387 if (mData->mMachineState != MachineState_Aborted) 11388 setMachineState(MachineState_Aborted); 11389 } 11390 11391 // any machine settings modified? 11392 if (mData->flModifications) 11393 { 11394 LogWarningThisFunc(("Discarding unsaved settings changes!\n")); 11395 rollback(false /* aNotify */); 11396 } 11397 11398 Assert( mConsoleTaskData.strStateFilePath.isEmpty() 11399 || !mConsoleTaskData.mSnapshot); 11400 if (!mConsoleTaskData.strStateFilePath.isEmpty()) 11401 { 11402 LogWarningThisFunc(("canceling failed save state request!\n")); 11403 endSavingState(E_FAIL, tr("Machine terminated with pending save state!")); 11404 } 11405 else if (!mConsoleTaskData.mSnapshot.isNull()) 11406 { 11407 LogWarningThisFunc(("canceling untaken snapshot!\n")); 11408 11409 /* delete all differencing hard disks created (this will also attach 11410 * their parents back by rolling back mMediaData) */ 11411 rollbackMedia(); 11412 11413 // delete the saved state file (it might have been already created) 11414 // AFTER killing the snapshot so that releaseSavedStateFile() won't 11415 // think it's still in use 11416 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath(); 11417 mConsoleTaskData.mSnapshot->uninit(); 11418 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ ); 11419 } 11420 11421 if (!mData->mSession.mType.isEmpty()) 11422 { 11423 /* mType is not null when this machine's process has been started by 11424 * Machine::LaunchVMProcess(), therefore it is our child. We 11425 * need to queue the PID to reap the process (and avoid zombies on 11426 * Linux). */ 11427 Assert(mData->mSession.mPid != NIL_RTPROCESS); 11428 mParent->addProcessToReap(mData->mSession.mPid); 11429 } 11430 11431 mData->mSession.mPid = NIL_RTPROCESS; 11432 11433 if (aReason == Uninit::Unexpected) 11434 { 11435 /* Uninitialization didn't come from #checkForDeath(), so tell the 11436 * client watcher thread to update the set of machines that have open 11437 * sessions. */ 11438 mParent->updateClientWatcher(); 11439 } 11440 11441 /* uninitialize all remote controls */ 11442 if (mData->mSession.mRemoteControls.size()) 11443 { 11444 LogFlowThisFunc(("Closing remote sessions (%d):\n", 11445 mData->mSession.mRemoteControls.size())); 11446 11447 Data::Session::RemoteControlList::iterator it = 11448 mData->mSession.mRemoteControls.begin(); 11449 while (it != mData->mSession.mRemoteControls.end()) 11450 { 11451 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n")); 11452 HRESULT rc = (*it)->Uninitialize(); 11453 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc)); 11454 if (FAILED(rc)) 11455 LogWarningThisFunc(("Forgot to close the remote session?\n")); 11456 ++it; 11457 } 11458 mData->mSession.mRemoteControls.clear(); 11459 } 11460 11461 /* 11462 * An expected uninitialization can come only from #checkForDeath(). 11463 * Otherwise it means that something's gone really wrong (for example, 11464 * the Session implementation has released the VirtualBox reference 11465 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore, 11466 * etc). However, it's also possible, that the client releases the IPC 11467 * semaphore correctly (i.e. before it releases the VirtualBox reference), 11468 * but the VirtualBox release event comes first to the server process. 11469 * This case is practically possible, so we should not assert on an 11470 * unexpected uninit, just log a warning. 11471 */ 11472 11473 if ((aReason == Uninit::Unexpected)) 11474 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n")); 11475 11476 if (aReason != Uninit::Normal) 11477 { 11478 mData->mSession.mDirectControl.setNull(); 11479 } 11480 else 11481 { 11482 /* this must be null here (see #OnSessionEnd()) */ 11483 Assert(mData->mSession.mDirectControl.isNull()); 11484 Assert(mData->mSession.mState == SessionState_Unlocking); 11485 Assert(!mData->mSession.mProgress.isNull()); 11486 } 11487 if (mData->mSession.mProgress) 11488 { 11489 if (aReason == Uninit::Normal) 11490 mData->mSession.mProgress->notifyComplete(S_OK); 11491 else 11492 mData->mSession.mProgress->notifyComplete(E_FAIL, 11493 COM_IIDOF(ISession), 11494 getComponentName(), 11495 tr("The VM session was aborted")); 11496 mData->mSession.mProgress.setNull(); 11497 } 11498 11499 /* remove the association between the peer machine and this session machine */ 11500 Assert( (SessionMachine*)mData->mSession.mMachine == this 11501 || aReason == Uninit::Unexpected); 11502 11503 /* reset the rest of session data */ 11504 mData->mSession.mMachine.setNull(); 11505 mData->mSession.mState = SessionState_Unlocked; 11506 mData->mSession.mType.setNull(); 11507 11508 /* close the interprocess semaphore before leaving the exclusive lock */ 11509 #if defined(RT_OS_WINDOWS) 11510 if (mIPCSem) 11511 ::CloseHandle(mIPCSem); 11512 mIPCSem = NULL; 11513 #elif defined(RT_OS_OS2) 11514 if (mIPCSem != NULLHANDLE) 11515 ::DosCloseMutexSem(mIPCSem); 11516 mIPCSem = NULLHANDLE; 11517 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 11518 if (mIPCSem >= 0) 11519 ::semctl(mIPCSem, 0, IPC_RMID); 11520 mIPCSem = -1; 11521 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 11522 mIPCKey = "0"; 11523 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 11524 #else 11525 # error "Port me!" 11526 #endif 11527 11528 /* fire an event */ 11529 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); 11530 11531 uninitDataAndChildObjects(); 11532 11533 /* free the essential data structure last */ 11534 mData.free(); 11535 11536 #if 1 /** @todo Please review this change! (bird) */ 11537 /* drop the exclusive lock before setting the below two to NULL */ 11538 multilock.release(); 11539 #else 11540 /* leave the exclusive lock before setting the below two to NULL */ 11541 multilock.leave(); 11542 #endif 11543 11544 unconst(mParent) = NULL; 11545 unconst(mPeer) = NULL; 11546 11547 LogFlowThisFuncLeave(); 11548 } 11549 11550 // util::Lockable interface 11551 //////////////////////////////////////////////////////////////////////////////// 11552 11553 /** 11554 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle 11555 * with the primary Machine instance (mPeer). 11556 */ 11557 RWLockHandle *SessionMachine::lockHandle() const 11558 { 11559 AssertReturn(mPeer != NULL, NULL); 11560 return mPeer->lockHandle(); 11561 } 11562 11563 // IInternalMachineControl methods 11564 //////////////////////////////////////////////////////////////////////////////// 11565 11566 /** 11567 * @note Locks this object for writing. 11568 */ 11569 STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove) 11570 { 11571 AutoCaller autoCaller(this); 11572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11573 11574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11575 11576 mRemoveSavedState = aRemove; 11577 11578 return S_OK; 11579 } 11580 11581 /** 11582 * @note Locks the same as #setMachineState() does. 11583 */ 11584 STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState) 11585 { 11586 return setMachineState(aMachineState); 11587 } 11588 11589 /** 11590 * @note Locks this object for reading. 11591 */ 11592 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId) 11593 { 11594 AutoCaller autoCaller(this); 11595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11596 11597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 11598 11599 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 11600 mIPCSemName.cloneTo(aId); 11601 return S_OK; 11602 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 11603 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 11604 mIPCKey.cloneTo(aId); 11605 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 11606 mData->m_strConfigFileFull.cloneTo(aId); 11607 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 11608 return S_OK; 11609 #else 11610 # error "Port me!" 11611 #endif 11612 } 11613 11614 /** 11615 * @note Locks this object for writing. 11616 */ 11617 STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress) 11618 { 11619 LogFlowThisFunc(("aProgress=%p\n", aProgress)); 11620 AutoCaller autoCaller(this); 11621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11622 11623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11624 11625 if (mData->mSession.mState != SessionState_Locked) 11626 return VBOX_E_INVALID_OBJECT_STATE; 11627 11628 if (!mData->mSession.mProgress.isNull()) 11629 mData->mSession.mProgress->setOtherProgressObject(aProgress); 11630 11631 LogFlowThisFunc(("returns S_OK.\n")); 11632 return S_OK; 11633 } 11634 11635 /** 11636 * @note Locks this object for writing. 11637 */ 11638 STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult) 11639 { 11640 AutoCaller autoCaller(this); 11641 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11642 11643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11644 11645 if (mData->mSession.mState != SessionState_Locked) 11646 return VBOX_E_INVALID_OBJECT_STATE; 11647 11648 /* Finalize the LaunchVMProcess progress object. */ 11649 if (mData->mSession.mProgress) 11650 { 11651 mData->mSession.mProgress->notifyComplete((HRESULT)iResult); 11652 mData->mSession.mProgress.setNull(); 11653 } 11654 11655 if (SUCCEEDED((HRESULT)iResult)) 11656 { 11657 #ifdef VBOX_WITH_RESOURCE_USAGE_API 11658 /* The VM has been powered up successfully, so it makes sense 11659 * now to offer the performance metrics for a running machine 11660 * object. Doing it earlier wouldn't be safe. */ 11661 registerMetrics(mParent->performanceCollector(), mPeer, 11662 mData->mSession.mPid); 11663 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 11664 } 11665 11666 return S_OK; 11667 } 11668 11669 /** 11670 * @note Locks this object for writing. 11671 */ 11672 STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress) 11673 { 11674 LogFlowThisFuncEnter(); 11675 11676 CheckComArgOutPointerValid(aProgress); 11677 11678 AutoCaller autoCaller(this); 11679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11680 11681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11682 11683 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null, 11684 E_FAIL); 11685 11686 /* create a progress object to track operation completion */ 11687 ComObjPtr<Progress> pProgress; 11688 pProgress.createObject(); 11689 pProgress->init(getVirtualBox(), 11690 static_cast<IMachine *>(this) /* aInitiator */, 11691 Bstr(tr("Stopping the virtual machine")).raw(), 11692 FALSE /* aCancelable */); 11693 11694 /* fill in the console task data */ 11695 mConsoleTaskData.mLastState = mData->mMachineState; 11696 mConsoleTaskData.mProgress = pProgress; 11697 11698 /* set the state to Stopping (this is expected by Console::PowerDown()) */ 11699 setMachineState(MachineState_Stopping); 11700 11701 pProgress.queryInterfaceTo(aProgress); 11702 11703 return S_OK; 11704 } 11705 11706 /** 11707 * @note Locks this object for writing. 11708 */ 11709 STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg) 11710 { 11711 LogFlowThisFuncEnter(); 11712 11713 AutoCaller autoCaller(this); 11714 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11715 11716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11717 11718 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff) 11719 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping)) 11720 && mConsoleTaskData.mLastState != MachineState_Null, 11721 E_FAIL); 11722 11723 /* 11724 * On failure, set the state to the state we had when BeginPoweringDown() 11725 * was called (this is expected by Console::PowerDown() and the associated 11726 * task). On success the VM process already changed the state to 11727 * MachineState_PoweredOff, so no need to do anything. 11728 */ 11729 if (FAILED(iResult)) 11730 setMachineState(mConsoleTaskData.mLastState); 11731 11732 /* notify the progress object about operation completion */ 11733 Assert(mConsoleTaskData.mProgress); 11734 if (SUCCEEDED(iResult)) 11735 mConsoleTaskData.mProgress->notifyComplete(S_OK); 11736 else 11737 { 11738 Utf8Str strErrMsg(aErrMsg); 11739 if (strErrMsg.length()) 11740 mConsoleTaskData.mProgress->notifyComplete(iResult, 11741 COM_IIDOF(ISession), 11742 getComponentName(), 11743 strErrMsg.c_str()); 11744 else 11745 mConsoleTaskData.mProgress->notifyComplete(iResult); 11746 } 11747 11748 /* clear out the temporary saved state data */ 11749 mConsoleTaskData.mLastState = MachineState_Null; 11750 mConsoleTaskData.mProgress.setNull(); 11751 11752 LogFlowThisFuncLeave(); 11753 return S_OK; 11754 } 11755 11756 11757 /** 11758 * Goes through the USB filters of the given machine to see if the given 11759 * device matches any filter or not. 11760 * 11761 * @note Locks the same as USBController::hasMatchingFilter() does. 11762 */ 11763 STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice, 11764 BOOL *aMatched, 11765 ULONG *aMaskedIfs) 11766 { 11767 LogFlowThisFunc(("\n")); 11768 11769 CheckComArgNotNull(aUSBDevice); 11770 CheckComArgOutPointerValid(aMatched); 11771 11772 AutoCaller autoCaller(this); 11773 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11774 11775 #ifdef VBOX_WITH_USB 11776 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs); 11777 #else 11778 NOREF(aUSBDevice); 11779 NOREF(aMaskedIfs); 11780 *aMatched = FALSE; 11781 #endif 11782 11783 return S_OK; 11784 } 11785 11786 /** 11787 * @note Locks the same as Host::captureUSBDevice() does. 11788 */ 11789 STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId) 11790 { 11791 LogFlowThisFunc(("\n")); 11792 11793 AutoCaller autoCaller(this); 11794 AssertComRCReturnRC(autoCaller.rc()); 11795 11796 #ifdef VBOX_WITH_USB 11797 /* if captureDeviceForVM() fails, it must have set extended error info */ 11798 clearError(); 11799 MultiResult rc = mParent->host()->checkUSBProxyService(); 11800 if (FAILED(rc)) return rc; 11801 11802 USBProxyService *service = mParent->host()->usbProxyService(); 11803 AssertReturn(service, E_FAIL); 11804 return service->captureDeviceForVM(this, Guid(aId).ref()); 11805 #else 11806 NOREF(aId); 11807 return E_NOTIMPL; 11808 #endif 11809 } 11810 11811 /** 11812 * @note Locks the same as Host::detachUSBDevice() does. 11813 */ 11814 STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone) 11815 { 11816 LogFlowThisFunc(("\n")); 11817 11818 AutoCaller autoCaller(this); 11819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11820 11821 #ifdef VBOX_WITH_USB 11822 USBProxyService *service = mParent->host()->usbProxyService(); 11823 AssertReturn(service, E_FAIL); 11824 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone); 11825 #else 11826 NOREF(aId); 11827 NOREF(aDone); 11828 return E_NOTIMPL; 11829 #endif 11830 } 11831 11832 /** 11833 * Inserts all machine filters to the USB proxy service and then calls 11834 * Host::autoCaptureUSBDevices(). 11835 * 11836 * Called by Console from the VM process upon VM startup. 11837 * 11838 * @note Locks what called methods lock. 11839 */ 11840 STDMETHODIMP SessionMachine::AutoCaptureUSBDevices() 11841 { 11842 LogFlowThisFunc(("\n")); 11843 11844 AutoCaller autoCaller(this); 11845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11846 11847 #ifdef VBOX_WITH_USB 11848 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */); 11849 AssertComRC(rc); 11850 NOREF(rc); 11851 11852 USBProxyService *service = mParent->host()->usbProxyService(); 11853 AssertReturn(service, E_FAIL); 11854 return service->autoCaptureDevicesForVM(this); 11855 #else 11856 return S_OK; 11857 #endif 11858 } 11859 11860 /** 11861 * Removes all machine filters from the USB proxy service and then calls 11862 * Host::detachAllUSBDevices(). 11863 * 11864 * Called by Console from the VM process upon normal VM termination or by 11865 * SessionMachine::uninit() upon abnormal VM termination (from under the 11866 * Machine/SessionMachine lock). 11867 * 11868 * @note Locks what called methods lock. 11869 */ 11870 STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone) 11871 { 11872 LogFlowThisFunc(("\n")); 11873 11874 AutoCaller autoCaller(this); 11875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11876 11877 #ifdef VBOX_WITH_USB 11878 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */); 11879 AssertComRC(rc); 11880 NOREF(rc); 11881 11882 USBProxyService *service = mParent->host()->usbProxyService(); 11883 AssertReturn(service, E_FAIL); 11884 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */); 11885 #else 11886 NOREF(aDone); 11887 return S_OK; 11888 #endif 11889 } 11890 11891 /** 11892 * @note Locks this object for writing. 11893 */ 11894 STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession, 11895 IProgress **aProgress) 11896 { 11897 LogFlowThisFuncEnter(); 11898 11899 AssertReturn(aSession, E_INVALIDARG); 11900 AssertReturn(aProgress, E_INVALIDARG); 11901 11902 AutoCaller autoCaller(this); 11903 11904 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state())); 11905 /* 11906 * We don't assert below because it might happen that a non-direct session 11907 * informs us it is closed right after we've been uninitialized -- it's ok. 11908 */ 11909 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 11910 11911 /* get IInternalSessionControl interface */ 11912 ComPtr<IInternalSessionControl> control(aSession); 11913 11914 ComAssertRet(!control.isNull(), E_INVALIDARG); 11915 11916 /* Creating a Progress object requires the VirtualBox lock, and 11917 * thus locking it here is required by the lock order rules. */ 11918 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS); 11919 11920 if (control == mData->mSession.mDirectControl) 11921 { 11922 ComAssertRet(aProgress, E_POINTER); 11923 11924 /* The direct session is being normally closed by the client process 11925 * ----------------------------------------------------------------- */ 11926 11927 /* go to the closing state (essential for all open*Session() calls and 11928 * for #checkForDeath()) */ 11929 Assert(mData->mSession.mState == SessionState_Locked); 11930 mData->mSession.mState = SessionState_Unlocking; 11931 11932 /* set direct control to NULL to release the remote instance */ 11933 mData->mSession.mDirectControl.setNull(); 11934 LogFlowThisFunc(("Direct control is set to NULL\n")); 11935 11936 if (mData->mSession.mProgress) 11937 { 11938 /* finalize the progress, someone might wait if a frontend 11939 * closes the session before powering on the VM. */ 11940 mData->mSession.mProgress->notifyComplete(E_FAIL, 11941 COM_IIDOF(ISession), 11942 getComponentName(), 11943 tr("The VM session was closed before any attempt to power it on")); 11944 mData->mSession.mProgress.setNull(); 11945 } 11946 11947 /* Create the progress object the client will use to wait until 11948 * #checkForDeath() is called to uninitialize this session object after 11949 * it releases the IPC semaphore. 11950 * Note! Because we're "reusing" mProgress here, this must be a proxy 11951 * object just like for LaunchVMProcess. */ 11952 Assert(mData->mSession.mProgress.isNull()); 11953 ComObjPtr<ProgressProxy> progress; 11954 progress.createObject(); 11955 ComPtr<IUnknown> pPeer(mPeer); 11956 progress->init(mParent, pPeer, 11957 Bstr(tr("Closing session")).raw(), 11958 FALSE /* aCancelable */); 11959 progress.queryInterfaceTo(aProgress); 11960 mData->mSession.mProgress = progress; 11961 } 11962 else 11963 { 11964 /* the remote session is being normally closed */ 11965 Data::Session::RemoteControlList::iterator it = 11966 mData->mSession.mRemoteControls.begin(); 11967 while (it != mData->mSession.mRemoteControls.end()) 11968 { 11969 if (control == *it) 11970 break; 11971 ++it; 11972 } 11973 BOOL found = it != mData->mSession.mRemoteControls.end(); 11974 ComAssertMsgRet(found, ("The session is not found in the session list!"), 11975 E_INVALIDARG); 11976 mData->mSession.mRemoteControls.remove(*it); 11977 } 11978 11979 LogFlowThisFuncLeave(); 11980 return S_OK; 11981 } 11982 11983 /** 11984 * @note Locks this object for writing. 11985 */ 11986 STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath) 11987 { 11988 LogFlowThisFuncEnter(); 11989 11990 CheckComArgOutPointerValid(aProgress); 11991 CheckComArgOutPointerValid(aStateFilePath); 11992 11993 AutoCaller autoCaller(this); 11994 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11995 11996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11997 11998 AssertReturn( mData->mMachineState == MachineState_Paused 11999 && mConsoleTaskData.mLastState == MachineState_Null 12000 && mConsoleTaskData.strStateFilePath.isEmpty(), 12001 E_FAIL); 12002 12003 /* create a progress object to track operation completion */ 12004 ComObjPtr<Progress> pProgress; 12005 pProgress.createObject(); 12006 pProgress->init(getVirtualBox(), 12007 static_cast<IMachine *>(this) /* aInitiator */, 12008 Bstr(tr("Saving the execution state of the virtual machine")).raw(), 12009 FALSE /* aCancelable */); 12010 12011 Utf8Str strStateFilePath; 12012 /* stateFilePath is null when the machine is not running */ 12013 if (mData->mMachineState == MachineState_Paused) 12014 composeSavedStateFilename(strStateFilePath); 12015 12016 /* fill in the console task data */ 12017 mConsoleTaskData.mLastState = mData->mMachineState; 12018 mConsoleTaskData.strStateFilePath = strStateFilePath; 12019 mConsoleTaskData.mProgress = pProgress; 12020 12021 /* set the state to Saving (this is expected by Console::SaveState()) */ 12022 setMachineState(MachineState_Saving); 12023 12024 strStateFilePath.cloneTo(aStateFilePath); 12025 pProgress.queryInterfaceTo(aProgress); 12026 12027 return S_OK; 12028 } 12029 12030 /** 12031 * @note Locks mParent + this object for writing. 12032 */ 12033 STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg) 12034 { 12035 LogFlowThisFunc(("\n")); 12036 12037 AutoCaller autoCaller(this); 12038 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12039 12040 /* endSavingState() need mParent lock */ 12041 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); 12042 12043 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved) 12044 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving)) 12045 && mConsoleTaskData.mLastState != MachineState_Null 12046 && !mConsoleTaskData.strStateFilePath.isEmpty(), 12047 E_FAIL); 12048 12049 /* 12050 * On failure, set the state to the state we had when BeginSavingState() 12051 * was called (this is expected by Console::SaveState() and the associated 12052 * task). On success the VM process already changed the state to 12053 * MachineState_Saved, so no need to do anything. 12054 */ 12055 if (FAILED(iResult)) 12056 setMachineState(mConsoleTaskData.mLastState); 12057 12058 return endSavingState(iResult, aErrMsg); 12059 } 12060 12061 /** 12062 * @note Locks this object for writing. 12063 */ 12064 STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile) 12065 { 12066 LogFlowThisFunc(("\n")); 12067 12068 CheckComArgStrNotEmptyOrNull(aSavedStateFile); 12069 12070 AutoCaller autoCaller(this); 12071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12072 12073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12074 12075 AssertReturn( mData->mMachineState == MachineState_PoweredOff 12076 || mData->mMachineState == MachineState_Teleported 12077 || mData->mMachineState == MachineState_Aborted 12078 , E_FAIL); /** @todo setError. */ 12079 12080 Utf8Str stateFilePathFull = aSavedStateFile; 12081 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull); 12082 if (RT_FAILURE(vrc)) 12083 return setError(VBOX_E_FILE_ERROR, 12084 tr("Invalid saved state file path '%ls' (%Rrc)"), 12085 aSavedStateFile, 12086 vrc); 12087 12088 mSSData->strStateFilePath = stateFilePathFull; 12089 12090 /* The below setMachineState() will detect the state transition and will 12091 * update the settings file */ 12092 12093 return setMachineState(MachineState_Saved); 12094 } 12095 12096 STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames), 12097 ComSafeArrayOut(BSTR, aValues), 12098 ComSafeArrayOut(LONG64, aTimestamps), 12099 ComSafeArrayOut(BSTR, aFlags)) 12100 { 12101 LogFlowThisFunc(("\n")); 12102 12103 #ifdef VBOX_WITH_GUEST_PROPS 12104 using namespace guestProp; 12105 12106 AutoCaller autoCaller(this); 12107 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12108 12109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12110 12111 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER); 12112 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER); 12113 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER); 12114 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER); 12115 12116 size_t cEntries = mHWData->mGuestProperties.size(); 12117 com::SafeArray<BSTR> names(cEntries); 12118 com::SafeArray<BSTR> values(cEntries); 12119 com::SafeArray<LONG64> timestamps(cEntries); 12120 com::SafeArray<BSTR> flags(cEntries); 12121 unsigned i = 0; 12122 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin(); 12123 it != mHWData->mGuestProperties.end(); 12124 ++it) 12125 { 12126 char szFlags[MAX_FLAGS_LEN + 1]; 12127 it->strName.cloneTo(&names[i]); 12128 it->strValue.cloneTo(&values[i]); 12129 timestamps[i] = it->mTimestamp; 12130 /* If it is NULL, keep it NULL. */ 12131 if (it->mFlags) 12132 { 12133 writeFlags(it->mFlags, szFlags); 12134 Bstr(szFlags).cloneTo(&flags[i]); 12135 } 12136 else 12137 flags[i] = NULL; 12138 ++i; 12139 } 12140 names.detachTo(ComSafeArrayOutArg(aNames)); 12141 values.detachTo(ComSafeArrayOutArg(aValues)); 12142 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps)); 12143 flags.detachTo(ComSafeArrayOutArg(aFlags)); 12144 return S_OK; 12145 #else 12146 ReturnComNotImplemented(); 12147 #endif 12148 } 12149 12150 STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName, 12151 IN_BSTR aValue, 12152 LONG64 aTimestamp, 12153 IN_BSTR aFlags) 12154 { 12155 LogFlowThisFunc(("\n")); 12156 12157 #ifdef VBOX_WITH_GUEST_PROPS 12158 using namespace guestProp; 12159 12160 CheckComArgStrNotEmptyOrNull(aName); 12161 CheckComArgMaybeNull(aValue); 12162 CheckComArgMaybeNull(aFlags); 12163 12164 try 12165 { 12166 /* 12167 * Convert input up front. 12168 */ 12169 Utf8Str utf8Name(aName); 12170 uint32_t fFlags = NILFLAG; 12171 if (aFlags) 12172 { 12173 Utf8Str utf8Flags(aFlags); 12174 int vrc = validateFlags(utf8Flags.c_str(), &fFlags); 12175 AssertRCReturn(vrc, E_INVALIDARG); 12176 } 12177 12178 /* 12179 * Now grab the object lock, validate the state and do the update. 12180 */ 12181 AutoCaller autoCaller(this); 12182 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 12183 12184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12185 12186 switch (mData->mMachineState) 12187 { 12188 case MachineState_Paused: 12189 case MachineState_Running: 12190 case MachineState_Teleporting: 12191 case MachineState_TeleportingPausedVM: 12192 case MachineState_LiveSnapshotting: 12193 case MachineState_DeletingSnapshotOnline: 12194 case MachineState_DeletingSnapshotPaused: 12195 case MachineState_Saving: 12196 break; 12197 12198 default: 12199 #ifndef DEBUG_sunlover 12200 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)), 12201 VBOX_E_INVALID_VM_STATE); 12202 #else 12203 return VBOX_E_INVALID_VM_STATE; 12204 #endif 12205 } 12206 12207 setModified(IsModified_MachineData); 12208 mHWData.backup(); 12209 12210 /** @todo r=bird: The careful memory handling doesn't work out here because 12211 * the catch block won't undo any damage we've done. So, if push_back throws 12212 * bad_alloc then you've lost the value. 12213 * 12214 * Another thing. Doing a linear search here isn't extremely efficient, esp. 12215 * since values that changes actually bubbles to the end of the list. Using 12216 * something that has an efficient lookup and can tolerate a bit of updates 12217 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some 12218 * combination of RTStrCache (for sharing names and getting uniqueness into 12219 * the bargain) and hash/tree is another. */ 12220 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin(); 12221 iter != mHWData->mGuestProperties.end(); 12222 ++iter) 12223 if (utf8Name == iter->strName) 12224 { 12225 mHWData->mGuestProperties.erase(iter); 12226 mData->mGuestPropertiesModified = TRUE; 12227 break; 12228 } 12229 if (aValue != NULL) 12230 { 12231 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags }; 12232 mHWData->mGuestProperties.push_back(property); 12233 mData->mGuestPropertiesModified = TRUE; 12234 } 12235 12236 /* 12237 * Send a callback notification if appropriate 12238 */ 12239 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty() 12240 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(), 12241 RTSTR_MAX, 12242 utf8Name.c_str(), 12243 RTSTR_MAX, NULL) 12244 ) 12245 { 12246 alock.leave(); 12247 12248 mParent->onGuestPropertyChange(mData->mUuid, 12249 aName, 12250 aValue, 12251 aFlags); 12252 } 12253 } 12254 catch (...) 12255 { 12256 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 12257 } 12258 return S_OK; 12259 #else 12260 ReturnComNotImplemented(); 12261 #endif 12262 } 12263 12264 // public methods only for internal purposes 12265 ///////////////////////////////////////////////////////////////////////////// 12266 12267 /** 12268 * Called from the client watcher thread to check for expected or unexpected 12269 * death of the client process that has a direct session to this machine. 12270 * 12271 * On Win32 and on OS/2, this method is called only when we've got the 12272 * mutex (i.e. the client has either died or terminated normally) so it always 12273 * returns @c true (the client is terminated, the session machine is 12274 * uninitialized). 12275 * 12276 * On other platforms, the method returns @c true if the client process has 12277 * terminated normally or abnormally and the session machine was uninitialized, 12278 * and @c false if the client process is still alive. 12279 * 12280 * @note Locks this object for writing. 12281 */ 12282 bool SessionMachine::checkForDeath() 12283 { 12284 Uninit::Reason reason; 12285 bool terminated = false; 12286 12287 /* Enclose autoCaller with a block because calling uninit() from under it 12288 * will deadlock. */ 12289 { 12290 AutoCaller autoCaller(this); 12291 if (!autoCaller.isOk()) 12292 { 12293 /* return true if not ready, to cause the client watcher to exclude 12294 * the corresponding session from watching */ 12295 LogFlowThisFunc(("Already uninitialized!\n")); 12296 return true; 12297 } 12298 12299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12300 12301 /* Determine the reason of death: if the session state is Closing here, 12302 * everything is fine. Otherwise it means that the client did not call 12303 * OnSessionEnd() before it released the IPC semaphore. This may happen 12304 * either because the client process has abnormally terminated, or 12305 * because it simply forgot to call ISession::Close() before exiting. We 12306 * threat the latter also as an abnormal termination (see 12307 * Session::uninit() for details). */ 12308 reason = mData->mSession.mState == SessionState_Unlocking ? 12309 Uninit::Normal : 12310 Uninit::Abnormal; 12311 12312 #if defined(RT_OS_WINDOWS) 12313 12314 AssertMsg(mIPCSem, ("semaphore must be created")); 12315 12316 /* release the IPC mutex */ 12317 ::ReleaseMutex(mIPCSem); 12318 12319 terminated = true; 12320 12321 #elif defined(RT_OS_OS2) 12322 12323 AssertMsg(mIPCSem, ("semaphore must be created")); 12324 12325 /* release the IPC mutex */ 12326 ::DosReleaseMutexSem(mIPCSem); 12327 12328 terminated = true; 12329 12330 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12331 12332 AssertMsg(mIPCSem >= 0, ("semaphore must be created")); 12333 12334 int val = ::semctl(mIPCSem, 0, GETVAL); 12335 if (val > 0) 12336 { 12337 /* the semaphore is signaled, meaning the session is terminated */ 12338 terminated = true; 12339 } 12340 12341 #else 12342 # error "Port me!" 12343 #endif 12344 12345 } /* AutoCaller block */ 12346 12347 if (terminated) 12348 uninit(reason); 12349 12350 return terminated; 12351 } 12352 12353 /** 12354 * @note Locks this object for reading. 12355 */ 12356 HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter) 12357 { 12358 LogFlowThisFunc(("\n")); 12359 12360 AutoCaller autoCaller(this); 12361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12362 12363 ComPtr<IInternalSessionControl> directControl; 12364 { 12365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12366 directControl = mData->mSession.mDirectControl; 12367 } 12368 12369 /* ignore notifications sent after #OnSessionEnd() is called */ 12370 if (!directControl) 12371 return S_OK; 12372 12373 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter); 12374 } 12375 12376 /** 12377 * @note Locks this object for reading. 12378 */ 12379 HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName, 12380 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort) 12381 { 12382 LogFlowThisFunc(("\n")); 12383 12384 AutoCaller autoCaller(this); 12385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12386 12387 ComPtr<IInternalSessionControl> directControl; 12388 { 12389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12390 directControl = mData->mSession.mDirectControl; 12391 } 12392 12393 /* ignore notifications sent after #OnSessionEnd() is called */ 12394 if (!directControl) 12395 return S_OK; 12396 /* 12397 * instead acting like callback we ask IVirtualBox deliver corresponding event 12398 */ 12399 12400 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort); 12401 return S_OK; 12402 } 12403 12404 /** 12405 * @note Locks this object for reading. 12406 */ 12407 HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort) 12408 { 12409 LogFlowThisFunc(("\n")); 12410 12411 AutoCaller autoCaller(this); 12412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12413 12414 ComPtr<IInternalSessionControl> directControl; 12415 { 12416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12417 directControl = mData->mSession.mDirectControl; 12418 } 12419 12420 /* ignore notifications sent after #OnSessionEnd() is called */ 12421 if (!directControl) 12422 return S_OK; 12423 12424 return directControl->OnSerialPortChange(serialPort); 12425 } 12426 12427 /** 12428 * @note Locks this object for reading. 12429 */ 12430 HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort) 12431 { 12432 LogFlowThisFunc(("\n")); 12433 12434 AutoCaller autoCaller(this); 12435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12436 12437 ComPtr<IInternalSessionControl> directControl; 12438 { 12439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12440 directControl = mData->mSession.mDirectControl; 12441 } 12442 12443 /* ignore notifications sent after #OnSessionEnd() is called */ 12444 if (!directControl) 12445 return S_OK; 12446 12447 return directControl->OnParallelPortChange(parallelPort); 12448 } 12449 12450 /** 12451 * @note Locks this object for reading. 12452 */ 12453 HRESULT SessionMachine::onStorageControllerChange() 12454 { 12455 LogFlowThisFunc(("\n")); 12456 12457 AutoCaller autoCaller(this); 12458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12459 12460 ComPtr<IInternalSessionControl> directControl; 12461 { 12462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12463 directControl = mData->mSession.mDirectControl; 12464 } 12465 12466 /* ignore notifications sent after #OnSessionEnd() is called */ 12467 if (!directControl) 12468 return S_OK; 12469 12470 return directControl->OnStorageControllerChange(); 12471 } 12472 12473 /** 12474 * @note Locks this object for reading. 12475 */ 12476 HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce) 12477 { 12478 LogFlowThisFunc(("\n")); 12479 12480 AutoCaller autoCaller(this); 12481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12482 12483 ComPtr<IInternalSessionControl> directControl; 12484 { 12485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12486 directControl = mData->mSession.mDirectControl; 12487 } 12488 12489 /* ignore notifications sent after #OnSessionEnd() is called */ 12490 if (!directControl) 12491 return S_OK; 12492 12493 return directControl->OnMediumChange(aAttachment, aForce); 12494 } 12495 12496 /** 12497 * @note Locks this object for reading. 12498 */ 12499 HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove) 12500 { 12501 LogFlowThisFunc(("\n")); 12502 12503 AutoCaller autoCaller(this); 12504 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 12505 12506 ComPtr<IInternalSessionControl> directControl; 12507 { 12508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12509 directControl = mData->mSession.mDirectControl; 12510 } 12511 12512 /* ignore notifications sent after #OnSessionEnd() is called */ 12513 if (!directControl) 12514 return S_OK; 12515 12516 return directControl->OnCPUChange(aCPU, aRemove); 12517 } 12518 12519 HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap) 12520 { 12521 LogFlowThisFunc(("\n")); 12522 12523 AutoCaller autoCaller(this); 12524 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 12525 12526 ComPtr<IInternalSessionControl> directControl; 12527 { 12528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12529 directControl = mData->mSession.mDirectControl; 12530 } 12531 12532 /* ignore notifications sent after #OnSessionEnd() is called */ 12533 if (!directControl) 12534 return S_OK; 12535 12536 return directControl->OnCPUExecutionCapChange(aExecutionCap); 12537 } 12538 12539 /** 12540 * @note Locks this object for reading. 12541 */ 12542 HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart) 12543 { 12544 LogFlowThisFunc(("\n")); 12545 12546 AutoCaller autoCaller(this); 12547 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12548 12549 ComPtr<IInternalSessionControl> directControl; 12550 { 12551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12552 directControl = mData->mSession.mDirectControl; 12553 } 12554 12555 /* ignore notifications sent after #OnSessionEnd() is called */ 12556 if (!directControl) 12557 return S_OK; 12558 12559 return directControl->OnVRDEServerChange(aRestart); 12560 } 12561 12562 /** 12563 * @note Locks this object for reading. 12564 */ 12565 HRESULT SessionMachine::onUSBControllerChange() 12566 { 12567 LogFlowThisFunc(("\n")); 12568 12569 AutoCaller autoCaller(this); 12570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12571 12572 ComPtr<IInternalSessionControl> directControl; 12573 { 12574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12575 directControl = mData->mSession.mDirectControl; 12576 } 12577 12578 /* ignore notifications sent after #OnSessionEnd() is called */ 12579 if (!directControl) 12580 return S_OK; 12581 12582 return directControl->OnUSBControllerChange(); 12583 } 12584 12585 /** 12586 * @note Locks this object for reading. 12587 */ 12588 HRESULT SessionMachine::onSharedFolderChange() 12589 { 12590 LogFlowThisFunc(("\n")); 12591 12592 AutoCaller autoCaller(this); 12593 AssertComRCReturnRC(autoCaller.rc()); 12594 12595 ComPtr<IInternalSessionControl> directControl; 12596 { 12597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12598 directControl = mData->mSession.mDirectControl; 12599 } 12600 12601 /* ignore notifications sent after #OnSessionEnd() is called */ 12602 if (!directControl) 12603 return S_OK; 12604 12605 return directControl->OnSharedFolderChange(FALSE /* aGlobal */); 12606 } 12607 12608 /** 12609 * @note Locks this object for reading. 12610 */ 12611 HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) 12612 { 12613 LogFlowThisFunc(("\n")); 12614 12615 AutoCaller autoCaller(this); 12616 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 12617 12618 ComPtr<IInternalSessionControl> directControl; 12619 { 12620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12621 directControl = mData->mSession.mDirectControl; 12622 } 12623 12624 /* ignore notifications sent after #OnSessionEnd() is called */ 12625 if (!directControl) 12626 return S_OK; 12627 12628 return directControl->OnBandwidthGroupChange(aBandwidthGroup); 12629 } 12630 12631 /** 12632 * @note Locks this object for reading. 12633 */ 12634 HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove) 12635 { 12636 LogFlowThisFunc(("\n")); 12637 12638 AutoCaller autoCaller(this); 12639 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12640 12641 ComPtr<IInternalSessionControl> directControl; 12642 { 12643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12644 directControl = mData->mSession.mDirectControl; 12645 } 12646 12647 /* ignore notifications sent after #OnSessionEnd() is called */ 12648 if (!directControl) 12649 return S_OK; 12650 12651 return directControl->OnStorageDeviceChange(aAttachment, aRemove); 12652 } 12653 12654 /** 12655 * Returns @c true if this machine's USB controller reports it has a matching 12656 * filter for the given USB device and @c false otherwise. 12657 * 12658 * @note Caller must have requested machine read lock. 12659 */ 12660 bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs) 12661 { 12662 AutoCaller autoCaller(this); 12663 /* silently return if not ready -- this method may be called after the 12664 * direct machine session has been called */ 12665 if (!autoCaller.isOk()) 12666 return false; 12667 12668 12669 #ifdef VBOX_WITH_USB 12670 switch (mData->mMachineState) 12671 { 12672 case MachineState_Starting: 12673 case MachineState_Restoring: 12674 case MachineState_TeleportingIn: 12675 case MachineState_Paused: 12676 case MachineState_Running: 12677 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of 12678 * elsewhere... */ 12679 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs); 12680 default: break; 12681 } 12682 #else 12683 NOREF(aDevice); 12684 NOREF(aMaskedIfs); 12685 #endif 12686 return false; 12687 } 12688 12689 /** 12690 * @note The calls shall hold no locks. Will temporarily lock this object for reading. 12691 */ 12692 HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice, 12693 IVirtualBoxErrorInfo *aError, 12694 ULONG aMaskedIfs) 12695 { 12696 LogFlowThisFunc(("\n")); 12697 12698 AutoCaller autoCaller(this); 12699 12700 /* This notification may happen after the machine object has been 12701 * uninitialized (the session was closed), so don't assert. */ 12702 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 12703 12704 ComPtr<IInternalSessionControl> directControl; 12705 { 12706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12707 directControl = mData->mSession.mDirectControl; 12708 } 12709 12710 /* fail on notifications sent after #OnSessionEnd() is called, it is 12711 * expected by the caller */ 12712 if (!directControl) 12713 return E_FAIL; 12714 12715 /* No locks should be held at this point. */ 12716 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf()))); 12717 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf()))); 12718 12719 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs); 12720 } 12721 12722 /** 12723 * @note The calls shall hold no locks. Will temporarily lock this object for reading. 12724 */ 12725 HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId, 12726 IVirtualBoxErrorInfo *aError) 12727 { 12728 LogFlowThisFunc(("\n")); 12729 12730 AutoCaller autoCaller(this); 12731 12732 /* This notification may happen after the machine object has been 12733 * uninitialized (the session was closed), so don't assert. */ 12734 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 12735 12736 ComPtr<IInternalSessionControl> directControl; 12737 { 12738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 12739 directControl = mData->mSession.mDirectControl; 12740 } 12741 12742 /* fail on notifications sent after #OnSessionEnd() is called, it is 12743 * expected by the caller */ 12744 if (!directControl) 12745 return E_FAIL; 12746 12747 /* No locks should be held at this point. */ 12748 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf()))); 12749 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf()))); 12750 12751 return directControl->OnUSBDeviceDetach(aId, aError); 12752 } 12753 12754 // protected methods 12755 ///////////////////////////////////////////////////////////////////////////// 12756 12757 /** 12758 * Helper method to finalize saving the state. 12759 * 12760 * @note Must be called from under this object's lock. 12761 * 12762 * @param aRc S_OK if the snapshot has been taken successfully 12763 * @param aErrMsg human readable error message for failure 12764 * 12765 * @note Locks mParent + this objects for writing. 12766 */ 12767 HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg) 12768 { 12769 LogFlowThisFuncEnter(); 12770 12771 AutoCaller autoCaller(this); 12772 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12773 12774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12775 12776 HRESULT rc = S_OK; 12777 12778 if (SUCCEEDED(aRc)) 12779 { 12780 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath; 12781 12782 /* save all VM settings */ 12783 rc = saveSettings(NULL); 12784 // no need to check whether VirtualBox.xml needs saving also since 12785 // we can't have a name change pending at this point 12786 } 12787 else 12788 { 12789 // delete the saved state file (it might have been already created); 12790 // we need not check whether this is shared with a snapshot here because 12791 // we certainly created this saved state file here anew 12792 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str()); 12793 } 12794 12795 /* notify the progress object about operation completion */ 12796 Assert(mConsoleTaskData.mProgress); 12797 if (SUCCEEDED(aRc)) 12798 mConsoleTaskData.mProgress->notifyComplete(S_OK); 12799 else 12800 { 12801 if (aErrMsg.length()) 12802 mConsoleTaskData.mProgress->notifyComplete(aRc, 12803 COM_IIDOF(ISession), 12804 getComponentName(), 12805 aErrMsg.c_str()); 12806 else 12807 mConsoleTaskData.mProgress->notifyComplete(aRc); 12808 } 12809 12810 /* clear out the temporary saved state data */ 12811 mConsoleTaskData.mLastState = MachineState_Null; 12812 mConsoleTaskData.strStateFilePath.setNull(); 12813 mConsoleTaskData.mProgress.setNull(); 12814 12815 LogFlowThisFuncLeave(); 12816 return rc; 12817 } 12818 12819 /** 12820 * Deletes the given file if it is no longer in use by either the current machine state 12821 * (if the machine is "saved") or any of the machine's snapshots. 12822 * 12823 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine 12824 * but is different for each SnapshotMachine. When calling this, the order of calling this 12825 * function on the one hand and changing that variable OR the snapshots tree on the other hand 12826 * is therefore critical. I know, it's all rather messy. 12827 * 12828 * @param strStateFile 12829 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use. 12830 */ 12831 void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile, 12832 Snapshot *pSnapshotToIgnore) 12833 { 12834 // it is safe to delete this saved state file if it is not currently in use by the machine ... 12835 if ( (strStateFile.isNotEmpty()) 12836 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state 12837 ) 12838 // ... and it must also not be shared with other snapshots 12839 if ( !mData->mFirstSnapshot 12840 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore) 12841 // this checks the SnapshotMachine's state file paths 12842 ) 12843 RTFileDelete(strStateFile.c_str()); 12844 } 12845 12846 /** 12847 * Locks the attached media. 12848 * 12849 * All attached hard disks are locked for writing and DVD/floppy are locked for 12850 * reading. Parents of attached hard disks (if any) are locked for reading. 12851 * 12852 * This method also performs accessibility check of all media it locks: if some 12853 * media is inaccessible, the method will return a failure and a bunch of 12854 * extended error info objects per each inaccessible medium. 12855 * 12856 * Note that this method is atomic: if it returns a success, all media are 12857 * locked as described above; on failure no media is locked at all (all 12858 * succeeded individual locks will be undone). 12859 * 12860 * This method is intended to be called when the machine is in Starting or 12861 * Restoring state and asserts otherwise. 12862 * 12863 * The locks made by this method must be undone by calling #unlockMedia() when 12864 * no more needed. 12865 */ 12866 HRESULT SessionMachine::lockMedia() 12867 { 12868 AutoCaller autoCaller(this); 12869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12870 12871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12872 12873 AssertReturn( mData->mMachineState == MachineState_Starting 12874 || mData->mMachineState == MachineState_Restoring 12875 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL); 12876 /* bail out if trying to lock things with already set up locking */ 12877 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL); 12878 12879 clearError(); 12880 MultiResult mrc(S_OK); 12881 12882 /* Collect locking information for all medium objects attached to the VM. */ 12883 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 12884 it != mMediaData->mAttachments.end(); 12885 ++it) 12886 { 12887 MediumAttachment* pAtt = *it; 12888 DeviceType_T devType = pAtt->getType(); 12889 Medium *pMedium = pAtt->getMedium(); 12890 12891 MediumLockList *pMediumLockList(new MediumLockList()); 12892 // There can be attachments without a medium (floppy/dvd), and thus 12893 // it's impossible to create a medium lock list. It still makes sense 12894 // to have the empty medium lock list in the map in case a medium is 12895 // attached later. 12896 if (pMedium != NULL) 12897 { 12898 MediumType_T mediumType = pMedium->getType(); 12899 bool fIsReadOnlyLock = mediumType == MediumType_Readonly 12900 || mediumType == MediumType_Shareable; 12901 bool fIsVitalImage = (devType == DeviceType_HardDisk); 12902 12903 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */, 12904 !fIsReadOnlyLock /* fMediumLockWrite */, 12905 NULL, 12906 *pMediumLockList); 12907 if (FAILED(mrc)) 12908 { 12909 delete pMediumLockList; 12910 mData->mSession.mLockedMedia.Clear(); 12911 break; 12912 } 12913 } 12914 12915 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList); 12916 if (FAILED(rc)) 12917 { 12918 mData->mSession.mLockedMedia.Clear(); 12919 mrc = setError(rc, 12920 tr("Collecting locking information for all attached media failed")); 12921 break; 12922 } 12923 } 12924 12925 if (SUCCEEDED(mrc)) 12926 { 12927 /* Now lock all media. If this fails, nothing is locked. */ 12928 HRESULT rc = mData->mSession.mLockedMedia.Lock(); 12929 if (FAILED(rc)) 12930 { 12931 mrc = setError(rc, 12932 tr("Locking of attached media failed")); 12933 } 12934 } 12935 12936 return mrc; 12937 } 12938 12939 /** 12940 * Undoes the locks made by by #lockMedia(). 12941 */ 12942 void SessionMachine::unlockMedia() 12943 { 12944 AutoCaller autoCaller(this); 12945 AssertComRCReturnVoid(autoCaller.rc()); 12946 12947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12948 12949 /* we may be holding important error info on the current thread; 12950 * preserve it */ 12951 ErrorInfoKeeper eik; 12952 12953 HRESULT rc = mData->mSession.mLockedMedia.Clear(); 12954 AssertComRC(rc); 12955 } 12956 12957 /** 12958 * Helper to change the machine state (reimplementation). 12959 * 12960 * @note Locks this object for writing. 12961 */ 12962 HRESULT SessionMachine::setMachineState(MachineState_T aMachineState) 12963 { 12964 LogFlowThisFuncEnter(); 12965 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) )); 12966 12967 AutoCaller autoCaller(this); 12968 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 12969 12970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 12971 12972 MachineState_T oldMachineState = mData->mMachineState; 12973 12974 AssertMsgReturn(oldMachineState != aMachineState, 12975 ("oldMachineState=%s, aMachineState=%s\n", 12976 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)), 12977 E_FAIL); 12978 12979 HRESULT rc = S_OK; 12980 12981 int stsFlags = 0; 12982 bool deleteSavedState = false; 12983 12984 /* detect some state transitions */ 12985 12986 if ( ( oldMachineState == MachineState_Saved 12987 && aMachineState == MachineState_Restoring) 12988 || ( ( oldMachineState == MachineState_PoweredOff 12989 || oldMachineState == MachineState_Teleported 12990 || oldMachineState == MachineState_Aborted 12991 ) 12992 && ( aMachineState == MachineState_TeleportingIn 12993 || aMachineState == MachineState_Starting 12994 ) 12995 ) 12996 ) 12997 { 12998 /* The EMT thread is about to start */ 12999 13000 /* Nothing to do here for now... */ 13001 13002 /// @todo NEWMEDIA don't let mDVDDrive and other children 13003 /// change anything when in the Starting/Restoring state 13004 } 13005 else if ( ( oldMachineState == MachineState_Running 13006 || oldMachineState == MachineState_Paused 13007 || oldMachineState == MachineState_Teleporting 13008 || oldMachineState == MachineState_LiveSnapshotting 13009 || oldMachineState == MachineState_Stuck 13010 || oldMachineState == MachineState_Starting 13011 || oldMachineState == MachineState_Stopping 13012 || oldMachineState == MachineState_Saving 13013 || oldMachineState == MachineState_Restoring 13014 || oldMachineState == MachineState_TeleportingPausedVM 13015 || oldMachineState == MachineState_TeleportingIn 13016 ) 13017 && ( aMachineState == MachineState_PoweredOff 13018 || aMachineState == MachineState_Saved 13019 || aMachineState == MachineState_Teleported 13020 || aMachineState == MachineState_Aborted 13021 ) 13022 /* ignore PoweredOff->Saving->PoweredOff transition when taking a 13023 * snapshot */ 13024 && ( mConsoleTaskData.mSnapshot.isNull() 13025 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */ 13026 ) 13027 ) 13028 { 13029 /* The EMT thread has just stopped, unlock attached media. Note that as 13030 * opposed to locking that is done from Console, we do unlocking here 13031 * because the VM process may have aborted before having a chance to 13032 * properly unlock all media it locked. */ 13033 13034 unlockMedia(); 13035 } 13036 13037 if (oldMachineState == MachineState_Restoring) 13038 { 13039 if (aMachineState != MachineState_Saved) 13040 { 13041 /* 13042 * delete the saved state file once the machine has finished 13043 * restoring from it (note that Console sets the state from 13044 * Restoring to Saved if the VM couldn't restore successfully, 13045 * to give the user an ability to fix an error and retry -- 13046 * we keep the saved state file in this case) 13047 */ 13048 deleteSavedState = true; 13049 } 13050 } 13051 else if ( oldMachineState == MachineState_Saved 13052 && ( aMachineState == MachineState_PoweredOff 13053 || aMachineState == MachineState_Aborted 13054 || aMachineState == MachineState_Teleported 13055 ) 13056 ) 13057 { 13058 /* 13059 * delete the saved state after Console::ForgetSavedState() is called 13060 * or if the VM process (owning a direct VM session) crashed while the 13061 * VM was Saved 13062 */ 13063 13064 /// @todo (dmik) 13065 // Not sure that deleting the saved state file just because of the 13066 // client death before it attempted to restore the VM is a good 13067 // thing. But when it crashes we need to go to the Aborted state 13068 // which cannot have the saved state file associated... The only 13069 // way to fix this is to make the Aborted condition not a VM state 13070 // but a bool flag: i.e., when a crash occurs, set it to true and 13071 // change the state to PoweredOff or Saved depending on the 13072 // saved state presence. 13073 13074 deleteSavedState = true; 13075 mData->mCurrentStateModified = TRUE; 13076 stsFlags |= SaveSTS_CurStateModified; 13077 } 13078 13079 if ( aMachineState == MachineState_Starting 13080 || aMachineState == MachineState_Restoring 13081 || aMachineState == MachineState_TeleportingIn 13082 ) 13083 { 13084 /* set the current state modified flag to indicate that the current 13085 * state is no more identical to the state in the 13086 * current snapshot */ 13087 if (!mData->mCurrentSnapshot.isNull()) 13088 { 13089 mData->mCurrentStateModified = TRUE; 13090 stsFlags |= SaveSTS_CurStateModified; 13091 } 13092 } 13093 13094 if (deleteSavedState) 13095 { 13096 if (mRemoveSavedState) 13097 { 13098 Assert(!mSSData->strStateFilePath.isEmpty()); 13099 13100 // it is safe to delete the saved state file if ... 13101 if ( !mData->mFirstSnapshot // ... we have no snapshots or 13102 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */) 13103 // ... none of the snapshots share the saved state file 13104 ) 13105 RTFileDelete(mSSData->strStateFilePath.c_str()); 13106 } 13107 13108 mSSData->strStateFilePath.setNull(); 13109 stsFlags |= SaveSTS_StateFilePath; 13110 } 13111 13112 /* redirect to the underlying peer machine */ 13113 mPeer->setMachineState(aMachineState); 13114 13115 if ( aMachineState == MachineState_PoweredOff 13116 || aMachineState == MachineState_Teleported 13117 || aMachineState == MachineState_Aborted 13118 || aMachineState == MachineState_Saved) 13119 { 13120 /* the machine has stopped execution 13121 * (or the saved state file was adopted) */ 13122 stsFlags |= SaveSTS_StateTimeStamp; 13123 } 13124 13125 if ( ( oldMachineState == MachineState_PoweredOff 13126 || oldMachineState == MachineState_Aborted 13127 || oldMachineState == MachineState_Teleported 13128 ) 13129 && aMachineState == MachineState_Saved) 13130 { 13131 /* the saved state file was adopted */ 13132 Assert(!mSSData->strStateFilePath.isEmpty()); 13133 stsFlags |= SaveSTS_StateFilePath; 13134 } 13135 13136 #ifdef VBOX_WITH_GUEST_PROPS 13137 if ( aMachineState == MachineState_PoweredOff 13138 || aMachineState == MachineState_Aborted 13139 || aMachineState == MachineState_Teleported) 13140 { 13141 /* Make sure any transient guest properties get removed from the 13142 * property store on shutdown. */ 13143 13144 HWData::GuestPropertyList::iterator it; 13145 BOOL fNeedsSaving = mData->mGuestPropertiesModified; 13146 if (!fNeedsSaving) 13147 for (it = mHWData->mGuestProperties.begin(); 13148 it != mHWData->mGuestProperties.end(); ++it) 13149 if ( (it->mFlags & guestProp::TRANSIENT) 13150 || (it->mFlags & guestProp::TRANSRESET)) 13151 { 13152 fNeedsSaving = true; 13153 break; 13154 } 13155 if (fNeedsSaving) 13156 { 13157 mData->mCurrentStateModified = TRUE; 13158 stsFlags |= SaveSTS_CurStateModified; 13159 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings? 13160 } 13161 } 13162 #endif 13163 13164 rc = saveStateSettings(stsFlags); 13165 13166 if ( ( oldMachineState != MachineState_PoweredOff 13167 && oldMachineState != MachineState_Aborted 13168 && oldMachineState != MachineState_Teleported 13169 ) 13170 && ( aMachineState == MachineState_PoweredOff 13171 || aMachineState == MachineState_Aborted 13172 || aMachineState == MachineState_Teleported 13173 ) 13174 ) 13175 { 13176 /* we've been shut down for any reason */ 13177 /* no special action so far */ 13178 } 13179 13180 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) )); 13181 LogFlowThisFuncLeave(); 13182 return rc; 13183 } 13184 13185 /** 13186 * Sends the current machine state value to the VM process. 13187 * 13188 * @note Locks this object for reading, then calls a client process. 13189 */ 13190 HRESULT SessionMachine::updateMachineStateOnClient() 13191 { 13192 AutoCaller autoCaller(this); 13193 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 13194 13195 ComPtr<IInternalSessionControl> directControl; 13196 { 13197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 13198 AssertReturn(!!mData, E_FAIL); 13199 directControl = mData->mSession.mDirectControl; 13200 13201 /* directControl may be already set to NULL here in #OnSessionEnd() 13202 * called too early by the direct session process while there is still 13203 * some operation (like deleting the snapshot) in progress. The client 13204 * process in this case is waiting inside Session::close() for the 13205 * "end session" process object to complete, while #uninit() called by 13206 * #checkForDeath() on the Watcher thread is waiting for the pending 13207 * operation to complete. For now, we accept this inconsistent behavior 13208 * and simply do nothing here. */ 13209 13210 if (mData->mSession.mState == SessionState_Unlocking) 13211 return S_OK; 13212 13213 AssertReturn(!directControl.isNull(), E_FAIL); 13214 } 13215 13216 return directControl->UpdateMachineState(mData->mMachineState); 13217 } 771 void MachineCloneVM::destroy() 772 { 773 delete this; 774 } 775
Note:
See TracChangeset
for help on using the changeset viewer.