VirtualBox

Changeset 37491 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jun 16, 2011 12:23:34 PM (14 years ago)
Author:
vboxsync
Message:

Main-CloneVM: factor out the clone VM task

Location:
trunk/src/VBox/Main
Files:
3 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/Makefile.kmk

    r37282 r37491  
    302302        src-server/HostPower.cpp \
    303303        src-server/MachineImpl.cpp \
     304        src-server/MachineImplCloneVM.cpp \
    304305        src-server/Matching.cpp \
    305306        src-server/MediumAttachmentImpl.cpp \
  • trunk/src/VBox/Main/include/MachineImpl.h

    r37485 r37491  
    908908    friend class Appliance;
    909909    friend class VirtualBox;
     910
     911    friend class MachineCloneVM;
    910912};
    911913
  • trunk/src/VBox/Main/include/MachineImplCloneVM.h

    r37485 r37491  
    1 /* $Id$ */
    21/** @file
    3  * VirtualBox COM class implementation
     2 * Definition of MachineCloneVM
    43 */
    54
    65/*
    7  * Copyright (C) 2006-2011 Oracle Corporation
     6 * Copyright (C) 2011 Oracle Corporation
    87 *
    98 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1615 */
    1716
    18 #ifndef ____H_MACHINEIMPL
    19 #define ____H_MACHINEIMPL
     17#ifndef ____H_MACHINEIMPLCLONEVM
     18#define ____H_MACHINEIMPLCLONEVM
    2019
    21 #include "VirtualBoxBase.h"
    22 #include "SnapshotImpl.h"
     20#include "MachineImpl.h"
    2321#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 Windows
    34 #include "BandwidthControlImpl.h"
    35 #include "BandwidthGroupImpl.h"
    36 #include "VBox/settings.h"
    37 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    38 #include "Performance.h"
    39 #include "PerformanceImpl.h"
    40 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    4122
    42 // generated header
    43 #include "SchemaDefs.h"
     23/* Forward declaration of the d-pointer. */
     24struct MachineCloneVMPrivate;
    4425
    45 #include "VBox/com/ErrorInfo.h"
     26class MachineCloneVM
     27{
     28public:
     29    MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode, bool fFullClone);
     30    ~MachineCloneVM();
    4631
    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);
    35833
    35934protected:
    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();
    36637
    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;
    46441
    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;
    91043};
    91144
    912 // SessionMachine class
    913 ////////////////////////////////////////////////////////////////////////////////
     45#endif // ____H_MACHINEIMPLCLONEVM
     46/* vi: set tabstop=4 shiftwidth=4 expandtab: */
    91447
    915 /**
    916  *  @note Notes on locking objects of this class:
    917  *  SessionMachine shares some data with the primary Machine instance (pointed
    918  *  to by the |mPeer| member). In order to provide data consistency it also
    919  *  shares its lock handle. This means that whenever you lock a SessionMachine
    920  *  instance using Auto[Reader]Lock or AutoMultiLock, the corresponding Machine
    921  *  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 only
    945     HRESULT init(Machine *aMachine);
    946     void uninit() { uninit(Uninit::Unexpected); }
    947 
    948     // util::Lockable interface
    949     RWLockHandle *lockHandle() const;
    950 
    951     // IInternalMachineControl methods
    952     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 purposes
    995 
    996     virtual bool isSessionMachine() const
    997     {
    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 ConsoleTaskData
    1028     {
    1029         ConsoleTaskData()
    1030             : mLastState(MachineState_Null)
    1031         { }
    1032 
    1033         MachineState_T mLastState;
    1034         ComObjPtr<Progress> mProgress;
    1035 
    1036         // used when taking snapshot
    1037         ComObjPtr<Snapshot> mSnapshot;
    1038 
    1039         // used when saving state (either as part of a snapshot or separate)
    1040         Utf8Str strStateFilePath;
    1041     };
    1042 
    1043     struct Uninit
    1044     {
    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_KEYGEN
    1118     Bstr mIPCKey;
    1119 # endif /*VBOX_WITH_NEW_SYS_V_KEYGEN */
    1120 #else
    1121 # error "Port me!"
    1122 #endif
    1123 
    1124     static DECLCALLBACK(int) taskHandler(RTTHREAD thread, void *pvUser);
    1125 };
    1126 
    1127 // SnapshotMachine class
    1128 ////////////////////////////////////////////////////////////////////////////////
    1129 
    1130 /**
    1131  *  @note Notes on locking objects of this class:
    1132  *  SnapshotMachine shares some data with the primary Machine instance (pointed
    1133  *  to by the |mPeer| member). In order to provide data consistency it also
    1134  *  shares its lock handle. This means that whenever you lock a SessionMachine
    1135  *  instance using Auto[Reader]Lock or AutoMultiLock, the corresponding Machine
    1136  *  instance is also locked in the same lock mode. Keep it in mind.
    1137  */
    1138 class ATL_NO_VTABLE SnapshotMachine :
    1139     public Machine
    1140 {
    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 only
    1158     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 interface
    1169     RWLockHandle *lockHandle() const;
    1170 
    1171     // public methods only for internal purposes
    1172 
    1173     virtual bool isSnapshotMachine() const
    1174     {
    1175         return true;
    1176     }
    1177 
    1178     HRESULT onSnapshotChange(Snapshot *aSnapshot);
    1179 
    1180     // unsafe inline public methods for internal purposes only (ensure there is
    1181     // 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 definition
    1193 
    1194 inline const Guid &Machine::getSnapshotId() const
    1195 {
    1196     return (isSnapshotMachine())
    1197                 ? static_cast<const SnapshotMachine*>(this)->getSnapshotId()
    1198                 : Guid::Empty;
    1199 }
    1200 
    1201 
    1202 #endif // ____H_MACHINEIMPL
    1203 /* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r37486 r37491  
    5050#include "DisplayUtils.h"
    5151#include "BandwidthControlImpl.h"
     52#include "MachineImplCloneVM.h"
    5253
    5354// generated header
     
    60736074}
    60746075
    6075 struct Machine::CloneVMTask
    6076 {
    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 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 */
    6102 };
    6103 
    61046076STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, BOOL fFullClone, IProgress **pProgress)
    61056077{
     
    61086080    CheckComArgNotNull(pTarget);
    61096081    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);
    62776090
    62786091    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
    6312 {
    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) const
    6336 {
    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) const
    6349 {
    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_HardDisk
    6362                 && 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) const
    6371 {
    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) const
    6384 {
    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 task
    6415  * @return
    6416  */
    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) it
    6430      *   goes to the new main VM folder. Maybe we like to be a little bit
    6431      *   smarter here.
    6432      * - Snapshot diffs (can) have the uuid as name. After cloning this isn't
    6433      *   right anymore. Is it worth to change to the new uuid? Or should the
    6434      *   cloned disks called exactly as the original one or should all new disks
    6435      *   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     try
    6445     {
    6446         /* Copy all the configuration from this machine to an empty
    6447          * 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 section
    6454          * 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         }else
    6471         if (   pTask->mode == CloneMode_MachineAndChildStates
    6472             && !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 the
    6485          * 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 is
    6502          * necessary, cause different snapshots could have the same
    6503          * parents/parent chain. If a medium is in this map already, it isn't
    6504          * 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                 else
    6533                 {
    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 defaults
    6546                          * 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, then
    6587                          * 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 the
    6597                      * 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); // pllRegistriesThatNeedSaving
    6616                 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); // pllRegistriesThatNeedSaving
    6630                 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 new
    6641              * 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 current
    6664              * machine state or the snapshots. */
    6665             if (sst.snapshotUuid.isEmpty())
    6666                 pTrgMCF->strStateFile = strTrgSaveState;
    6667             else
    6668                 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 parent
    6699 //                Guid uuidRegistryParent;
    6700 //                if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
    6701 //                {
    6702                     // parent image has no registry: this can happen if we're attaching a new immutable
    6703                     // image that has not yet been attached (medium then points to the base and we're
    6704                     // creating the diff image for the immutable, and the parent is not yet registered);
    6705                     // put the parent in the machine registry then
    6706 //                    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 stuff
    6727              * over to the new machine. The machine have to be mutable for
    6728              * 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 make
    6737          * sure the new medias know of there new parent or we get in trouble
    6738          * when the media registry is saved for this VM, especially in case of
    6739          * 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 be
    6771          * 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 the
    6782              * 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     }
    67956092
    67966093    return rc;
  • trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp

    r37486 r37491  
    11/* $Id$ */
    22/** @file
    3  * Implementation of IMachine in VBoxSVC.
     3 * Implementation of MachineCloneVM
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2011 Oracle Corporation
     7 * Copyright (C) 2011 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    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
    3520#include "VirtualBoxImpl.h"
    36 #include "MachineImpl.h"
    37 #include "ProgressImpl.h"
    38 #include "ProgressProxyImpl.h"
    39 #include "MediumAttachmentImpl.h"
    4021#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
    6423#include <iprt/path.h>
    6524#include <iprt/dir.h>
    66 #include <iprt/env.h>
    67 #include <iprt/lockvalidator.h>
    68 #include <iprt/process.h>
    6925#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
    7427#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
    9931/////////////////////////////////////////////////////////////////////////////
    10032
     33typedef struct
     34{
     35    ComPtr<IMedium>         pMedium;
     36    uint64_t                uSize;
     37}MEDIUMTASK;
     38
     39typedef struct
     40{
     41    RTCList<MEDIUMTASK>     chain;
     42    bool                    fCreateDiffs;
     43}MEDIUMTASKCHAIN;
     44
     45typedef struct
     46{
     47    Guid                    snapshotUuid;
     48    Utf8Str                 strSaveStateFile;
     49    uint64_t                cbSize;
     50}SAVESTATETASK;
     51
     52// The private class
    10153/////////////////////////////////////////////////////////////////////////////
    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
     55struct 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 */
    6077105    ComObjPtr<Machine>          pSrcMachine;
    6078106    ComObjPtr<Machine>          pTrgMachine;
     
    6082110    CloneMode_T                 mode;
    6083111    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 */
    6102114};
    6103115
    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
     116HRESULT MachineCloneVMPrivate::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const
    6312117{
    6313118    HRESULT rc = S_OK;
     
    6333138}
    6334139
    6335 settings::Snapshot Machine::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const
     140settings::Snapshot MachineCloneVMPrivate::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const
    6336141{
    6337142    settings::SnapshotsList::const_iterator it;
     
    6346151}
    6347152
    6348 void Machine::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const
     153void MachineCloneVMPrivate::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const
    6349154{
    6350155    settings::StorageControllersList::iterator it3;
     
    6368173}
    6369174
    6370 void Machine::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const
     175void MachineCloneVMPrivate::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const
    6371176{
    6372177    settings::SnapshotsList::iterator it;
     
    6381186}
    6382187
    6383 void Machine::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
     188void MachineCloneVMPrivate::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
    6384189{
    6385190    settings::SnapshotsList::iterator it;
     
    6394199
    6395200/* static */
    6396 int Machine::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser)
     201int MachineCloneVMPrivate::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser)
    6397202{
    6398203    ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);
     
    6410215}
    6411216
    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
     221MachineCloneVM::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
     226MachineCloneVM::~MachineCloneVM()
     227{
     228    delete d_ptr;
     229}
     230
     231HRESULT 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
     386HRESULT MachineCloneVM::run()
     387{
     388    DPTR(MachineCloneVM);
     389    ComObjPtr<Machine> &p = d->p;
     390
     391    AutoCaller autoCaller(p);
    6420392    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6421393
    6422     AutoReadLock  srcLock(this COMMA_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);
    6424396
    6425397    MultiResult rc = S_OK;
     
    6437409
    6438410    /* Where should all the media go? */
    6439     Utf8Str strTrgMachineFolder = pTask->pTrgMachine->getSettingsFileFull();
     411    Utf8Str strTrgMachineFolder = d->pTrgMachine->getSettingsFileFull();
    6440412    strTrgMachineFolder.stripFilename();
    6441413
     
    6447419         * configuration dataset. */
    6448420        settings::MachineConfigFile *pTrgMCF = new settings::MachineConfigFile(0);
    6449         *pTrgMCF = *mData->pMachineConfigFile;
     421        *pTrgMCF = *d->pSrcMachine->mData->pMachineConfigFile;
    6450422
    6451423        /* Reset media registry. */
     
    6454426         * with the stuff from the snapshot. */
    6455427        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)
    6460432        {
    6461433            if (!sn.uuid.isEmpty())
     
    6469441            pTrgMCF->uuidCurrentSnapshot.clear();
    6470442        }else
    6471         if (   pTask->mode == CloneMode_MachineAndChildStates
     443        if (   d->mode == CloneMode_MachineAndChildStates
    6472444            && !sn.uuid.isEmpty())
    6473445        {
     
    6490462        pTrgMCF->fCurrentStateModified = true;
    6491463        /* 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;
    6494466
    6495467        Bstr bstrSrcSnapshotFolder;
    6496         rc = pTask->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());
     468        rc = d->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());
    6497469        if (FAILED(rc)) throw rc;
    6498470        /* The absolute name of the snapshot folder. */
     
    6506478        typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair;
    6507479        TStrMediumMap map;
    6508         for (size_t i = 0; i < pTask->llMedias.size(); ++i)
    6509         {
    6510             const Machine::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);
    6511483            ComObjPtr<Medium> pNewParent;
    6512484            for (size_t a = mtc.chain.size(); a > 0; --a)
    6513485            {
    6514                 const Machine::CloneVMTask::MediumTask &mt = mtc.chain.at(a - 1);
     486                const MEDIUMTASK &mt = mtc.chain.at(a - 1);
    6515487                ComPtr<IMedium> pMedium = mt.pMedium;
    6516488
     
    6519491                if (FAILED(rc)) throw rc;
    6520492
    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);
    6522494                if (FAILED(rc)) throw rc;
    6523495
     
    6557529
    6558530                    Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw());
    6559                     rc = pTarget->init(mParent,
     531                    rc = pTarget->init(p->mParent,
    6560532                                       Utf8Str(bstrSrcFormat),
    6561533                                       strFile,
    6562                                        pTask->pTrgMachine->mData->mUuid,  /* media registry */
    6563                                        NULL                               /* llRegistriesThatNeedSaving */);
     534                                       d->pTrgMachine->mData->mUuid,  /* media registry */
     535                                       NULL                           /* llRegistriesThatNeedSaving */);
    6564536                    if (FAILED(rc)) throw rc;
    6565537
     
    6574546                    /* Wait until the asynchrony process has finished. */
    6575547                    srcLock.release();
    6576                     rc = pTask->pProgress->WaitForAsyncProgressCompletion(progress2);
     548                    rc = d->pProgress->WaitForAsyncProgressCompletion(progress2);
    6577549                    srcLock.acquire();
    6578550                    if (FAILED(rc)) throw rc;
     
    6587559                         * retrieve the error info from there, or it'll be lost. */
    6588560                        ProgressErrorInfo info(progress2);
    6589                         throw setError(iRc, Utf8Str(info.getText()).c_str());
     561                        throw p->setError(iRc, Utf8Str(info.getText()).c_str());
    6590562                    }
    6591563
     
    6609581                ComObjPtr<Medium> diff;
    6610582                diff.createObject();
    6611                 rc = diff->init(mParent,
     583                rc = diff->init(p->mParent,
    6612584                                pNewParent->getPreferredDiffFormat(),
    6613585                                strTrgSnapshotFolder,
    6614                                 pTask->pTrgMachine->mData->mUuid,
     586                                d->pTrgMachine->mData->mUuid,
    6615587                                NULL); // pllRegistriesThatNeedSaving
    6616588                if (FAILED(rc)) throw rc;
     
    6640612            /* We have to patch the configuration, so it contains the new
    6641613             * 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);
    6644616        }
    6645617        /* 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);
    6649621            const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str()));
    6650622
    6651623            /* 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);
    6653625            if (FAILED(rc)) throw rc;
    6654626            /* Copy the file only if it was not copied already. */
    6655627            if (!newFiles.contains(strTrgSaveState.c_str()))
    6656628            {
    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);
    6658630                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);
    6661633                newFiles.append(strTrgSaveState);
    6662634            }
     
    6666638                pTrgMCF->strStateFile = strTrgSaveState;
    6667639            else
    6668                 cloneUpdateStateFile(pTrgMCF->llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);
     640                d->cloneUpdateStateFile(pTrgMCF->llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);
    6669641        }
    6670642
    6671643        if (false)
    6672 //        if (!pTask->pOldMachineState.isNull())
     644//        if (!d->pOldMachineState.isNull())
    6673645        {
    6674646            SafeIfaceArray<IMediumAttachment> sfaAttachments;
    6675             rc = pTask->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
     647            rc = d->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
    6676648            if (FAILED(rc)) throw rc;
    6677649            for (size_t a = 0; a < sfaAttachments.size(); ++a)
     
    6722694
    6723695        {
    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);
    6725697            if (FAILED(rc)) throw rc;
    6726698            /* After modifying the new machine config, we can copy the stuff
    6727699             * over to the new machine. The machine have to be mutable for
    6728700             * 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);
    6733705            if (FAILED(rc)) throw rc;
    6734706        }
     
    6740712//        for (size_t i = 0; i < newBaseMedias.size(); ++i)
    6741713//        {
    6742 //            rc = newBaseMedias.at(i)->addRegistry(pTask->pTrgMachine->mData->mUuid, true /* fRecursive */);
     714//            rc = newBaseMedias.at(i)->addRegistry(d->pTrgMachine->mData->mUuid, true /* fRecursive */);
    6743715//            if (FAILED(rc)) throw rc;
    6744716//        }
    6745717
    6746718        /* Now save the new configuration to disk. */
    6747         rc = pTask->pTrgMachine->SaveSettings();
     719        rc = d->pTrgMachine->SaveSettings();
    6748720        if (FAILED(rc)) throw rc;
    6749721    }
     
    6766738            vrc = RTFileDelete(newFiles.at(i).c_str());
    6767739            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);
    6769741        }
    6770742        /* Delete all already created medias. (Reverse, cause there could be
     
    6787759                vrc = RTFileDelete(strLoc.c_str());
    6788760                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);
    6790762            }
    6791763        }
     
    6797769}
    6798770
    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 }
     771void MachineCloneVM::destroy()
     772{
     773    delete this;
     774}
     775
Note: See TracChangeset for help on using the changeset viewer.

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