VirtualBox

Changeset 47561 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Aug 6, 2013 3:18:17 PM (12 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
87761
Message:

Main/VirtualBox+Machine+Session: separate out the client death detection functionality into separate objects

Location:
trunk/src/VBox/Main
Files:
3 added
8 edited
3 copied

Legend:

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

    r47376 r47561  
    334334        src-server/BandwidthGroupImpl.cpp \
    335335        src-server/BIOSSettingsImpl.cpp \
     336        src-server/ClientWatcher.cpp \
     337        src-server/ClientToken.cpp \
    336338        src-server/DHCPServerImpl.cpp \
    337339        src-server/NetworkServiceRunner.cpp \
     
    634636        src-client/BusAssignmentManager.cpp \
    635637        $(if $(VBOX_WITH_PCI_PASSTHROUGH),src-client/PCIRawDevImpl.cpp,) \
     638        src-client/ClientTokenHolder.cpp \
    636639        src-client/ConsoleImpl.cpp \
    637640        src-client/ConsoleImpl2.cpp \
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r47485 r47561  
    33643364  <interface
    33653365    name="IInternalMachineControl" extends="$unknown"
    3366     uuid="dca36a92-703c-4649-98a4-f40c1ef0c336"
     3366    uuid="aca30121-e06b-4ace-bc22-d18506a8ba2b"
    33673367    internal="yes"
    33683368    wsmap="suppress"
     
    33863386      </desc>
    33873387      <param name="state" type="MachineState" dir="in"/>
    3388     </method>
    3389 
    3390     <method name="getIPCId">
    3391       <param name="id" type="wstring" dir="return"/>
    33923388    </method>
    33933389
     
    1733117327  <interface
    1733217328    name="IInternalSessionControl" extends="$unknown"
    17333     uuid="cddf451c-a006-4c33-8245-63b3c9ae6586"
     17329    uuid="3eeeb8e7-374f-4e0a-a5fd-f548742da8d4"
    1733417330    internal="yes"
    1733517331    wsmap="suppress"
     
    1737217368      <param name="machine" type="IMachine" dir="in"/>
    1737317369      <param name="lockType" type="LockType" dir="in"/>
     17370      <param name="tokenId" type="wstring" dir="in"/>
    1737417371    </method>
    1737517372
  • trunk/src/VBox/Main/include/MachineImpl.h

    r47409 r47561  
    783783    }
    784784
    785 #if defined(RT_OS_WINDOWS)
    786 
    787     bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    788                        ComPtr<IInternalSessionControl> *aControl = NULL,
    789                        HANDLE *aIPCSem = NULL, bool aAllowClosing = false);
    790     bool isSessionSpawning(RTPROCESS *aPID = NULL);
    791 
    792     bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine,
    793                                 ComPtr<IInternalSessionControl> *aControl = NULL,
    794                                 HANDLE *aIPCSem = NULL)
    795     { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); }
    796 
    797 #elif defined(RT_OS_OS2)
    798 
    799     bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    800                        ComPtr<IInternalSessionControl> *aControl = NULL,
    801                        HMTX *aIPCSem = NULL, bool aAllowClosing = false);
    802 
    803     bool isSessionSpawning(RTPROCESS *aPID = NULL);
    804 
    805     bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine,
    806                                  ComPtr<IInternalSessionControl> *aControl = NULL,
    807                                  HMTX *aIPCSem = NULL)
    808     { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); }
    809 
    810 #else
    811 
    812785    bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    813786                       ComPtr<IInternalSessionControl> *aControl = NULL,
     
    819792    { return isSessionOpen(aMachine, aControl, true /* aAllowClosing */); }
    820793
    821 #endif
    822 
    823794    bool checkForSpawnFailure();
    824795
     
    848819
    849820protected:
     821
     822    class ClientToken;
    850823
    851824    HRESULT checkStateDependency(StateDependency aDepType);
     
    10751048    STDMETHOD(SetRemoveSavedStateFile)(BOOL aRemove);
    10761049    STDMETHOD(UpdateState)(MachineState_T machineState);
    1077     STDMETHOD(GetIPCId)(BSTR *id);
    10781050    STDMETHOD(BeginPowerUp)(IProgress *aProgress);
    10791051    STDMETHOD(EndPowerUp)(LONG iResult);
     
    11341106    bool checkForDeath();
    11351107
     1108    void getTokenId(Utf8Str &strTokenId);
     1109    // getClientToken must be only used by callers who can guarantee that
     1110    // the object cannot be deleted in the mean time, i.e. have a caller/lock.
     1111    ClientToken *getClientToken();
     1112
    11361113    HRESULT onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter);
    11371114    HRESULT onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
     
    12361213    ConsoleTaskData mConsoleTaskData;
    12371214
    1238     /** interprocess semaphore handle for this machine */
    1239 #if defined(RT_OS_WINDOWS)
    1240     HANDLE mIPCSem;
    1241     Bstr mIPCSemName;
    1242     friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    1243                                        ComPtr<IInternalSessionControl> *aControl,
    1244                                        HANDLE *aIPCSem, bool aAllowClosing);
    1245 #elif defined(RT_OS_OS2)
    1246     HMTX mIPCSem;
    1247     Bstr mIPCSemName;
    1248     friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    1249                                        ComPtr<IInternalSessionControl> *aControl,
    1250                                        HMTX *aIPCSem, bool aAllowClosing);
    1251 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1252     int mIPCSem;
    1253 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    1254     Bstr mIPCKey;
    1255 # endif /*VBOX_WITH_NEW_SYS_V_KEYGEN */
    1256 #else
    1257 # error "Port me!"
    1258 #endif
     1215    /** client token for this machine */
     1216    ClientToken *mClientToken;
    12591217
    12601218    static DECLCALLBACK(int) taskHandler(RTTHREAD thread, void *pvUser);
  • trunk/src/VBox/Main/include/SessionImpl.h

    r46775 r47561  
    2424#ifdef RT_OS_WINDOWS
    2525# include "win/resource.h"
    26 #endif
    27 
    28 /** @def VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    29  *  Use SYS V IPC for watching a session.
    30  *  This is defined in the Makefile since it's also used by MachineImpl.h/cpp.
    31  */
    32 #ifdef DOXYGEN_RUNNING
    33 # define VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    3426#endif
    3527
     
    8173    STDMETHOD(GetPID)(ULONG *aPid);
    8274    STDMETHOD(GetRemoteConsole)(IConsole **aConsole);
    83     STDMETHOD(AssignMachine)(IMachine *aMachine, LockType_T aLockType);
     75    STDMETHOD(AssignMachine)(IMachine *aMachine, LockType_T aLockType, IN_BSTR aTokenId);
    8476    STDMETHOD(AssignRemoteMachine)(IMachine *aMachine, IConsole *aConsole);
    8577    STDMETHOD(UpdateMachineState)(MachineState_T aMachineState);
     
    124116
    125117    HRESULT unlockMachine(bool aFinalRelease, bool aFromServer);
    126     HRESULT grabIPCSemaphore();
    127     void releaseIPCSemaphore();
    128118
    129119    SessionState_T mState;
     
    139129    ComPtr<IVirtualBox> mVirtualBox;
    140130
    141     /* interprocess semaphore handle (id) for the opened machine */
    142 #if defined(RT_OS_WINDOWS)
    143     HANDLE mIPCSem;
    144     HANDLE mIPCThreadSem;
    145 #elif defined(RT_OS_OS2)
    146     RTTHREAD mIPCThread;
    147     RTSEMEVENT mIPCThreadSem;
    148 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    149     int mIPCSem;
    150 #else
    151 # error "Port me!"
    152 #endif
     131    class ClientTokenHolder;
     132
     133    ClientTokenHolder *mClientTokenHolder;
    153134};
    154135
  • trunk/src/VBox/Main/include/VirtualBoxImpl.h

    r47525 r47561  
    2020
    2121#include "VirtualBoxBase.h"
     22#include "objectslist.h"
    2223
    2324#ifdef RT_OS_WINDOWS
     
    3334class SessionMachine;
    3435class GuestOSType;
    35 class SharedFolder;
    3636class Progress;
    3737class Host;
     
    3939class DHCPServer;
    4040class PerformanceCollector;
    41 class VirtualBoxCallbackRegistration; /* see VirtualBoxImpl.cpp */
    4241#ifdef VBOX_WITH_EXTPACK
    4342class ExtPackManager;
     
    5251class SVCHlpClient;
    5352#endif
    54 
    55 struct VMClientWatcherData;
    5653
    5754namespace settings
     
    7168
    7269    typedef std::list<ComPtr<IInternalSessionControl> > InternalControlList;
     70    typedef ObjectsList<Machine> MachinesOList;
    7371
    7472    class CallbackEvent;
     
    216214    void onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName, IN_BSTR aValue,
    217215                               IN_BSTR aFlags);
    218     void onMachineUninit(Machine *aMachine);
    219216    void onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
    220217                                   NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
     
    234231    void getOpenedMachines(SessionMachinesList &aMachines,
    235232                           InternalControlList *aControls = NULL);
     233    MachinesOList &getMachinesList();
    236234
    237235    HRESULT findMachine(const Guid &aId,
     
    322320
    323321private:
     322    class ClientWatcher;
    324323
    325324    static HRESULT setErrorStatic(HRESULT aResultCode,
     
    361360    static Bstr sAPIVersion;
    362361
    363     static DECLCALLBACK(int) ClientWatcher(RTTHREAD thread, void *pvUser);
    364362    static DECLCALLBACK(int) AsyncEventHandler(RTTHREAD thread, void *pvUser);
    365363
  • trunk/src/VBox/Main/src-client/ClientTokenHolder.cpp

    r47555 r47561  
    1 /* $Id$ */
    21/** @file
    3  * VBox Client Session COM Class implementation in VBoxC.
     2 *
     3 * VirtualBox API client token holder (in the client process)
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2012 Oracle Corporation
     7 * Copyright (C) 2006-2013 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
     18#include <iprt/asm.h>
     19#include <iprt/assert.h>
     20#include <iprt/log.h>
     21#include <iprt/semaphore.h>
     22#include <iprt/process.h>
     23
    1824#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    19 #   include <errno.h>
    20 #   include <sys/types.h>
    21 #   include <sys/stat.h>
    22 #   include <sys/ipc.h>
    23 #   include <sys/sem.h>
    24 #endif
    25 
     25# include <errno.h>
     26# include <sys/types.h>
     27# include <sys/stat.h>
     28# include <sys/ipc.h>
     29# include <sys/sem.h>
     30#endif
     31
     32#include <VBox/com/defs.h>
     33
     34#include "ClientTokenHolder.h"
    2635#include "SessionImpl.h"
    27 #include "ConsoleImpl.h"
    28 #include "Global.h"
    29 
    30 #include "AutoCaller.h"
    31 #include "Logging.h"
    32 
    33 #include <VBox/err.h>
    34 #include <iprt/process.h>
    35 
    36 #if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)
    37 /** VM IPC mutex holder thread */
    38 static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser);
    39 #endif
    40 
    41 /**
    42  *  Local macro to check whether the session is open and return an error if not.
    43  *  @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this
    44  *  macro.
    45  */
    46 #define CHECK_OPEN() \
    47     do { \
    48         if (mState != SessionState_Locked) \
    49             return setError(E_UNEXPECTED, tr ("The session is not locked (session state: %s)"), Global::stringifySessionState(mState)); \
    50     } while (0)
    51 
    52 // constructor / destructor
    53 /////////////////////////////////////////////////////////////////////////////
    54 
    55 HRESULT Session::FinalConstruct()
    56 {
    57     LogFlowThisFunc(("\n"));
    58 
    59     HRESULT rc = init();
    60 
    61     BaseFinalConstruct();
    62 
    63     return rc;
    64 }
    65 
    66 void Session::FinalRelease()
    67 {
    68     LogFlowThisFunc(("\n"));
    69 
    70     uninit();
    71 
    72     BaseFinalRelease();
    73 }
    74 
    75 // public initializer/uninitializer for internal purposes only
    76 /////////////////////////////////////////////////////////////////////////////
    77 
    78 /**
    79  *  Initializes the Session object.
    80  */
    81 HRESULT Session::init()
    82 {
    83     /* Enclose the state transition NotReady->InInit->Ready */
    84     AutoInitSpan autoInitSpan(this);
    85     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    86 
    87     LogFlowThisFuncEnter();
    88 
    89     mState = SessionState_Unlocked;
    90     mType = SessionType_Null;
    91 
     36
     37
     38#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
     39/** client token holder thread */
     40static DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser);
     41#endif
     42
     43
     44Session::ClientTokenHolder::ClientTokenHolder()
     45{
     46    AssertReleaseFailed();
     47}
     48
     49Session::ClientTokenHolder::~ClientTokenHolder()
     50{
     51    /* release the client token */
    9252#if defined(RT_OS_WINDOWS)
    93     mIPCSem = NULL;
    94     mIPCThreadSem = NULL;
     53
     54    if (mSem && mThreadSem)
     55    {
     56        /*
     57         *  tell the thread holding the token to release it;
     58         *  it will close mSem handle
     59         */
     60        ::SetEvent(mSem);
     61        /* wait for the thread to finish */
     62        ::WaitForSingleObject(mThreadSem, INFINITE);
     63        ::CloseHandle(mThreadSem);
     64
     65        mThreadSem = NULL;
     66        mSem = NULL;
     67        mThread = NIL_RTTHREAD;
     68    }
     69
    9570#elif defined(RT_OS_OS2)
    96     mIPCThread = NIL_RTTHREAD;
    97     mIPCThreadSem = NIL_RTSEMEVENT;
     71
     72    if (mThread != NIL_RTTHREAD)
     73    {
     74        Assert(mSem != NIL_RTSEMEVENT);
     75
     76        /* tell the thread holding the token to release it */
     77        int vrc = RTSemEventSignal(mSem);
     78        AssertRC(vrc == NO_ERROR);
     79
     80        /* wait for the thread to finish */
     81        vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
     82        Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
     83
     84        mThread = NIL_RTTHREAD;
     85    }
     86
     87    if (mSem != NIL_RTSEMEVENT)
     88    {
     89        RTSemEventDestroy(mSem);
     90        mSem = NIL_RTSEMEVENT;
     91    }
     92
    9893#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    99     mIPCSem = -1;
     94
     95    if (mSem >= 0)
     96    {
     97        ::sembuf sop = { 0, 1, SEM_UNDO };
     98        ::semop(mSem, &sop, 1);
     99
     100        mSem = -1;
     101    }
     102
    100103#else
    101104# error "Port me!"
    102105#endif
    103 
    104     /* Confirm a successful initialization when it's the case */
    105     autoInitSpan.setSucceeded();
    106 
    107     LogFlowThisFuncLeave();
    108 
    109     return S_OK;
    110 }
    111 
    112 /**
    113  *  Uninitializes the Session object.
    114  *
    115  *  @note Locks this object for writing.
    116  */
    117 void Session::uninit()
    118 {
    119     LogFlowThisFuncEnter();
    120 
    121     /* Enclose the state transition Ready->InUninit->NotReady */
    122     AutoUninitSpan autoUninitSpan(this);
    123     if (autoUninitSpan.uninitDone())
    124     {
    125         LogFlowThisFunc(("Already uninitialized.\n"));
    126         LogFlowThisFuncLeave();
    127         return;
    128     }
    129 
    130     /* close() needs write lock */
    131     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    132 
    133     if (mState != SessionState_Unlocked)
    134     {
    135         Assert(mState == SessionState_Locked ||
    136                mState == SessionState_Spawning);
    137 
    138         HRESULT rc = unlockMachine(true /* aFinalRelease */, false /* aFromServer */);
    139         AssertComRC(rc);
    140     }
    141 
    142     LogFlowThisFuncLeave();
    143 }
    144 
    145 // ISession properties
    146 /////////////////////////////////////////////////////////////////////////////
    147 
    148 STDMETHODIMP Session::COMGETTER(State)(SessionState_T *aState)
    149 {
    150     CheckComArgOutPointerValid(aState);
    151 
    152     AutoCaller autoCaller(this);
    153     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    154 
    155     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    156 
    157     *aState = mState;
    158 
    159     return S_OK;
    160 }
    161 
    162 STDMETHODIMP Session::COMGETTER(Type)(SessionType_T *aType)
    163 {
    164     CheckComArgOutPointerValid(aType);
    165 
    166     AutoCaller autoCaller(this);
    167     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    168 
    169     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    170 
    171     CHECK_OPEN();
    172 
    173     *aType = mType;
    174     return S_OK;
    175 }
    176 
    177 STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine)
    178 {
    179     CheckComArgOutPointerValid(aMachine);
    180 
    181     AutoCaller autoCaller(this);
    182     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    183 
    184     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    185 
    186     CHECK_OPEN();
    187 
    188     HRESULT rc;
    189     if (mConsole)
    190        rc = mConsole->machine().queryInterfaceTo(aMachine);
     106}
     107
     108Session::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) :
     109    mClientTokenId(strTokenId)
     110{
     111    mSem = CTHSEMARG;
     112    mThread = NIL_RTTHREAD;
     113
     114#if defined(RT_OS_WINDOWS)
     115    mThreadSem = CTHTHREADSEMARG;
     116
     117    Bstr bstrTokenId(strTokenId);
     118
     119    /*
     120     * Since there is no guarantee that the constructor and destructor will be
     121     * called in the same thread, we need a separate thread to hold the token.
     122     */
     123
     124    mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
     125    AssertMsgReturnVoid(mThreadSem,
     126                        ("Cannot create an event sem, err=%d", ::GetLastError()));
     127
     128    void *data[3];
     129    data[0] = (void*)(BSTR)bstrTokenId.raw();
     130    data[1] = (void*)mThreadSem;
     131    data[2] = 0; /* will get an output from the thread */
     132
     133    /* create a thread to hold the token until signalled to release it */
     134    int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
     135    AssertRCReturnVoid(vrc);
     136
     137    /* wait until thread init is completed */
     138    DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE);
     139    AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
     140    Assert(data[2]);
     141
     142    if (wrc == WAIT_OBJECT_0 && data[2])
     143    {
     144        /* memorize the event sem we should signal in close() */
     145        mSem = (HANDLE)data[2];
     146    }
    191147    else
    192        rc = mRemoteMachine.queryInterfaceTo(aMachine);
    193     if (FAILED(rc))
    194     {
    195         /** @todo VBox 3.3: replace E_FAIL with rc here. */
    196         if (mConsole)
    197             setError(E_FAIL, tr("Failed to query the session machine (%Rhrc)"), rc);
    198         else if (FAILED_DEAD_INTERFACE(rc))
    199             setError(E_FAIL, tr("Peer process crashed"));
    200         else
    201             setError(E_FAIL, tr("Failed to query the remote session machine (%Rhrc)"), rc);
    202     }
    203 
    204     return rc;
    205 }
    206 
    207 STDMETHODIMP Session::COMGETTER(Console)(IConsole **aConsole)
    208 {
    209     CheckComArgOutPointerValid(aConsole);
    210 
    211     AutoCaller autoCaller(this);
    212     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    213 
    214     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    215 
    216     CHECK_OPEN();
    217 
    218     HRESULT rc;
    219     if (mConsole)
    220         rc = mConsole.queryInterfaceTo(aConsole);
    221     else
    222         rc = mRemoteConsole.queryInterfaceTo(aConsole);
    223 
    224     if (FAILED(rc))
    225     {
    226         /** @todo VBox 3.3: replace E_FAIL with rc here. */
    227         if (mConsole)
    228             setError(E_FAIL, tr("Failed to query the console (%Rhrc)"), rc);
    229         else if (FAILED_DEAD_INTERFACE(rc))
    230             setError(E_FAIL, tr("Peer process crashed"));
    231         else
    232             setError(E_FAIL, tr("Failed to query the remote console (%Rhrc)"), rc);
    233     }
    234 
    235     return rc;
    236 }
    237 
    238 // ISession methods
    239 /////////////////////////////////////////////////////////////////////////////
    240 
    241 STDMETHODIMP Session::UnlockMachine()
    242 {
    243     LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType));
    244 
    245     AutoCaller autoCaller(this);
    246     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    247 
    248     /* close() needs write lock */
    249     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    250 
    251     CHECK_OPEN();
    252 
    253     return unlockMachine(false /* aFinalRelease */, false /* aFromServer */);
    254 }
    255 
    256 // IInternalSessionControl methods
    257 /////////////////////////////////////////////////////////////////////////////
    258 
    259 STDMETHODIMP Session::GetPID(ULONG *aPid)
    260 {
    261     AssertReturn(aPid, E_POINTER);
    262 
    263     AutoCaller autoCaller(this);
    264     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    265 
    266     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    267 
    268     *aPid = (ULONG)RTProcSelf();
    269     AssertCompile(sizeof(*aPid) == sizeof(RTPROCESS));
    270 
    271     return S_OK;
    272 }
    273 
    274 STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole)
    275 {
    276     LogFlowThisFuncEnter();
    277     AssertReturn(aConsole, E_POINTER);
    278 
    279     AutoCaller autoCaller(this);
    280     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    281 
    282     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    283 
    284     AssertReturn(mState != SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
    285 
    286     AssertMsgReturn(mType == SessionType_WriteLock && !!mConsole,
    287                     ("This is not a direct session!\n"),
    288                     VBOX_E_INVALID_OBJECT_STATE);
    289 
    290     /* return a failure if the session already transitioned to Closing
    291      * but the server hasn't processed Machine::OnSessionEnd() yet. */
    292     if (mState != SessionState_Locked)
    293         return VBOX_E_INVALID_VM_STATE;
    294 
    295     mConsole.queryInterfaceTo(aConsole);
    296 
    297     LogFlowThisFuncLeave();
    298 
    299     return S_OK;
    300 }
    301 
    302 STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType)
    303 {
    304     LogFlowThisFuncEnter();
    305     LogFlowThisFunc(("aMachine=%p\n", aMachine));
    306 
    307     AutoCaller autoCaller(this);
    308     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    309 
    310     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    311 
    312     AssertReturn(mState == SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
    313 
    314     if (!aMachine)
    315     {
    316         /*
    317          *  A special case: the server informs us that this session has been
    318          *  passed to IMachine::launchVMProcess() so this session will become
    319          *  remote (but not existing) when AssignRemoteMachine() is called.
    320          */
    321 
    322         AssertReturn(mType == SessionType_Null, VBOX_E_INVALID_OBJECT_STATE);
    323         mType = SessionType_Remote;
    324         mState = SessionState_Spawning;
    325 
    326         LogFlowThisFuncLeave();
    327         return S_OK;
    328     }
    329 
    330     HRESULT rc = E_FAIL;
    331 
    332     /* query IInternalMachineControl interface */
    333     mControl = aMachine;
    334     AssertReturn(!!mControl, E_FAIL);
    335 
    336     rc = mConsole.createObject();
    337     AssertComRCReturn(rc, rc);
    338 
    339     rc = mConsole->init(aMachine, mControl, aLockType);
    340     AssertComRCReturn(rc, rc);
    341 
    342     rc = grabIPCSemaphore();
     148    {
     149        ::CloseHandle(mThreadSem);
     150        mThreadSem = NULL;
     151    }
     152#elif defined(RT_OS_OS2)
     153    Bstr bstrTokenId(strTokenId);
    343154
    344155    /*
    345      *  Reference the VirtualBox object to ensure the server is up
    346      *  until the session is closed
     156     * Since there is no guarantee that the constructor and destructor will be
     157     * called in the same thread, we need a separate thread to hold the token.
    347158     */
    348     if (SUCCEEDED(rc))
    349        rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
    350 
    351     if (SUCCEEDED(rc))
    352     {
    353         mType = SessionType_WriteLock;
    354         mState = SessionState_Locked;
    355     }
    356     else
    357     {
    358         /* some cleanup */
    359         mControl.setNull();
    360         if (!mConsole.isNull())
    361         {
    362             mConsole->uninit();
    363             mConsole.setNull();
    364         }
    365     }
    366 
    367     LogFlowThisFunc(("rc=%08X\n", rc));
    368     LogFlowThisFuncLeave();
    369 
    370     return rc;
    371 }
    372 
    373 STDMETHODIMP Session::AssignRemoteMachine(IMachine *aMachine, IConsole *aConsole)
    374 {
    375     LogFlowThisFuncEnter();
    376     LogFlowThisFunc(("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
    377 
    378     AssertReturn(aMachine && aConsole, E_INVALIDARG);
    379 
    380     AutoCaller autoCaller(this);
    381     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    382 
    383     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    384 
    385     AssertReturn(mState == SessionState_Unlocked ||
    386                   mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
    387 
    388     HRESULT rc = E_FAIL;
    389 
    390     /* query IInternalMachineControl interface */
    391     mControl = aMachine;
    392     AssertReturn(!!mControl, E_FAIL);
    393 
    394     /// @todo (dmik)
    395     //      currently, the remote session returns the same machine and
    396     //      console objects as the direct session, thus giving the
    397     //      (remote) client full control over the direct session. For the
    398     //      console, it is the desired behavior (the ability to control
    399     //      VM execution is a must for the remote session). What about
    400     //      the machine object, we may want to prevent the remote client
    401     //      from modifying machine data. In this case, we must:
    402     //      1)  assign the Machine object (instead of the SessionMachine
    403     //          object that is passed to this method) to mRemoteMachine;
    404     //      2)  remove GetMachine() property from the IConsole interface
    405     //          because it always returns the SessionMachine object
    406     //          (alternatively, we can supply a separate IConsole
    407     //          implementation that will return the Machine object in
    408     //          response to GetMachine()).
    409 
    410     mRemoteMachine = aMachine;
    411     mRemoteConsole = aConsole;
    412 
    413     /*
    414      *  Reference the VirtualBox object to ensure the server is up
    415      *  until the session is closed
    416      */
    417     rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
    418 
    419     if (SUCCEEDED(rc))
    420     {
    421         /*
    422          *  RemoteSession type can be already set by AssignMachine() when its
    423          *  argument is NULL (a special case)
    424          */
    425         if (mType != SessionType_Remote)
    426             mType = SessionType_Shared;
    427         else
    428             Assert(mState == SessionState_Spawning);
    429 
    430         mState = SessionState_Locked;
    431     }
    432     else
    433     {
    434         /* some cleanup */
    435         mControl.setNull();
    436         mRemoteMachine.setNull();
    437         mRemoteConsole.setNull();
    438     }
    439 
    440     LogFlowThisFunc(("rc=%08X\n", rc));
    441     LogFlowThisFuncLeave();
    442 
    443     return rc;
    444 }
    445 
    446 STDMETHODIMP Session::UpdateMachineState(MachineState_T aMachineState)
    447 {
    448     AutoCaller autoCaller(this);
    449 
    450     if (autoCaller.state() != Ready)
    451     {
    452         /*
    453          *  We might have already entered Session::uninit() at this point, so
    454          *  return silently (not interested in the state change during uninit)
    455          */
    456         LogFlowThisFunc(("Already uninitialized.\n"));
    457         return S_OK;
    458     }
    459 
    460     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    461 
    462     if (mState == SessionState_Unlocking)
    463     {
    464         LogFlowThisFunc(("Already being unlocked.\n"));
    465         return S_OK;
    466     }
    467 
    468     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    469     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    470 
    471     AssertReturn(!mControl.isNull(), E_FAIL);
    472     AssertReturn(!mConsole.isNull(), E_FAIL);
    473 
    474     return mConsole->updateMachineState(aMachineState);
    475 }
    476 
    477 STDMETHODIMP Session::Uninitialize()
    478 {
    479     LogFlowThisFuncEnter();
    480 
    481     AutoCaller autoCaller(this);
    482 
    483     HRESULT rc = S_OK;
    484 
    485     if (autoCaller.state() == Ready)
    486     {
    487         /* close() needs write lock */
    488         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    489 
    490         LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
    491 
    492         if (mState == SessionState_Unlocking)
    493         {
    494             LogFlowThisFunc(("Already being unlocked.\n"));
    495             return S_OK;
    496         }
    497 
    498 #ifndef DEBUG_andy /* Don't bug me -- now time to fix this at the moment. */
    499         AssertReturn(mState == SessionState_Locked ||
    500                      mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
    501 #endif
    502 
    503         /* close ourselves */
    504         rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
    505     }
    506     else if (autoCaller.state() == InUninit)
    507     {
    508         /*
    509          *  We might have already entered Session::uninit() at this point,
    510          *  return silently
    511          */
    512         LogFlowThisFunc(("Already uninitialized.\n"));
    513     }
    514     else
    515     {
    516         LogWarningThisFunc(("UNEXPECTED uninitialization!\n"));
    517         rc = autoCaller.rc();
    518     }
    519 
    520     LogFlowThisFunc(("rc=%08X\n", rc));
    521     LogFlowThisFuncLeave();
    522 
    523     return rc;
    524 }
    525 
    526 STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
    527 {
    528     LogFlowThisFunc(("\n"));
    529 
    530     AutoCaller autoCaller(this);
    531     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    532 
    533     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    534     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    535     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    536     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    537 
    538     return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
    539 }
    540 
    541 STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
    542 {
    543     LogFlowThisFunc(("\n"));
    544 
    545     AutoCaller autoCaller(this);
    546     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    547 
    548     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    549     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    550     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    551     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    552 
    553     return mConsole->onSerialPortChange(serialPort);
    554 }
    555 
    556 STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
    557 {
    558     LogFlowThisFunc(("\n"));
    559 
    560     AutoCaller autoCaller(this);
    561     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    562 
    563     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    564     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    565     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    566     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    567 
    568     return mConsole->onParallelPortChange(parallelPort);
    569 }
    570 
    571 STDMETHODIMP Session::OnStorageControllerChange()
    572 {
    573     LogFlowThisFunc(("\n"));
    574 
    575     AutoCaller autoCaller(this);
    576     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    577 
    578     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    579     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    580     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    581     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    582 
    583     return mConsole->onStorageControllerChange();
    584 }
    585 
    586 STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
    587 {
    588     LogFlowThisFunc(("\n"));
    589 
    590     AutoCaller autoCaller(this);
    591     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    592 
    593     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    594     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    595     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    596     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    597 
    598     return mConsole->onMediumChange(aMediumAttachment, aForce);
    599 }
    600 
    601 STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
    602 {
    603     LogFlowThisFunc(("\n"));
    604 
    605     AutoCaller autoCaller(this);
    606     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    607 
    608     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    609     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    610     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    611     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    612 
    613     return mConsole->onCPUChange(aCPU, aRemove);
    614 }
    615 
    616 STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
    617 {
    618     LogFlowThisFunc(("\n"));
    619 
    620     AutoCaller autoCaller(this);
    621     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    622 
    623     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    624     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    625     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    626     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    627 
    628     return mConsole->onCPUExecutionCapChange(aExecutionCap);
    629 }
    630 
    631 STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
    632 {
    633     LogFlowThisFunc(("\n"));
    634 
    635     AutoCaller autoCaller(this);
    636     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    637 
    638     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    639     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    640     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    641     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    642 
    643     return mConsole->onVRDEServerChange(aRestart);
    644 }
    645 
    646 STDMETHODIMP Session::OnVideoCaptureChange()
    647 {
    648     LogFlowThisFunc(("\n"));
    649 
    650     AutoCaller autoCaller(this);
    651     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    652 
    653     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    654     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    655     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    656     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    657 
    658     return mConsole->onVideoCaptureChange();
    659 }
    660 
    661 STDMETHODIMP Session::OnUSBControllerChange()
    662 {
    663     LogFlowThisFunc(("\n"));
    664 
    665     AutoCaller autoCaller(this);
    666     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    667 
    668     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    669     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    670     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    671     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    672 
    673     return mConsole->onUSBControllerChange();
    674 }
    675 
    676 STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
    677 {
    678     LogFlowThisFunc(("\n"));
    679 
    680     AutoCaller autoCaller(this);
    681     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    682 
    683     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    684     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    685     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    686     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    687 
    688     return mConsole->onSharedFolderChange(aGlobal);
    689 }
    690 
    691 STDMETHODIMP Session::OnClipboardModeChange(ClipboardMode_T aClipboardMode)
    692 {
    693     LogFlowThisFunc(("\n"));
    694 
    695     AutoCaller autoCaller(this);
    696     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    697 
    698     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    699     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    700     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    701     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    702 
    703     return mConsole->onClipboardModeChange(aClipboardMode);
    704 }
    705 
    706 STDMETHODIMP Session::OnDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
    707 {
    708     LogFlowThisFunc(("\n"));
    709 
    710     AutoCaller autoCaller(this);
    711     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    712 
    713     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    714     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    715     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    716 
    717     return mConsole->onDragAndDropModeChange(aDragAndDropMode);
    718 }
    719 
    720 STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
    721                                         IVirtualBoxErrorInfo *aError,
    722                                         ULONG aMaskedIfs)
    723 {
    724     LogFlowThisFunc(("\n"));
    725 
    726     AutoCaller autoCaller(this);
    727     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    728 
    729     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    730     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    731     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    732     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    733 
    734     return mConsole->onUSBDeviceAttach(aDevice, aError, aMaskedIfs);
    735 }
    736 
    737 STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
    738                                         IVirtualBoxErrorInfo *aError)
    739 {
    740     LogFlowThisFunc(("\n"));
    741 
    742     AutoCaller autoCaller(this);
    743     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    744 
    745     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    746     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    747     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    748     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    749 
    750     return mConsole->onUSBDeviceDetach(aId, aError);
    751 }
    752 
    753 STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
    754 {
    755     AutoCaller autoCaller(this);
    756     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    757 
    758     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    759 
    760     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    761     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    762 
    763     if (mState != SessionState_Locked)
    764     {
    765         /* the call from Machine issued when the session is open can arrive
    766          * after the session starts closing or gets closed. Note that when
    767          * aCheck is false, we return E_FAIL to indicate that aWinId we return
    768          * is not valid */
    769         *aCanShow = FALSE;
    770         *aWinId = 0;
    771         return aCheck ? S_OK : E_FAIL;
    772     }
    773 
    774     return mConsole->onShowWindow(aCheck, aCanShow, aWinId);
    775 }
    776 
    777 STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
    778 {
    779     LogFlowThisFunc(("\n"));
    780 
    781     AutoCaller autoCaller(this);
    782     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    783 
    784     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    785     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    786     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    787     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    788 
    789     return mConsole->onBandwidthGroupChange(aBandwidthGroup);
    790 }
    791 
    792 STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove, BOOL aSilent)
    793 {
    794     LogFlowThisFunc(("\n"));
    795 
    796     AutoCaller autoCaller(this);
    797     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    798 
    799     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    800     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    801     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    802     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    803 
    804     return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove, aSilent);
    805 }
    806 
    807 STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
    808                                           BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
    809 {
    810 #ifdef VBOX_WITH_GUEST_PROPS
    811     AutoCaller autoCaller(this);
    812     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    813 
    814     if (mState != SessionState_Locked)
    815         return setError(VBOX_E_INVALID_VM_STATE,
    816                         tr("Machine is not locked by session (session state: %s)."),
    817                         Global::stringifySessionState(mState));
    818     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    819     CheckComArgStrNotEmptyOrNull(aName);
    820     if (!aIsSetter && !VALID_PTR(aRetValue))
    821         return E_POINTER;
    822     if (!aIsSetter && !VALID_PTR(aRetTimestamp))
    823         return E_POINTER;
    824     if (!aIsSetter && !VALID_PTR(aRetFlags))
    825         return E_POINTER;
    826     /* aValue can be NULL for a setter call if the property is to be deleted. */
    827     if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
    828         return setError(E_INVALIDARG, tr("Invalid value pointer"));
    829     /* aFlags can be null if it is to be left as is */
    830     if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
    831         return setError(E_INVALIDARG, tr("Invalid flags pointer"));
    832 
    833     /* If this session is not in a VM process fend off the call. The caller
    834      * handles this correctly, by doing the operation in VBoxSVC. */
    835     if (!mConsole)
    836         return E_ACCESSDENIED;
    837 
    838     if (!aIsSetter)
    839         return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
    840     else
    841         return mConsole->setGuestProperty(aName, aValue, aFlags);
    842 #else /* VBOX_WITH_GUEST_PROPS not defined */
    843     ReturnComNotImplemented();
    844 #endif /* VBOX_WITH_GUEST_PROPS not defined */
    845 }
    846 
    847 STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
    848                                                ComSafeArrayOut(BSTR, aNames),
    849                                                ComSafeArrayOut(BSTR, aValues),
    850                                                ComSafeArrayOut(LONG64, aTimestamps),
    851                                                ComSafeArrayOut(BSTR, aFlags))
    852 {
    853 #ifdef VBOX_WITH_GUEST_PROPS
    854     AutoCaller autoCaller(this);
    855     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    856 
    857     if (mState != SessionState_Locked)
    858         return setError(VBOX_E_INVALID_VM_STATE,
    859                         tr("Machine is not locked by session (session state: %s)."),
    860                         Global::stringifySessionState(mState));
    861     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    862     if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
    863         return E_POINTER;
    864     if (ComSafeArrayOutIsNull(aNames))
    865         return E_POINTER;
    866     if (ComSafeArrayOutIsNull(aValues))
    867         return E_POINTER;
    868     if (ComSafeArrayOutIsNull(aTimestamps))
    869         return E_POINTER;
    870     if (ComSafeArrayOutIsNull(aFlags))
    871         return E_POINTER;
    872 
    873     /* If this session is not in a VM process fend off the call. The caller
    874      * handles this correctly, by doing the operation in VBoxSVC. */
    875     if (!mConsole)
    876         return E_ACCESSDENIED;
    877 
    878     return mConsole->enumerateGuestProperties(aPatterns,
    879                                               ComSafeArrayOutArg(aNames),
    880                                               ComSafeArrayOutArg(aValues),
    881                                               ComSafeArrayOutArg(aTimestamps),
    882                                               ComSafeArrayOutArg(aFlags));
    883 #else /* VBOX_WITH_GUEST_PROPS not defined */
    884     ReturnComNotImplemented();
    885 #endif /* VBOX_WITH_GUEST_PROPS not defined */
    886 }
    887 
    888 STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
    889                                         ULONG aSourceIdx, ULONG aTargetIdx,
    890                                         IMedium *aSource, IMedium *aTarget,
    891                                         BOOL aMergeForward,
    892                                         IMedium *aParentForTarget,
    893                                         ComSafeArrayIn(IMedium *, aChildrenToReparent),
    894                                         IProgress *aProgress)
    895 {
    896     AutoCaller autoCaller(this);
    897     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    898 
    899     if (mState != SessionState_Locked)
    900         return setError(VBOX_E_INVALID_VM_STATE,
    901                         tr("Machine is not locked by session (session state: %s)."),
    902                         Global::stringifySessionState(mState));
    903     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    904     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    905     CheckComArgNotNull(aMediumAttachment);
    906     CheckComArgSafeArrayNotNull(aChildrenToReparent);
    907 
    908     return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
    909                                        aTargetIdx, aSource, aTarget,
    910                                        aMergeForward, aParentForTarget,
    911                                        ComSafeArrayInArg(aChildrenToReparent),
    912                                        aProgress);
    913 }
    914 
    915 STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable)
    916 {
    917     AutoCaller autoCaller(this);
    918     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    919 
    920     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    921     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    922     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    923     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    924 
    925     mConsole->enableVMMStatistics(aEnable);
    926 
    927     return S_OK;
    928 }
    929 
    930 STDMETHODIMP Session::PauseWithReason(Reason_T aReason)
    931 {
    932     AutoCaller autoCaller(this);
    933     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    934 
    935     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    936     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    937     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    938     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    939 
    940     return mConsole->pause(aReason);
    941 }
    942 
    943 STDMETHODIMP Session::ResumeWithReason(Reason_T aReason)
    944 {
    945     AutoCaller autoCaller(this);
    946     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    947 
    948     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    949     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    950     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    951     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    952 
    953     return mConsole->resume(aReason);
    954 }
    955 
    956 STDMETHODIMP Session::SaveStateWithReason(Reason_T aReason, IProgress **aProgress)
    957 {
    958     AutoCaller autoCaller(this);
    959     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    960 
    961     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    962     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    963     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    964     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    965 
    966     return mConsole->saveState(aReason, aProgress);
    967 }
    968 
    969 // private methods
    970 ///////////////////////////////////////////////////////////////////////////////
    971 
    972 /**
    973  *  Unlocks a machine associated with the current session.
    974  *
    975  *  @param aFinalRelease    called as a result of FinalRelease()
    976  *  @param aFromServer      called as a result of Uninitialize()
    977  *
    978  *  @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize().
    979  *  @note Locks this object for writing.
    980  */
    981 HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
    982 {
    983     LogFlowThisFuncEnter();
    984     LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
    985                       aFinalRelease, aFromServer));
    986 
    987     AutoCaller autoCaller(this);
    988     AssertComRCReturnRC(autoCaller.rc());
    989 
    990     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    991 
    992     LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
    993 
    994     if (mState != SessionState_Locked)
    995     {
    996         Assert(mState == SessionState_Spawning);
    997 
    998         /* The session object is going to be uninitialized before it has been
    999          * assigned a direct console of the machine the client requested to open
    1000          * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
    1001          * only if this close request comes from the server (for example, it
    1002          * detected that the VM process it started terminated before opening a
    1003          * direct session). Otherwise, it means that the client is too fast and
    1004          * trying to close the session before waiting for the progress object it
    1005          * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
    1006         Assert(aFromServer);
    1007 
    1008         mState = SessionState_Unlocked;
    1009         mType = SessionType_Null;
    1010 #if defined(RT_OS_WINDOWS)
    1011         Assert(!mIPCSem && !mIPCThreadSem);
    1012 #elif defined(RT_OS_OS2)
    1013         Assert(mIPCThread == NIL_RTTHREAD &&
    1014                mIPCThreadSem == NIL_RTSEMEVENT);
     159
     160    int vrc = RTSemEventCreate(&mSem);
     161    AssertRCReturnVoid(vrc);
     162
     163    void *data[3];
     164    data[0] = (void*)bstrTokenId.raw();
     165    data[1] = (void*)mSem;
     166    data[2] = (void*)false; /* will get the thread result here */
     167
     168    /* create a thread to hold the token until signalled to release it */
     169    vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data,
     170                         0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
     171    AssertRCReturnVoid(vrc);
     172    /* wait until thread init is completed */
     173    vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
     174    AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
     175
     176    /* the thread must succeed */
     177    AssertReturnVoid((bool)data[2]);
     178
    1015179#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1016         Assert(mIPCSem == -1);
     180
     181# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
     182    key_t key = RTStrToUInt32(strTokenId.c_str());
     183    AssertMsgReturnVoid(key != 0,
     184                        ("Key value of 0 is not valid for client token"));
     185# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
     186    char *pszSemName = NULL;
     187    RTStrUtf8ToCurrentCP(&pszSemName, strTokenId);
     188    key_t key = ::ftok(pszSemName, 'V');
     189    RTStrFree(pszSemName);
     190# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
     191    int s = ::semget(key, 0, 0);
     192    AssertMsgReturnVoid(s >= 0,
     193                        ("Cannot open semaphore, errno=%d", errno));
     194
     195    /* grab the semaphore */
     196    ::sembuf sop = { 0,  -1, SEM_UNDO };
     197    int rv = ::semop(s, &sop, 1);
     198    AssertMsgReturnVoid(rv == 0,
     199                        ("Cannot grab semaphore, errno=%d", errno));
     200    mSem = s;
     201
    1017202#else
    1018203# error "Port me!"
    1019204#endif
    1020         LogFlowThisFuncLeave();
    1021         return S_OK;
    1022     }
    1023 
    1024     /* go to the closing state */
    1025     mState = SessionState_Unlocking;
    1026 
    1027     if (mType == SessionType_WriteLock)
    1028     {
    1029         if (!mConsole.isNull())
    1030         {
    1031             mConsole->uninit();
    1032             mConsole.setNull();
    1033         }
    1034     }
    1035     else
    1036     {
    1037         mRemoteMachine.setNull();
    1038         mRemoteConsole.setNull();
    1039     }
    1040 
    1041     ComPtr<IProgress> progress;
    1042 
    1043     if (!aFinalRelease && !aFromServer)
    1044     {
    1045         /*
    1046          *  We trigger OnSessionEnd() only when the session closes itself using
    1047          *  Close(). Note that if isFinalRelease = TRUE here, this means that
    1048          *  the client process has already initialized the termination procedure
    1049          *  without issuing Close() and the IPC channel is no more operational --
    1050          *  so we cannot call the server's method (it will definitely fail). The
    1051          *  server will instead simply detect the abnormal client death (since
    1052          *  OnSessionEnd() is not called) and reset the machine state to Aborted.
    1053          */
    1054 
    1055         /*
    1056          *  while waiting for OnSessionEnd() to complete one of our methods
    1057          *  can be called by the server (for example, Uninitialize(), if the
    1058          *  direct session has initiated a closure just a bit before us) so
    1059          *  we need to release the lock to avoid deadlocks. The state is already
    1060          *  SessionState_Closing here, so it's safe.
    1061          */
    1062         alock.release();
    1063 
    1064         LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
    1065         HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
    1066         LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
    1067 
    1068         alock.acquire();
    1069 
    1070         /*
    1071          *  If we get E_UNEXPECTED this means that the direct session has already
    1072          *  been closed, we're just too late with our notification and nothing more
    1073          *
    1074          *  bird: Seems E_ACCESSDENIED is what gets returned these days; see
    1075          *        VirtualBoxBase::addCaller.
    1076          */
    1077         if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
    1078             rc = S_OK;
    1079 
    1080 #ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */
    1081         AssertComRC(rc);
    1082 #endif
    1083     }
    1084 
    1085     mControl.setNull();
    1086 
    1087     if (mType == SessionType_WriteLock)
    1088     {
    1089         releaseIPCSemaphore();
    1090         if (!aFinalRelease && !aFromServer)
    1091         {
    1092             /*
    1093              *  Wait for the server to grab the semaphore and destroy the session
    1094              *  machine (allowing us to open a new session with the same machine
    1095              *  once this method returns)
    1096              */
    1097             Assert(!!progress);
    1098             if (progress)
    1099                 progress->WaitForCompletion(-1);
    1100         }
    1101     }
    1102 
    1103     mState = SessionState_Unlocked;
    1104     mType = SessionType_Null;
    1105 
    1106     /* release the VirtualBox instance as the very last step */
    1107     mVirtualBox.setNull();
    1108 
    1109     LogFlowThisFuncLeave();
    1110     return S_OK;
    1111 }
    1112 
    1113 /** @note To be called only from #AssignMachine() */
    1114 HRESULT Session::grabIPCSemaphore()
    1115 {
    1116     HRESULT rc = E_FAIL;
    1117 
    1118     /* open the IPC semaphore based on the sessionId and try to grab it */
    1119     Bstr ipcId;
    1120     rc = mControl->GetIPCId(ipcId.asOutParam());
    1121     AssertComRCReturnRC(rc);
    1122 
    1123     LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
    1124 
    1125 #if defined(RT_OS_WINDOWS)
    1126 
    1127     /*
    1128      *  Since Session is an MTA object, this method can be executed on
    1129      *  any thread, and this thread will not necessarily match the thread on
    1130      *  which close() will be called later. Therefore, we need a separate
    1131      *  thread to hold the IPC mutex and then release it in close().
    1132      */
    1133 
    1134     mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    1135     AssertMsgReturn(mIPCThreadSem,
    1136                     ("Cannot create an event sem, err=%d", ::GetLastError()),
    1137                     E_FAIL);
    1138 
    1139     void *data[3];
    1140     data[0] = (void*)(BSTR)ipcId.raw();
    1141     data[1] = (void*)mIPCThreadSem;
    1142     data[2] = 0; /* will get an output from the thread */
    1143 
    1144     /* create a thread to hold the IPC mutex until signalled to release it */
    1145     RTTHREAD tid;
    1146     int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
    1147     AssertRCReturn(vrc, E_FAIL);
    1148 
    1149     /* wait until thread init is completed */
    1150     DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
    1151     AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
    1152     Assert(data[2]);
    1153 
    1154     if (wrc == WAIT_OBJECT_0 && data[2])
    1155     {
    1156         /* memorize the event sem we should signal in close() */
    1157         mIPCSem = (HANDLE)data[2];
    1158         rc = S_OK;
    1159     }
    1160     else
    1161     {
    1162         ::CloseHandle(mIPCThreadSem);
    1163         mIPCThreadSem = NULL;
    1164         rc = E_FAIL;
    1165     }
    1166 
    1167 #elif defined(RT_OS_OS2)
    1168 
    1169     /* We use XPCOM where any message (including close()) can arrive on any
    1170      * worker thread (which will not necessarily match this thread that opens
    1171      * the mutex). Therefore, we need a separate thread to hold the IPC mutex
    1172      * and then release it in close(). */
    1173 
    1174     int vrc = RTSemEventCreate(&mIPCThreadSem);
    1175     AssertRCReturn(vrc, E_FAIL);
    1176 
    1177     void *data[3];
    1178     data[0] = (void*)ipcId.raw();
    1179     data[1] = (void*)mIPCThreadSem;
    1180     data[2] = (void*)false; /* will get the thread result here */
    1181 
    1182     /* create a thread to hold the IPC mutex until signalled to release it */
    1183     vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
    1184                          0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
    1185     AssertRCReturn(vrc, E_FAIL);
    1186 
    1187     /* wait until thread init is completed */
    1188     vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
    1189     AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
    1190 
    1191     /* the thread must succeed */
    1192     AssertReturn((bool)data[2], E_FAIL);
    1193 
    1194 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1195 
    1196 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    1197     Utf8Str ipcKey = ipcId;
    1198     key_t key = RTStrToUInt32(ipcKey.c_str());
    1199     AssertMsgReturn (key != 0,
    1200                     ("Key value of 0 is not valid for IPC semaphore"),
    1201                     E_FAIL);
    1202 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    1203     Utf8Str semName = ipcId;
    1204     char *pszSemName = NULL;
    1205     RTStrUtf8ToCurrentCP (&pszSemName, semName);
    1206     key_t key = ::ftok (pszSemName, 'V');
    1207     RTStrFree (pszSemName);
    1208 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    1209 
    1210     mIPCSem = ::semget (key, 0, 0);
    1211     AssertMsgReturn (mIPCSem >= 0,
    1212                     ("Cannot open IPC semaphore, errno=%d", errno),
    1213                     E_FAIL);
    1214 
    1215     /* grab the semaphore */
    1216     ::sembuf sop = { 0,  -1, SEM_UNDO };
    1217     int rv = ::semop (mIPCSem, &sop, 1);
    1218     AssertMsgReturn (rv == 0,
    1219                     ("Cannot grab IPC semaphore, errno=%d", errno),
    1220                     E_FAIL);
    1221 
    1222 #else
    1223 # error "Port me!"
    1224 #endif
    1225 
    1226     return rc;
    1227 }
    1228 
    1229 /** @note To be called only from #close() */
    1230 void Session::releaseIPCSemaphore()
    1231 {
    1232     /* release the IPC semaphore */
    1233 #if defined(RT_OS_WINDOWS)
    1234 
    1235     if (mIPCSem && mIPCThreadSem)
    1236     {
    1237         /*
    1238          *  tell the thread holding the IPC mutex to release it;
    1239          *  it will close mIPCSem handle
    1240          */
    1241         ::SetEvent (mIPCSem);
    1242         /* wait for the thread to finish */
    1243         ::WaitForSingleObject (mIPCThreadSem, INFINITE);
    1244         ::CloseHandle (mIPCThreadSem);
    1245 
    1246         mIPCThreadSem = NULL;
    1247         mIPCSem = NULL;
    1248     }
    1249 
    1250 #elif defined(RT_OS_OS2)
    1251 
    1252     if (mIPCThread != NIL_RTTHREAD)
    1253     {
    1254         Assert (mIPCThreadSem != NIL_RTSEMEVENT);
    1255 
    1256         /* tell the thread holding the IPC mutex to release it */
    1257         int vrc = RTSemEventSignal (mIPCThreadSem);
    1258         AssertRC(vrc == NO_ERROR);
    1259 
    1260         /* wait for the thread to finish */
    1261         vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
    1262         Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
    1263 
    1264         mIPCThread = NIL_RTTHREAD;
    1265     }
    1266 
    1267     if (mIPCThreadSem != NIL_RTSEMEVENT)
    1268     {
    1269         RTSemEventDestroy (mIPCThreadSem);
    1270         mIPCThreadSem = NIL_RTSEMEVENT;
    1271     }
    1272 
    1273 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1274 
    1275     if (mIPCSem >= 0)
    1276     {
    1277         ::sembuf sop = { 0, 1, SEM_UNDO };
    1278         ::semop (mIPCSem, &sop, 1);
    1279 
    1280         mIPCSem = -1;
    1281     }
    1282 
    1283 #else
    1284 # error "Port me!"
    1285 #endif
    1286 }
    1287 
    1288 #if defined(RT_OS_WINDOWS)
    1289 /** VM IPC mutex holder thread */
    1290 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
     205}
     206
     207bool Session::ClientTokenHolder::isReady()
     208{
     209    return mSem != CTHSEMARG;
     210}
     211
     212#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
     213/** client token holder thread */
     214DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser)
    1291215{
    1292216    LogFlowFuncEnter();
    1293217
    1294     Assert (pvUser);
    1295     void **data = (void **) pvUser;
    1296 
     218    Assert(pvUser);
     219
     220    void **data = (void **)pvUser;
     221
     222# if defined(RT_OS_WINDOWS)
    1297223    BSTR sessionId = (BSTR)data[0];
    1298224    HANDLE initDoneSem = (HANDLE)data[1];
    1299225
    1300     HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
    1301     AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
    1302 
    1303     if (ipcMutex)
    1304     {
    1305         /* grab the mutex */
    1306         DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
    1307         AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
     226    HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);
     227    AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError()));
     228
     229    if (mutex)
     230    {
     231        /* grab the token */
     232        DWORD wrc = ::WaitForSingleObject(mutex, 0);
     233        AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc));
    1308234        if (wrc == WAIT_OBJECT_0)
    1309235        {
    1310             HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
    1311             AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
     236            HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
     237            AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
    1312238            if (finishSem)
    1313239            {
    1314240                data[2] = (void*)finishSem;
    1315241                /* signal we're done with init */
    1316                 ::SetEvent (initDoneSem);
    1317                 /* wait until we're signaled to release the IPC mutex */
    1318                 ::WaitForSingleObject (finishSem, INFINITE);
    1319                 /* release the IPC mutex */
    1320                 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
    1321                 BOOL success = ::ReleaseMutex (ipcMutex);
    1322                 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
    1323                 ::CloseHandle (ipcMutex);
    1324                 ::CloseHandle (finishSem);
     242                ::SetEvent(initDoneSem);
     243                /* wait until we're signaled to release the token */
     244                ::WaitForSingleObject(finishSem, INFINITE);
     245                /* release the token */
     246                LogFlow(("ClientTokenHolderThread(): releasing token...\n"));
     247                BOOL success = ::ReleaseMutex(mutex);
     248                AssertMsg(success, ("cannot release token, err=%d\n", ::GetLastError()));
     249                ::CloseHandle(mutex);
     250                ::CloseHandle(finishSem);
    1325251            }
    1326252        }
     
    1328254
    1329255    /* signal we're done */
    1330     ::SetEvent (initDoneSem);
    1331 
    1332     LogFlowFuncLeave();
    1333 
    1334     return 0;
    1335 }
    1336 #endif
    1337 
    1338 #if defined(RT_OS_OS2)
    1339 /** VM IPC mutex holder thread */
    1340 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
    1341 {
    1342     LogFlowFuncEnter();
    1343 
    1344     Assert (pvUser);
    1345     void **data = (void **) pvUser;
    1346 
    1347     Utf8Str ipcId = (BSTR)data[0];
     256    ::SetEvent(initDoneSem);
     257# elif defined(RT_OS_OS2)
     258    Utf8Str sessionId = (BSTR)data[0];
    1348259    RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
    1349260
    1350     LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
    1351 
    1352     HMTX ipcMutex = NULLHANDLE;
    1353     APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
    1354     AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
     261    LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem));
     262
     263    HMTX mutex = NULLHANDLE;
     264    APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex);
     265    AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc));
    1355266
    1356267    if (arc == NO_ERROR)
    1357268    {
    1358         /* grab the mutex */
    1359         LogFlowFunc (("grabbing IPC mutex...\n"));
    1360         arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
    1361         AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
     269        /* grab the token */
     270        LogFlowFunc(("grabbing token...\n"));
     271        arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN);
     272        AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc));
    1362273        if (arc == NO_ERROR)
    1363274        {
     
    1365276            data[2] = (void*)true;
    1366277            /* signal we're done */
    1367             int vrc = RTThreadUserSignal (Thread);
     278            int vrc = RTThreadUserSignal(Thread);
    1368279            AssertRC(vrc);
    1369280
    1370             /* wait until we're signaled to release the IPC mutex */
    1371             LogFlowFunc (("waiting for termination signal..\n"));
    1372             vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
    1373             Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
    1374 
    1375             /* release the IPC mutex */
    1376             LogFlowFunc (("releasing IPC mutex...\n"));
    1377             arc = ::DosReleaseMutexSem (ipcMutex);
    1378             AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
     281            /* wait until we're signaled to release the token */
     282            LogFlowFunc(("waiting for termination signal..\n"));
     283            vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT);
     284            Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
     285
     286            /* release the token */
     287            LogFlowFunc(("releasing token...\n"));
     288            arc = ::DosReleaseMutexSem(mutex);
     289            AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc));
    1379290        }
    1380 
    1381         ::DosCloseMutexSem (ipcMutex);
     291        ::DosCloseMutexSem(mutex);
    1382292    }
    1383293
     
    1385295    data[1] = (void*)false;
    1386296    /* signal we're done */
    1387     int vrc = RTThreadUserSignal (Thread);
     297    int vrc = RTThreadUserSignal(Thread);
    1388298    AssertRC(vrc);
     299# else
     300#  error "Port me!"
     301# endif
    1389302
    1390303    LogFlowFuncLeave();
     
    1393306}
    1394307#endif
     308
    1395309/* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/src-client/SessionImpl.cpp

    r46775 r47561  
    55
    66/*
    7  * Copyright (C) 2006-2012 Oracle Corporation
     7 * Copyright (C) 2006-2013 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    18 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    19 #   include <errno.h>
    20 #   include <sys/types.h>
    21 #   include <sys/stat.h>
    22 #   include <sys/ipc.h>
    23 #   include <sys/sem.h>
    24 #endif
    25 
    2618#include "SessionImpl.h"
    2719#include "ConsoleImpl.h"
    2820#include "Global.h"
     21#include "ClientTokenHolder.h"
    2922
    3023#include "AutoCaller.h"
     
    3326#include <VBox/err.h>
    3427#include <iprt/process.h>
    35 
    36 #if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)
    37 /** VM IPC mutex holder thread */
    38 static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser);
    39 #endif
    4028
    4129/**
     
    9078    mType = SessionType_Null;
    9179
    92 #if defined(RT_OS_WINDOWS)
    93     mIPCSem = NULL;
    94     mIPCThreadSem = NULL;
    95 #elif defined(RT_OS_OS2)
    96     mIPCThread = NIL_RTTHREAD;
    97     mIPCThreadSem = NIL_RTSEMEVENT;
    98 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    99     mIPCSem = -1;
    100 #else
    101 # error "Port me!"
    102 #endif
     80    mClientTokenHolder = NULL;
    10381
    10482    /* Confirm a successful initialization when it's the case */
     
    300278}
    301279
    302 STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType)
     280STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType,
     281                                    IN_BSTR aTokenId)
    303282{
    304283    LogFlowThisFuncEnter();
     
    340319    AssertComRCReturn(rc, rc);
    341320
    342     rc = grabIPCSemaphore();
     321    Utf8Str strTokenId(aTokenId);
     322    Assert(!strTokenId.isEmpty());
     323    /* create the machine client token */
     324    try
     325    {
     326        mClientTokenHolder = new ClientTokenHolder(strTokenId);
     327        if (!mClientTokenHolder->isReady())
     328        {
     329            delete mClientTokenHolder;
     330            mClientTokenHolder = NULL;
     331            rc = E_FAIL;
     332        }
     333    }
     334    catch (std::bad_alloc &)
     335    {
     336        rc = E_OUTOFMEMORY;
     337    }
    343338
    344339    /*
     
    10081003        mState = SessionState_Unlocked;
    10091004        mType = SessionType_Null;
    1010 #if defined(RT_OS_WINDOWS)
    1011         Assert(!mIPCSem && !mIPCThreadSem);
    1012 #elif defined(RT_OS_OS2)
    1013         Assert(mIPCThread == NIL_RTTHREAD &&
    1014                mIPCThreadSem == NIL_RTSEMEVENT);
    1015 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1016         Assert(mIPCSem == -1);
    1017 #else
    1018 # error "Port me!"
    1019 #endif
     1005
     1006        Assert(!mClientTokenHolder);
     1007
    10201008        LogFlowThisFuncLeave();
    10211009        return S_OK;
     
    10871075    if (mType == SessionType_WriteLock)
    10881076    {
    1089         releaseIPCSemaphore();
     1077        if (mClientTokenHolder)
     1078        {
     1079            delete mClientTokenHolder;
     1080            mClientTokenHolder = NULL;
     1081        }
     1082
    10901083        if (!aFinalRelease && !aFromServer)
    10911084        {
     
    11111104}
    11121105
    1113 /** @note To be called only from #AssignMachine() */
    1114 HRESULT Session::grabIPCSemaphore()
    1115 {
    1116     HRESULT rc = E_FAIL;
    1117 
    1118     /* open the IPC semaphore based on the sessionId and try to grab it */
    1119     Bstr ipcId;
    1120     rc = mControl->GetIPCId(ipcId.asOutParam());
    1121     AssertComRCReturnRC(rc);
    1122 
    1123     LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
    1124 
    1125 #if defined(RT_OS_WINDOWS)
    1126 
    1127     /*
    1128      *  Since Session is an MTA object, this method can be executed on
    1129      *  any thread, and this thread will not necessarily match the thread on
    1130      *  which close() will be called later. Therefore, we need a separate
    1131      *  thread to hold the IPC mutex and then release it in close().
    1132      */
    1133 
    1134     mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    1135     AssertMsgReturn(mIPCThreadSem,
    1136                     ("Cannot create an event sem, err=%d", ::GetLastError()),
    1137                     E_FAIL);
    1138 
    1139     void *data[3];
    1140     data[0] = (void*)(BSTR)ipcId.raw();
    1141     data[1] = (void*)mIPCThreadSem;
    1142     data[2] = 0; /* will get an output from the thread */
    1143 
    1144     /* create a thread to hold the IPC mutex until signalled to release it */
    1145     RTTHREAD tid;
    1146     int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
    1147     AssertRCReturn(vrc, E_FAIL);
    1148 
    1149     /* wait until thread init is completed */
    1150     DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
    1151     AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
    1152     Assert(data[2]);
    1153 
    1154     if (wrc == WAIT_OBJECT_0 && data[2])
    1155     {
    1156         /* memorize the event sem we should signal in close() */
    1157         mIPCSem = (HANDLE)data[2];
    1158         rc = S_OK;
    1159     }
    1160     else
    1161     {
    1162         ::CloseHandle(mIPCThreadSem);
    1163         mIPCThreadSem = NULL;
    1164         rc = E_FAIL;
    1165     }
    1166 
    1167 #elif defined(RT_OS_OS2)
    1168 
    1169     /* We use XPCOM where any message (including close()) can arrive on any
    1170      * worker thread (which will not necessarily match this thread that opens
    1171      * the mutex). Therefore, we need a separate thread to hold the IPC mutex
    1172      * and then release it in close(). */
    1173 
    1174     int vrc = RTSemEventCreate(&mIPCThreadSem);
    1175     AssertRCReturn(vrc, E_FAIL);
    1176 
    1177     void *data[3];
    1178     data[0] = (void*)ipcId.raw();
    1179     data[1] = (void*)mIPCThreadSem;
    1180     data[2] = (void*)false; /* will get the thread result here */
    1181 
    1182     /* create a thread to hold the IPC mutex until signalled to release it */
    1183     vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
    1184                          0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
    1185     AssertRCReturn(vrc, E_FAIL);
    1186 
    1187     /* wait until thread init is completed */
    1188     vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
    1189     AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
    1190 
    1191     /* the thread must succeed */
    1192     AssertReturn((bool)data[2], E_FAIL);
    1193 
    1194 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1195 
    1196 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    1197     Utf8Str ipcKey = ipcId;
    1198     key_t key = RTStrToUInt32(ipcKey.c_str());
    1199     AssertMsgReturn (key != 0,
    1200                     ("Key value of 0 is not valid for IPC semaphore"),
    1201                     E_FAIL);
    1202 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    1203     Utf8Str semName = ipcId;
    1204     char *pszSemName = NULL;
    1205     RTStrUtf8ToCurrentCP (&pszSemName, semName);
    1206     key_t key = ::ftok (pszSemName, 'V');
    1207     RTStrFree (pszSemName);
    1208 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    1209 
    1210     mIPCSem = ::semget (key, 0, 0);
    1211     AssertMsgReturn (mIPCSem >= 0,
    1212                     ("Cannot open IPC semaphore, errno=%d", errno),
    1213                     E_FAIL);
    1214 
    1215     /* grab the semaphore */
    1216     ::sembuf sop = { 0,  -1, SEM_UNDO };
    1217     int rv = ::semop (mIPCSem, &sop, 1);
    1218     AssertMsgReturn (rv == 0,
    1219                     ("Cannot grab IPC semaphore, errno=%d", errno),
    1220                     E_FAIL);
    1221 
    1222 #else
    1223 # error "Port me!"
    1224 #endif
    1225 
    1226     return rc;
    1227 }
    1228 
    1229 /** @note To be called only from #close() */
    1230 void Session::releaseIPCSemaphore()
    1231 {
    1232     /* release the IPC semaphore */
    1233 #if defined(RT_OS_WINDOWS)
    1234 
    1235     if (mIPCSem && mIPCThreadSem)
    1236     {
    1237         /*
    1238          *  tell the thread holding the IPC mutex to release it;
    1239          *  it will close mIPCSem handle
    1240          */
    1241         ::SetEvent (mIPCSem);
    1242         /* wait for the thread to finish */
    1243         ::WaitForSingleObject (mIPCThreadSem, INFINITE);
    1244         ::CloseHandle (mIPCThreadSem);
    1245 
    1246         mIPCThreadSem = NULL;
    1247         mIPCSem = NULL;
    1248     }
    1249 
    1250 #elif defined(RT_OS_OS2)
    1251 
    1252     if (mIPCThread != NIL_RTTHREAD)
    1253     {
    1254         Assert (mIPCThreadSem != NIL_RTSEMEVENT);
    1255 
    1256         /* tell the thread holding the IPC mutex to release it */
    1257         int vrc = RTSemEventSignal (mIPCThreadSem);
    1258         AssertRC(vrc == NO_ERROR);
    1259 
    1260         /* wait for the thread to finish */
    1261         vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
    1262         Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
    1263 
    1264         mIPCThread = NIL_RTTHREAD;
    1265     }
    1266 
    1267     if (mIPCThreadSem != NIL_RTSEMEVENT)
    1268     {
    1269         RTSemEventDestroy (mIPCThreadSem);
    1270         mIPCThreadSem = NIL_RTSEMEVENT;
    1271     }
    1272 
    1273 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1274 
    1275     if (mIPCSem >= 0)
    1276     {
    1277         ::sembuf sop = { 0, 1, SEM_UNDO };
    1278         ::semop (mIPCSem, &sop, 1);
    1279 
    1280         mIPCSem = -1;
    1281     }
    1282 
    1283 #else
    1284 # error "Port me!"
    1285 #endif
    1286 }
    1287 
    1288 #if defined(RT_OS_WINDOWS)
    1289 /** VM IPC mutex holder thread */
    1290 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
    1291 {
    1292     LogFlowFuncEnter();
    1293 
    1294     Assert (pvUser);
    1295     void **data = (void **) pvUser;
    1296 
    1297     BSTR sessionId = (BSTR)data[0];
    1298     HANDLE initDoneSem = (HANDLE)data[1];
    1299 
    1300     HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
    1301     AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
    1302 
    1303     if (ipcMutex)
    1304     {
    1305         /* grab the mutex */
    1306         DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
    1307         AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
    1308         if (wrc == WAIT_OBJECT_0)
    1309         {
    1310             HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
    1311             AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
    1312             if (finishSem)
    1313             {
    1314                 data[2] = (void*)finishSem;
    1315                 /* signal we're done with init */
    1316                 ::SetEvent (initDoneSem);
    1317                 /* wait until we're signaled to release the IPC mutex */
    1318                 ::WaitForSingleObject (finishSem, INFINITE);
    1319                 /* release the IPC mutex */
    1320                 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
    1321                 BOOL success = ::ReleaseMutex (ipcMutex);
    1322                 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
    1323                 ::CloseHandle (ipcMutex);
    1324                 ::CloseHandle (finishSem);
    1325             }
    1326         }
    1327     }
    1328 
    1329     /* signal we're done */
    1330     ::SetEvent (initDoneSem);
    1331 
    1332     LogFlowFuncLeave();
    1333 
    1334     return 0;
    1335 }
    1336 #endif
    1337 
    1338 #if defined(RT_OS_OS2)
    1339 /** VM IPC mutex holder thread */
    1340 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
    1341 {
    1342     LogFlowFuncEnter();
    1343 
    1344     Assert (pvUser);
    1345     void **data = (void **) pvUser;
    1346 
    1347     Utf8Str ipcId = (BSTR)data[0];
    1348     RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
    1349 
    1350     LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
    1351 
    1352     HMTX ipcMutex = NULLHANDLE;
    1353     APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
    1354     AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
    1355 
    1356     if (arc == NO_ERROR)
    1357     {
    1358         /* grab the mutex */
    1359         LogFlowFunc (("grabbing IPC mutex...\n"));
    1360         arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
    1361         AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
    1362         if (arc == NO_ERROR)
    1363         {
    1364             /* store the answer */
    1365             data[2] = (void*)true;
    1366             /* signal we're done */
    1367             int vrc = RTThreadUserSignal (Thread);
    1368             AssertRC(vrc);
    1369 
    1370             /* wait until we're signaled to release the IPC mutex */
    1371             LogFlowFunc (("waiting for termination signal..\n"));
    1372             vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
    1373             Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
    1374 
    1375             /* release the IPC mutex */
    1376             LogFlowFunc (("releasing IPC mutex...\n"));
    1377             arc = ::DosReleaseMutexSem (ipcMutex);
    1378             AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
    1379         }
    1380 
    1381         ::DosCloseMutexSem (ipcMutex);
    1382     }
    1383 
    1384     /* store the answer */
    1385     data[1] = (void*)false;
    1386     /* signal we're done */
    1387     int vrc = RTThreadUserSignal (Thread);
    1388     AssertRC(vrc);
    1389 
    1390     LogFlowFuncLeave();
    1391 
    1392     return 0;
    1393 }
    1394 #endif
    13951106/* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/src-server/ClientToken.cpp

    r47555 r47561  
    1 /* $Id$ */
    21/** @file
    3  * Implementation of IMachine in VBoxSVC.
     2 *
     3 * VirtualBox API client crash token handling
    44 */
    55
     
    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
     18#include <iprt/asm.h>
     19#include <iprt/assert.h>
     20#include <iprt/log.h>
     21#include <iprt/semaphore.h>
     22#include <iprt/process.h>
    2523
    2624#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
     
    3230#endif
    3331
    34 #include "Logging.h"
    35 #include "VirtualBoxImpl.h"
     32#include <VBox/com/defs.h>
     33
     34#include <vector>
     35
     36#include "VirtualBoxBase.h"
     37#include "AutoCaller.h"
     38#include "ClientToken.h"
    3639#include "MachineImpl.h"
    37 #include "ProgressImpl.h"
    38 #include "ProgressProxyImpl.h"
    39 #include "MediumAttachmentImpl.h"
    40 #include "MediumImpl.h"
    41 #include "MediumLock.h"
    42 #include "USBControllerImpl.h"
    43 #include "USBDeviceFiltersImpl.h"
    44 #include "HostImpl.h"
    45 #include "SharedFolderImpl.h"
    46 #include "GuestOSTypeImpl.h"
    47 #include "VirtualBoxErrorInfoImpl.h"
    48 #include "GuestImpl.h"
    49 #include "StorageControllerImpl.h"
    50 #include "DisplayImpl.h"
    51 #include "DisplayUtils.h"
    52 #include "MachineImplCloneVM.h"
    53 #include "AutostartDb.h"
    54 #include "SystemPropertiesImpl.h"
    55 
    56 // generated header
    57 #include "VBoxEvents.h"
    58 
    59 #ifdef VBOX_WITH_USB
    60 # include "USBProxyService.h"
    61 #endif
    62 
    63 #include "AutoCaller.h"
    64 #include "HashedPw.h"
    65 #include "Performance.h"
    66 
    67 #include <iprt/asm.h>
    68 #include <iprt/path.h>
    69 #include <iprt/dir.h>
    70 #include <iprt/env.h>
    71 #include <iprt/lockvalidator.h>
    72 #include <iprt/process.h>
    73 #include <iprt/cpp/utils.h>
    74 #include <iprt/cpp/xml.h>               /* xml::XmlFileWriter::s_psz*Suff. */
    75 #include <iprt/sha.h>
    76 #include <iprt/string.h>
    77 #include <iprt/base64.h>
    78 
    79 #include <VBox/com/array.h>
    80 #include <VBox/com/list.h>
    81 
    82 #include <VBox/err.h>
    83 #include <VBox/param.h>
    84 #include <VBox/settings.h>
    85 #include <VBox/vmm/ssm.h>
    86 
    87 #ifdef VBOX_WITH_GUEST_PROPS
    88 # include <VBox/HostServices/GuestPropertySvc.h>
    89 # include <VBox/com/array.h>
    90 #endif
    91 
    92 #include "VBox/com/MultiResult.h"
    93 
    94 #include <algorithm>
    95 
    96 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    97 # define HOSTSUFF_EXE ".exe"
    98 #else /* !RT_OS_WINDOWS */
    99 # define HOSTSUFF_EXE ""
    100 #endif /* !RT_OS_WINDOWS */
    101 
    102 // defines / prototypes
    103 /////////////////////////////////////////////////////////////////////////////
    104 
    105 /////////////////////////////////////////////////////////////////////////////
    106 // Machine::Data structure
    107 /////////////////////////////////////////////////////////////////////////////
    108 
    109 Machine::Data::Data()
    110 {
    111     mRegistered                = FALSE;
    112     pMachineConfigFile         = NULL;
    113     /* Contains hints on what has changed when the user is using the VM (config
    114      * changes, running the VM, ...). This is used to decide if a config needs
    115      * to be written to disk. */
    116     flModifications            = 0;
    117     /* VM modification usually also trigger setting the current state to
    118      * "Modified". Although this is not always the case. An e.g. is the VM
    119      * initialization phase or when snapshot related data is changed. The
    120      * actually behavior is controlled by the following flag. */
    121     m_fAllowStateModification  = false;
    122     mAccessible                = FALSE;
    123     /* mUuid is initialized in Machine::init() */
    124 
    125     mMachineState              = MachineState_PoweredOff;
    126     RTTimeNow(&mLastStateChange);
    127 
    128     mMachineStateDeps          = 0;
    129     mMachineStateDepsSem       = NIL_RTSEMEVENTMULTI;
    130     mMachineStateChangePending = 0;
    131 
    132     mCurrentStateModified      = TRUE;
    133     mGuestPropertiesModified   = FALSE;
    134 
    135     mSession.mPID              = NIL_RTPROCESS;
    136     mSession.mState            = SessionState_Unlocked;
    137 }
    138 
    139 Machine::Data::~Data()
    140 {
    141     if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
    142     {
    143         RTSemEventMultiDestroy(mMachineStateDepsSem);
    144         mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
    145     }
    146     if (pMachineConfigFile)
    147     {
    148         delete pMachineConfigFile;
    149         pMachineConfigFile = NULL;
    150     }
    151 }
    152 
    153 /////////////////////////////////////////////////////////////////////////////
    154 // Machine::HWData structure
    155 /////////////////////////////////////////////////////////////////////////////
    156 
    157 Machine::HWData::HWData()
    158 {
    159     /* default values for a newly created machine */
    160     mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
    161     mMemorySize = 128;
    162     mCPUCount = 1;
    163     mCPUHotPlugEnabled = false;
    164     mMemoryBalloonSize = 0;
    165     mPageFusionEnabled = false;
    166     mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
    167     mVRAMSize = 8;
    168     mAccelerate3DEnabled = false;
    169     mAccelerate2DVideoEnabled = false;
    170     mMonitorCount = 1;
    171     mVideoCaptureWidth = 1024;
    172     mVideoCaptureHeight = 768;
    173     mVideoCaptureRate = 512;
    174     mVideoCaptureFPS = 25;
    175     mVideoCaptureEnabled = false;
    176     for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
    177         maVideoCaptureScreens[i] = true;
    178 
    179     mHWVirtExEnabled = true;
    180     mHWVirtExNestedPagingEnabled = true;
    181 #if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
    182     mHWVirtExLargePagesEnabled = true;
    183 #else
    184     /* Not supported on 32 bits hosts. */
    185     mHWVirtExLargePagesEnabled = false;
    186 #endif
    187     mHWVirtExVPIDEnabled = true;
    188     mHWVirtExUXEnabled = true;
    189     mHWVirtExForceEnabled = false;
    190 #if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
    191     mHWVirtExExclusive = false;
    192 #else
    193     mHWVirtExExclusive = true;
    194 #endif
    195 #if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
    196     mPAEEnabled = true;
    197 #else
    198     mPAEEnabled = false;
    199 #endif
    200     mLongMode =  HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
    201     mSyntheticCpu = false;
    202     mHPETEnabled = false;
    203 
    204     /* default boot order: floppy - DVD - HDD */
    205     mBootOrder[0] = DeviceType_Floppy;
    206     mBootOrder[1] = DeviceType_DVD;
    207     mBootOrder[2] = DeviceType_HardDisk;
    208     for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
    209         mBootOrder[i] = DeviceType_Null;
    210 
    211     mClipboardMode = ClipboardMode_Disabled;
    212     mDragAndDropMode = DragAndDropMode_Disabled;
    213     mGuestPropertyNotificationPatterns = "";
    214 
    215     mFirmwareType = FirmwareType_BIOS;
    216     mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
    217     mPointingHIDType = PointingHIDType_PS2Mouse;
    218     mChipsetType = ChipsetType_PIIX3;
    219     mEmulatedUSBWebcamEnabled = FALSE;
    220     mEmulatedUSBCardReaderEnabled = FALSE;
    221 
    222     for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
    223         mCPUAttached[i] = false;
    224 
    225     mIOCacheEnabled = true;
    226     mIOCacheSize    = 5; /* 5MB */
    227 
    228     /* Maximum CPU execution cap by default. */
    229     mCpuExecutionCap = 100;
    230 }
    231 
    232 Machine::HWData::~HWData()
    233 {
    234 }
    235 
    236 /////////////////////////////////////////////////////////////////////////////
    237 // Machine::HDData structure
    238 /////////////////////////////////////////////////////////////////////////////
    239 
    240 Machine::MediaData::MediaData()
    241 {
    242 }
    243 
    244 Machine::MediaData::~MediaData()
    245 {
    246 }
    247 
    248 /////////////////////////////////////////////////////////////////////////////
    249 // Machine class
    250 /////////////////////////////////////////////////////////////////////////////
    251 
    252 // constructor / destructor
    253 /////////////////////////////////////////////////////////////////////////////
    254 
    255 Machine::Machine() :
    256 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    257     mCollectorGuest(NULL),
    258 #endif
    259     mPeer(NULL),
    260     mParent(NULL),
    261     mSerialPorts(),
    262     mParallelPorts(),
    263     uRegistryNeedsSaving(0)
    264 {}
    265 
    266 Machine::~Machine()
    267 {}
    268 
    269 HRESULT Machine::FinalConstruct()
    270 {
    271     LogFlowThisFunc(("\n"));
    272     return BaseFinalConstruct();
    273 }
    274 
    275 void Machine::FinalRelease()
    276 {
    277     LogFlowThisFunc(("\n"));
    278     uninit();
    279     BaseFinalRelease();
    280 }
    281 
    282 /**
    283  *  Initializes a new machine instance; this init() variant creates a new, empty machine.
    284  *  This gets called from VirtualBox::CreateMachine().
    285  *
    286  *  @param aParent      Associated parent object
    287  *  @param strConfigFile  Local file system path to the VM settings file (can
    288  *                      be relative to the VirtualBox config directory).
    289  *  @param strName      name for the machine
    290  *  @param llGroups     list of groups for the machine
    291  *  @param aOsType      OS Type of this machine or NULL.
    292  *  @param aId          UUID for the new machine.
    293  *  @param fForceOverwrite Whether to overwrite an existing machine settings file.
    294  *
    295  *  @return  Success indicator. if not S_OK, the machine object is invalid
    296  */
    297 HRESULT Machine::init(VirtualBox *aParent,
    298                       const Utf8Str &strConfigFile,
    299                       const Utf8Str &strName,
    300                       const StringsList &llGroups,
    301                       GuestOSType *aOsType,
    302                       const Guid &aId,
    303                       bool fForceOverwrite,
    304                       bool fDirectoryIncludesUUID)
    305 {
    306     LogFlowThisFuncEnter();
    307     LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
    308 
    309     /* Enclose the state transition NotReady->InInit->Ready */
    310     AutoInitSpan autoInitSpan(this);
    311     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    312 
    313     HRESULT rc = initImpl(aParent, strConfigFile);
    314     if (FAILED(rc)) return rc;
    315 
    316     rc = tryCreateMachineConfigFile(fForceOverwrite);
    317     if (FAILED(rc)) return rc;
    318 
    319     if (SUCCEEDED(rc))
    320     {
    321         // create an empty machine config
    322         mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
    323 
    324         rc = initDataAndChildObjects();
    325     }
    326 
    327     if (SUCCEEDED(rc))
    328     {
    329         // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
    330         mData->mAccessible = TRUE;
    331 
    332         unconst(mData->mUuid) = aId;
    333 
    334         mUserData->s.strName = strName;
    335 
    336         mUserData->s.llGroups = llGroups;
    337 
    338         mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
    339         // the "name sync" flag determines whether the machine directory gets renamed along
    340         // with the machine file; say so if the settings file name is the same as the
    341         // settings file parent directory (machine directory)
    342         mUserData->s.fNameSync = isInOwnDir();
    343 
    344         // initialize the default snapshots folder
    345         rc = COMSETTER(SnapshotFolder)(NULL);
    346         AssertComRC(rc);
    347 
    348         if (aOsType)
    349         {
    350             /* Store OS type */
    351             mUserData->s.strOsType = aOsType->id();
    352 
    353             /* Apply BIOS defaults */
    354             mBIOSSettings->applyDefaults(aOsType);
    355 
    356             /* Apply network adapters defaults */
    357             for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
    358                 mNetworkAdapters[slot]->applyDefaults(aOsType);
    359 
    360             /* Apply serial port defaults */
    361             for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
    362                 mSerialPorts[slot]->applyDefaults(aOsType);
    363 
    364             /* Let the OS type select 64-bit ness. */
    365             mHWData->mLongMode = aOsType->is64Bit()
    366                                ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
    367         }
    368 
    369         /* At this point the changing of the current state modification
    370          * flag is allowed. */
    371         allowStateModification();
    372 
    373         /* commit all changes made during the initialization */
    374         commit();
    375     }
    376 
    377     /* Confirm a successful initialization when it's the case */
    378     if (SUCCEEDED(rc))
    379     {
    380         if (mData->mAccessible)
    381             autoInitSpan.setSucceeded();
    382         else
    383             autoInitSpan.setLimited();
    384     }
    385 
    386     LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
    387                      !!mUserData ? mUserData->s.strName.c_str() : "NULL",
    388                      mData->mRegistered,
    389                      mData->mAccessible,
    390                      rc));
    391 
    392     LogFlowThisFuncLeave();
    393 
    394     return rc;
    395 }
    396 
    397 /**
    398  *  Initializes a new instance with data from machine XML (formerly Init_Registered).
    399  *  Gets called in two modes:
    400  *
    401  *      -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
    402  *         UUID is specified and we mark the machine as "registered";
    403  *
    404  *      -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
    405  *         and the machine remains unregistered until RegisterMachine() is called.
    406  *
    407  *  @param aParent      Associated parent object
    408  *  @param aConfigFile  Local file system path to the VM settings file (can
    409  *                      be relative to the VirtualBox config directory).
    410  *  @param aId          UUID of the machine or NULL (see above).
    411  *
    412  *  @return  Success indicator. if not S_OK, the machine object is invalid
    413  */
    414 HRESULT Machine::initFromSettings(VirtualBox *aParent,
    415                                   const Utf8Str &strConfigFile,
    416                                   const Guid *aId)
    417 {
    418     LogFlowThisFuncEnter();
    419     LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
    420 
    421     /* Enclose the state transition NotReady->InInit->Ready */
    422     AutoInitSpan autoInitSpan(this);
    423     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    424 
    425     HRESULT rc = initImpl(aParent, strConfigFile);
    426     if (FAILED(rc)) return rc;
    427 
    428     if (aId)
    429     {
    430         // loading a registered VM:
    431         unconst(mData->mUuid) = *aId;
    432         mData->mRegistered = TRUE;
    433         // now load the settings from XML:
    434         rc = registeredInit();
    435             // this calls initDataAndChildObjects() and loadSettings()
    436     }
    437     else
    438     {
    439         // opening an unregistered VM (VirtualBox::OpenMachine()):
    440         rc = initDataAndChildObjects();
    441 
    442         if (SUCCEEDED(rc))
    443         {
    444             // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
    445             mData->mAccessible = TRUE;
    446 
    447             try
    448             {
    449                 // load and parse machine XML; this will throw on XML or logic errors
    450                 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
    451 
    452                 // reject VM UUID duplicates, they can happen if someone
    453                 // tries to register an already known VM config again
    454                 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
    455                                          true /* fPermitInaccessible */,
    456                                          false /* aDoSetError */,
    457                                          NULL) != VBOX_E_OBJECT_NOT_FOUND)
    458                 {
    459                     throw setError(E_FAIL,
    460                                    tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
    461                                    mData->m_strConfigFile.c_str());
    462                 }
    463 
    464                 // use UUID from machine config
    465                 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
    466 
    467                 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
    468                                                  NULL /* puuidRegistry */);
    469                 if (FAILED(rc)) throw rc;
    470 
    471                 /* At this point the changing of the current state modification
    472                  * flag is allowed. */
    473                 allowStateModification();
    474 
    475                 commit();
    476             }
    477             catch (HRESULT err)
    478             {
    479                 /* we assume that error info is set by the thrower */
    480                 rc = err;
    481             }
    482             catch (...)
    483             {
    484                 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    485             }
    486         }
    487     }
    488 
    489     /* Confirm a successful initialization when it's the case */
    490     if (SUCCEEDED(rc))
    491     {
    492         if (mData->mAccessible)
    493             autoInitSpan.setSucceeded();
    494         else
    495         {
    496             autoInitSpan.setLimited();
    497 
    498             // uninit media from this machine's media registry, or else
    499             // reloading the settings will fail
    500             mParent->unregisterMachineMedia(getId());
    501         }
    502     }
    503 
    504     LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
    505                       "rc=%08X\n",
    506                       !!mUserData ? mUserData->s.strName.c_str() : "NULL",
    507                       mData->mRegistered, mData->mAccessible, rc));
    508 
    509     LogFlowThisFuncLeave();
    510 
    511     return rc;
    512 }
    513 
    514 /**
    515  *  Initializes a new instance from a machine config that is already in memory
    516  *  (import OVF case). Since we are importing, the UUID in the machine
    517  *  config is ignored and we always generate a fresh one.
    518  *
    519  *  @param strName  Name for the new machine; this overrides what is specified in config and is used
    520  *                  for the settings file as well.
    521  *  @param config   Machine configuration loaded and parsed from XML.
    522  *
    523  *  @return  Success indicator. if not S_OK, the machine object is invalid
    524  */
    525 HRESULT Machine::init(VirtualBox *aParent,
    526                       const Utf8Str &strName,
    527                       const settings::MachineConfigFile &config)
    528 {
    529     LogFlowThisFuncEnter();
    530 
    531     /* Enclose the state transition NotReady->InInit->Ready */
    532     AutoInitSpan autoInitSpan(this);
    533     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    534 
    535     Utf8Str strConfigFile;
    536     aParent->getDefaultMachineFolder(strConfigFile);
    537     strConfigFile.append(RTPATH_DELIMITER);
    538     strConfigFile.append(strName);
    539     strConfigFile.append(RTPATH_DELIMITER);
    540     strConfigFile.append(strName);
    541     strConfigFile.append(".vbox");
    542 
    543     HRESULT rc = initImpl(aParent, strConfigFile);
    544     if (FAILED(rc)) return rc;
    545 
    546     rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
    547     if (FAILED(rc)) return rc;
    548 
    549     rc = initDataAndChildObjects();
    550 
    551     if (SUCCEEDED(rc))
    552     {
    553         // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
    554         mData->mAccessible = TRUE;
    555 
    556         // create empty machine config for instance data
    557         mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
    558 
    559         // generate fresh UUID, ignore machine config
    560         unconst(mData->mUuid).create();
    561 
    562         rc = loadMachineDataFromSettings(config,
    563                                          &mData->mUuid); // puuidRegistry: initialize media with this registry ID
    564 
    565         // override VM name as well, it may be different
    566         mUserData->s.strName = strName;
    567 
    568         if (SUCCEEDED(rc))
    569         {
    570             /* At this point the changing of the current state modification
    571              * flag is allowed. */
    572             allowStateModification();
    573 
    574             /* commit all changes made during the initialization */
    575             commit();
    576         }
    577     }
    578 
    579     /* Confirm a successful initialization when it's the case */
    580     if (SUCCEEDED(rc))
    581     {
    582         if (mData->mAccessible)
    583             autoInitSpan.setSucceeded();
    584         else
    585         {
    586             autoInitSpan.setLimited();
    587 
    588             // uninit media from this machine's media registry, or else
    589             // reloading the settings will fail
    590             mParent->unregisterMachineMedia(getId());
    591         }
    592     }
    593 
    594     LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
    595                      "rc=%08X\n",
    596                       !!mUserData ? mUserData->s.strName.c_str() : "NULL",
    597                       mData->mRegistered, mData->mAccessible, rc));
    598 
    599     LogFlowThisFuncLeave();
    600 
    601     return rc;
    602 }
    603 
    604 /**
    605  * Shared code between the various init() implementations.
    606  * @param aParent
    607  * @return
    608  */
    609 HRESULT Machine::initImpl(VirtualBox *aParent,
    610                           const Utf8Str &strConfigFile)
    611 {
    612     LogFlowThisFuncEnter();
    613 
    614     AssertReturn(aParent, E_INVALIDARG);
    615     AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
    616 
    617     HRESULT rc = S_OK;
    618 
    619     /* share the parent weakly */
    620     unconst(mParent) = aParent;
    621 
    622     /* allocate the essential machine data structure (the rest will be
    623      * allocated later by initDataAndChildObjects() */
    624     mData.allocate();
    625 
    626     /* memorize the config file name (as provided) */
    627     mData->m_strConfigFile = strConfigFile;
    628 
    629     /* get the full file name */
    630     int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
    631     if (RT_FAILURE(vrc1))
    632         return setError(VBOX_E_FILE_ERROR,
    633                         tr("Invalid machine settings file name '%s' (%Rrc)"),
    634                         strConfigFile.c_str(),
    635                         vrc1);
    636 
    637     LogFlowThisFuncLeave();
    638 
    639     return rc;
    640 }
    641 
    642 /**
    643  * Tries to create a machine settings file in the path stored in the machine
    644  * instance data. Used when a new machine is created to fail gracefully if
    645  * the settings file could not be written (e.g. because machine dir is read-only).
    646  * @return
    647  */
    648 HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
    649 {
    650     HRESULT rc = S_OK;
    651 
    652     // when we create a new machine, we must be able to create the settings file
    653     RTFILE f = NIL_RTFILE;
    654     int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    655     if (    RT_SUCCESS(vrc)
    656          || vrc == VERR_SHARING_VIOLATION
    657        )
    658     {
    659         if (RT_SUCCESS(vrc))
    660             RTFileClose(f);
    661         if (!fForceOverwrite)
    662             rc = setError(VBOX_E_FILE_ERROR,
    663                           tr("Machine settings file '%s' already exists"),
    664                           mData->m_strConfigFileFull.c_str());
    665         else
    666         {
    667             /* try to delete the config file, as otherwise the creation
    668              * of a new settings file will fail. */
    669             int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
    670             if (RT_FAILURE(vrc2))
    671                 rc = setError(VBOX_E_FILE_ERROR,
    672                               tr("Could not delete the existing settings file '%s' (%Rrc)"),
    673                               mData->m_strConfigFileFull.c_str(), vrc2);
    674         }
    675     }
    676     else if (    vrc != VERR_FILE_NOT_FOUND
    677               && vrc != VERR_PATH_NOT_FOUND
    678             )
    679         rc = setError(VBOX_E_FILE_ERROR,
    680                       tr("Invalid machine settings file name '%s' (%Rrc)"),
    681                       mData->m_strConfigFileFull.c_str(),
    682                       vrc);
    683     return rc;
    684 }
    685 
    686 /**
    687  *  Initializes the registered machine by loading the settings file.
    688  *  This method is separated from #init() in order to make it possible to
    689  *  retry the operation after VirtualBox startup instead of refusing to
    690  *  startup the whole VirtualBox server in case if the settings file of some
    691  *  registered VM is invalid or inaccessible.
    692  *
    693  *  @note Must be always called from this object's write lock
    694  *        (unless called from #init() that doesn't need any locking).
    695  *  @note Locks the mUSBController method for writing.
    696  *  @note Subclasses must not call this method.
    697  */
    698 HRESULT Machine::registeredInit()
    699 {
    700     AssertReturn(!isSessionMachine(), E_FAIL);
    701     AssertReturn(!isSnapshotMachine(), E_FAIL);
    702     AssertReturn(mData->mUuid.isValid(), E_FAIL);
    703     AssertReturn(!mData->mAccessible, E_FAIL);
    704 
    705     HRESULT rc = initDataAndChildObjects();
    706 
    707     if (SUCCEEDED(rc))
    708     {
    709         /* Temporarily reset the registered flag in order to let setters
    710          * potentially called from loadSettings() succeed (isMutable() used in
    711          * all setters will return FALSE for a Machine instance if mRegistered
    712          * is TRUE). */
    713         mData->mRegistered = FALSE;
    714 
    715         try
    716         {
    717             // load and parse machine XML; this will throw on XML or logic errors
    718             mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
    719 
    720             if (mData->mUuid != mData->pMachineConfigFile->uuid)
    721                 throw setError(E_FAIL,
    722                                tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
    723                                mData->pMachineConfigFile->uuid.raw(),
    724                                mData->m_strConfigFileFull.c_str(),
    725                                mData->mUuid.toString().c_str(),
    726                                mParent->settingsFilePath().c_str());
    727 
    728             rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
    729                                              NULL /* const Guid *puuidRegistry */);
    730             if (FAILED(rc)) throw rc;
    731         }
    732         catch (HRESULT err)
    733         {
    734             /* we assume that error info is set by the thrower */
    735             rc = err;
    736         }
    737         catch (...)
    738         {
    739             rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    740         }
    741 
    742         /* Restore the registered flag (even on failure) */
    743         mData->mRegistered = TRUE;
    744     }
    745 
    746     if (SUCCEEDED(rc))
    747     {
    748         /* Set mAccessible to TRUE only if we successfully locked and loaded
    749          * the settings file */
    750         mData->mAccessible = TRUE;
    751 
    752         /* commit all changes made during loading the settings file */
    753         commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
    754         /// @todo r=klaus for some reason the settings loading logic backs up
    755         // the settings, and therefore a commit is needed. Should probably be changed.
    756     }
    757     else
    758     {
    759         /* If the machine is registered, then, instead of returning a
    760          * failure, we mark it as inaccessible and set the result to
    761          * success to give it a try later */
    762 
    763         /* fetch the current error info */
    764         mData->mAccessError = com::ErrorInfo();
    765         LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
    766                     mData->mUuid.raw(),
    767                     mData->mAccessError.getText().raw()));
    768 
    769         /* rollback all changes */
    770         rollback(false /* aNotify */);
    771 
    772         // uninit media from this machine's media registry, or else
    773         // reloading the settings will fail
    774         mParent->unregisterMachineMedia(getId());
    775 
    776         /* uninitialize the common part to make sure all data is reset to
    777          * default (null) values */
    778         uninitDataAndChildObjects();
    779 
    780         rc = S_OK;
    781     }
    782 
    783     return rc;
    784 }
    785 
    786 /**
    787  *  Uninitializes the instance.
    788  *  Called either from FinalRelease() or by the parent when it gets destroyed.
    789  *
    790  *  @note The caller of this method must make sure that this object
    791  *  a) doesn't have active callers on the current thread and b) is not locked
    792  *  by the current thread; otherwise uninit() will hang either a) due to
    793  *  AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
    794  *  a dead-lock caused by this thread waiting for all callers on the other
    795  *  threads are done but preventing them from doing so by holding a lock.
    796  */
    797 void Machine::uninit()
    798 {
    799     LogFlowThisFuncEnter();
    800 
    801     Assert(!isWriteLockOnCurrentThread());
    802 
    803     Assert(!uRegistryNeedsSaving);
    804     if (uRegistryNeedsSaving)
    805     {
    806         AutoCaller autoCaller(this);
    807         if (SUCCEEDED(autoCaller.rc()))
    808         {
    809             AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    810             saveSettings(NULL, Machine::SaveS_Force);
    811         }
    812     }
    813 
    814     /* Enclose the state transition Ready->InUninit->NotReady */
    815     AutoUninitSpan autoUninitSpan(this);
    816     if (autoUninitSpan.uninitDone())
    817         return;
    818 
    819     Assert(!isSnapshotMachine());
    820     Assert(!isSessionMachine());
    821     Assert(!!mData);
    822 
    823     LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
    824     LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
    825 
    826     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    827 
    828     if (!mData->mSession.mMachine.isNull())
    829     {
    830         /* Theoretically, this can only happen if the VirtualBox server has been
    831          * terminated while there were clients running that owned open direct
    832          * sessions. Since in this case we are definitely called by
    833          * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
    834          * won't happen on the client watcher thread (because it does
    835          * VirtualBox::addCaller() for the duration of the
    836          * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
    837          * cannot happen until the VirtualBox caller is released). This is
    838          * important, because SessionMachine::uninit() cannot correctly operate
    839          * after we return from this method (it expects the Machine instance is
    840          * still valid). We'll call it ourselves below.
    841          */
    842         LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
    843                             (SessionMachine*)mData->mSession.mMachine));
    844 
    845         if (Global::IsOnlineOrTransient(mData->mMachineState))
    846         {
    847             LogWarningThisFunc(("Setting state to Aborted!\n"));
    848             /* set machine state using SessionMachine reimplementation */
    849             static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
    850         }
    851 
    852         /*
    853          *  Uninitialize SessionMachine using public uninit() to indicate
    854          *  an unexpected uninitialization.
    855          */
    856         mData->mSession.mMachine->uninit();
    857         /* SessionMachine::uninit() must set mSession.mMachine to null */
    858         Assert(mData->mSession.mMachine.isNull());
    859     }
    860 
    861     // uninit media from this machine's media registry, if they're still there
    862     Guid uuidMachine(getId());
    863 
    864     /* the lock is no more necessary (SessionMachine is uninitialized) */
    865     alock.release();
    866 
    867     /* XXX This will fail with
    868      *   "cannot be closed because it is still attached to 1 virtual machines"
    869      * because at this point we did not call uninitDataAndChildObjects() yet
    870      * and therefore also removeBackReference() for all these mediums was not called! */
    871 
    872     if (uuidMachine.isValid() && !uuidMachine.isZero())     // can be empty if we're called from a failure of Machine::init
    873         mParent->unregisterMachineMedia(uuidMachine);
    874 
    875     // has machine been modified?
    876     if (mData->flModifications)
    877     {
    878         LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
    879         rollback(false /* aNotify */);
    880     }
    881 
    882     if (mData->mAccessible)
    883         uninitDataAndChildObjects();
    884 
    885     /* free the essential data structure last */
    886     mData.free();
    887 
    888     LogFlowThisFuncLeave();
    889 }
    890 
    891 // IMachine properties
    892 /////////////////////////////////////////////////////////////////////////////
    893 
    894 STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
    895 {
    896     CheckComArgOutPointerValid(aParent);
    897 
    898     AutoLimitedCaller autoCaller(this);
    899     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    900 
    901     /* mParent is constant during life time, no need to lock */
    902     ComObjPtr<VirtualBox> pVirtualBox(mParent);
    903     pVirtualBox.queryInterfaceTo(aParent);
    904 
    905     return S_OK;
    906 }
    907 
    908 STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
    909 {
    910     CheckComArgOutPointerValid(aAccessible);
    911 
    912     AutoLimitedCaller autoCaller(this);
    913     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    914 
    915     LogFlowThisFunc(("ENTER\n"));
    916 
    917     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    918 
    919     HRESULT rc = S_OK;
    920 
    921     if (!mData->mAccessible)
    922     {
    923         /* try to initialize the VM once more if not accessible */
    924 
    925         AutoReinitSpan autoReinitSpan(this);
    926         AssertReturn(autoReinitSpan.isOk(), E_FAIL);
    927 
    928 #ifdef DEBUG
    929         LogFlowThisFunc(("Dumping media backreferences\n"));
    930         mParent->dumpAllBackRefs();
    931 #endif
    932 
    933         if (mData->pMachineConfigFile)
    934         {
    935             // reset the XML file to force loadSettings() (called from registeredInit())
    936             // to parse it again; the file might have changed
    937             delete mData->pMachineConfigFile;
    938             mData->pMachineConfigFile = NULL;
    939         }
    940 
    941         rc = registeredInit();
    942 
    943         if (SUCCEEDED(rc) && mData->mAccessible)
    944         {
    945             autoReinitSpan.setSucceeded();
    946 
    947             /* make sure interesting parties will notice the accessibility
    948              * state change */
    949             mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
    950             mParent->onMachineDataChange(mData->mUuid);
    951         }
    952     }
    953 
    954     if (SUCCEEDED(rc))
    955         *aAccessible = mData->mAccessible;
    956 
    957     LogFlowThisFuncLeave();
    958 
    959     return rc;
    960 }
    961 
    962 STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
    963 {
    964     CheckComArgOutPointerValid(aAccessError);
    965 
    966     AutoLimitedCaller autoCaller(this);
    967     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    968 
    969     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    970 
    971     if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
    972     {
    973         /* return shortly */
    974         aAccessError = NULL;
    975         return S_OK;
    976     }
    977 
    978     HRESULT rc = S_OK;
    979 
    980     ComObjPtr<VirtualBoxErrorInfo> errorInfo;
    981     rc = errorInfo.createObject();
    982     if (SUCCEEDED(rc))
    983     {
    984         errorInfo->init(mData->mAccessError.getResultCode(),
    985                         mData->mAccessError.getInterfaceID().ref(),
    986                         Utf8Str(mData->mAccessError.getComponent()).c_str(),
    987                         Utf8Str(mData->mAccessError.getText()));
    988         rc = errorInfo.queryInterfaceTo(aAccessError);
    989     }
    990 
    991     return rc;
    992 }
    993 
    994 STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
    995 {
    996     CheckComArgOutPointerValid(aName);
    997 
    998     AutoCaller autoCaller(this);
    999     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1000 
    1001     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1002 
    1003     mUserData->s.strName.cloneTo(aName);
    1004 
    1005     return S_OK;
    1006 }
    1007 
    1008 STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
    1009 {
    1010     CheckComArgStrNotEmptyOrNull(aName);
    1011 
    1012     AutoCaller autoCaller(this);
    1013     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1014 
    1015     // prohibit setting a UUID only as the machine name, or else it can
    1016     // never be found by findMachine()
    1017     Guid test(aName);
    1018 
    1019     if (test.isValid())
    1020         return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
    1021 
    1022     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1023 
    1024     HRESULT rc = checkStateDependency(MutableStateDep);
    1025     if (FAILED(rc)) return rc;
    1026 
    1027     setModified(IsModified_MachineData);
    1028     mUserData.backup();
    1029     mUserData->s.strName = aName;
    1030 
    1031     return S_OK;
    1032 }
    1033 
    1034 STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
    1035 {
    1036     CheckComArgOutPointerValid(aDescription);
    1037 
    1038     AutoCaller autoCaller(this);
    1039     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1040 
    1041     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1042 
    1043     mUserData->s.strDescription.cloneTo(aDescription);
    1044 
    1045     return S_OK;
    1046 }
    1047 
    1048 STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
    1049 {
    1050     AutoCaller autoCaller(this);
    1051     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1052 
    1053     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1054 
    1055     // this can be done in principle in any state as it doesn't affect the VM
    1056     // significantly, but play safe by not messing around while complex
    1057     // activities are going on
    1058     HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
    1059     if (FAILED(rc)) return rc;
    1060 
    1061     setModified(IsModified_MachineData);
    1062     mUserData.backup();
    1063     mUserData->s.strDescription = aDescription;
    1064 
    1065     return S_OK;
    1066 }
    1067 
    1068 STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
    1069 {
    1070     CheckComArgOutPointerValid(aId);
    1071 
    1072     AutoLimitedCaller autoCaller(this);
    1073     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1074 
    1075     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1076 
    1077     mData->mUuid.toUtf16().cloneTo(aId);
    1078 
    1079     return S_OK;
    1080 }
    1081 
    1082 STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
    1083 {
    1084     CheckComArgOutSafeArrayPointerValid(aGroups);
    1085 
    1086     AutoCaller autoCaller(this);
    1087     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1088 
    1089     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1090     SafeArray<BSTR> groups(mUserData->s.llGroups.size());
    1091     size_t i = 0;
    1092     for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
    1093          it != mUserData->s.llGroups.end();
    1094          ++it, i++)
    1095     {
    1096         Bstr tmp = *it;
    1097         tmp.cloneTo(&groups[i]);
    1098     }
    1099     groups.detachTo(ComSafeArrayOutArg(aGroups));
    1100 
    1101     return S_OK;
    1102 }
    1103 
    1104 STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
    1105 {
    1106     AutoCaller autoCaller(this);
    1107     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1108 
    1109     StringsList llGroups;
    1110     HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
    1111     if (FAILED(rc))
    1112         return rc;
    1113 
    1114     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1115 
    1116     // changing machine groups is possible while the VM is offline
    1117     rc = checkStateDependency(OfflineStateDep);
    1118     if (FAILED(rc)) return rc;
    1119 
    1120     setModified(IsModified_MachineData);
    1121     mUserData.backup();
    1122     mUserData->s.llGroups = llGroups;
    1123 
    1124     return S_OK;
    1125 }
    1126 
    1127 STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
    1128 {
    1129     CheckComArgOutPointerValid(aOSTypeId);
    1130 
    1131     AutoCaller autoCaller(this);
    1132     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1133 
    1134     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1135 
    1136     mUserData->s.strOsType.cloneTo(aOSTypeId);
    1137 
    1138     return S_OK;
    1139 }
    1140 
    1141 STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
    1142 {
    1143     CheckComArgStrNotEmptyOrNull(aOSTypeId);
    1144 
    1145     AutoCaller autoCaller(this);
    1146     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1147 
    1148     /* look up the object by Id to check it is valid */
    1149     ComPtr<IGuestOSType> guestOSType;
    1150     HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
    1151     if (FAILED(rc)) return rc;
    1152 
    1153     /* when setting, always use the "etalon" value for consistency -- lookup
    1154      * by ID is case-insensitive and the input value may have different case */
    1155     Bstr osTypeId;
    1156     rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
    1157     if (FAILED(rc)) return rc;
    1158 
    1159     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1160 
    1161     rc = checkStateDependency(MutableStateDep);
    1162     if (FAILED(rc)) return rc;
    1163 
    1164     setModified(IsModified_MachineData);
    1165     mUserData.backup();
    1166     mUserData->s.strOsType = osTypeId;
    1167 
    1168     return S_OK;
    1169 }
    1170 
    1171 
    1172 STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
    1173 {
    1174     CheckComArgOutPointerValid(aFirmwareType);
    1175 
    1176     AutoCaller autoCaller(this);
    1177     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1178 
    1179     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1180 
    1181     *aFirmwareType = mHWData->mFirmwareType;
    1182 
    1183     return S_OK;
    1184 }
    1185 
    1186 STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
    1187 {
    1188     AutoCaller autoCaller(this);
    1189     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1190     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1191 
    1192     HRESULT rc = checkStateDependency(MutableStateDep);
    1193     if (FAILED(rc)) return rc;
    1194 
    1195     setModified(IsModified_MachineData);
    1196     mHWData.backup();
    1197     mHWData->mFirmwareType = aFirmwareType;
    1198 
    1199     return S_OK;
    1200 }
    1201 
    1202 STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
    1203 {
    1204     CheckComArgOutPointerValid(aKeyboardHIDType);
    1205 
    1206     AutoCaller autoCaller(this);
    1207     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1208 
    1209     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1210 
    1211     *aKeyboardHIDType = mHWData->mKeyboardHIDType;
    1212 
    1213     return S_OK;
    1214 }
    1215 
    1216 STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T  aKeyboardHIDType)
    1217 {
    1218     AutoCaller autoCaller(this);
    1219     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1220     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1221 
    1222     HRESULT rc = checkStateDependency(MutableStateDep);
    1223     if (FAILED(rc)) return rc;
    1224 
    1225     setModified(IsModified_MachineData);
    1226     mHWData.backup();
    1227     mHWData->mKeyboardHIDType = aKeyboardHIDType;
    1228 
    1229     return S_OK;
    1230 }
    1231 
    1232 STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
    1233 {
    1234     CheckComArgOutPointerValid(aPointingHIDType);
    1235 
    1236     AutoCaller autoCaller(this);
    1237     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1238 
    1239     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1240 
    1241     *aPointingHIDType = mHWData->mPointingHIDType;
    1242 
    1243     return S_OK;
    1244 }
    1245 
    1246 STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T  aPointingHIDType)
    1247 {
    1248     AutoCaller autoCaller(this);
    1249     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1250     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1251 
    1252     HRESULT rc = checkStateDependency(MutableStateDep);
    1253     if (FAILED(rc)) return rc;
    1254 
    1255     setModified(IsModified_MachineData);
    1256     mHWData.backup();
    1257     mHWData->mPointingHIDType = aPointingHIDType;
    1258 
    1259     return S_OK;
    1260 }
    1261 
    1262 STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
    1263 {
    1264     CheckComArgOutPointerValid(aChipsetType);
    1265 
    1266     AutoCaller autoCaller(this);
    1267     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1268 
    1269     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1270 
    1271     *aChipsetType = mHWData->mChipsetType;
    1272 
    1273     return S_OK;
    1274 }
    1275 
    1276 STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
    1277 {
    1278     AutoCaller autoCaller(this);
    1279     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1280     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1281 
    1282     HRESULT rc = checkStateDependency(MutableStateDep);
    1283     if (FAILED(rc)) return rc;
    1284 
    1285     if (aChipsetType != mHWData->mChipsetType)
    1286     {
    1287         setModified(IsModified_MachineData);
    1288         mHWData.backup();
    1289         mHWData->mChipsetType = aChipsetType;
    1290 
    1291         // Resize network adapter array, to be finalized on commit/rollback.
    1292         // We must not throw away entries yet, otherwise settings are lost
    1293         // without a way to roll back.
    1294         size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
    1295         size_t oldCount = mNetworkAdapters.size();
    1296         if (newCount > oldCount)
    1297         {
    1298             mNetworkAdapters.resize(newCount);
    1299             for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
    1300             {
    1301                 unconst(mNetworkAdapters[slot]).createObject();
    1302                 mNetworkAdapters[slot]->init(this, slot);
    1303             }
    1304         }
    1305     }
    1306 
    1307     return S_OK;
    1308 }
    1309 
    1310 STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
    1311 {
    1312     CheckComArgOutPointerValid(aHWVersion);
    1313 
    1314     AutoCaller autoCaller(this);
    1315     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1316 
    1317     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1318 
    1319     mHWData->mHWVersion.cloneTo(aHWVersion);
    1320 
    1321     return S_OK;
    1322 }
    1323 
    1324 STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
    1325 {
    1326     /* check known version */
    1327     Utf8Str hwVersion = aHWVersion;
    1328     if (    hwVersion.compare("1") != 0
    1329         &&  hwVersion.compare("2") != 0)
    1330         return setError(E_INVALIDARG,
    1331                         tr("Invalid hardware version: %ls\n"), aHWVersion);
    1332 
    1333     AutoCaller autoCaller(this);
    1334     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1335 
    1336     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1337 
    1338     HRESULT rc = checkStateDependency(MutableStateDep);
    1339     if (FAILED(rc)) return rc;
    1340 
    1341     setModified(IsModified_MachineData);
    1342     mHWData.backup();
    1343     mHWData->mHWVersion = hwVersion;
    1344 
    1345     return S_OK;
    1346 }
    1347 
    1348 STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
    1349 {
    1350     CheckComArgOutPointerValid(aUUID);
    1351 
    1352     AutoCaller autoCaller(this);
    1353     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1354 
    1355     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1356 
    1357     if (mHWData->mHardwareUUID.isValid())
    1358         mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
    1359     else
    1360         mData->mUuid.toUtf16().cloneTo(aUUID);
    1361 
    1362     return S_OK;
    1363 }
    1364 
    1365 STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
    1366 {
    1367     Guid hardwareUUID(aUUID);
    1368     if (!hardwareUUID.isValid())
    1369         return E_INVALIDARG;
    1370 
    1371     AutoCaller autoCaller(this);
    1372     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1373 
    1374     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1375 
    1376     HRESULT rc = checkStateDependency(MutableStateDep);
    1377     if (FAILED(rc)) return rc;
    1378 
    1379     setModified(IsModified_MachineData);
    1380     mHWData.backup();
    1381     if (hardwareUUID == mData->mUuid)
    1382         mHWData->mHardwareUUID.clear();
    1383     else
    1384         mHWData->mHardwareUUID = hardwareUUID;
    1385 
    1386     return S_OK;
    1387 }
    1388 
    1389 STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
    1390 {
    1391     CheckComArgOutPointerValid(memorySize);
    1392 
    1393     AutoCaller autoCaller(this);
    1394     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1395 
    1396     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1397 
    1398     *memorySize = mHWData->mMemorySize;
    1399 
    1400     return S_OK;
    1401 }
    1402 
    1403 STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
    1404 {
    1405     /* check RAM limits */
    1406     if (    memorySize < MM_RAM_MIN_IN_MB
    1407          || memorySize > MM_RAM_MAX_IN_MB
    1408        )
    1409         return setError(E_INVALIDARG,
    1410                         tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
    1411                         memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
    1412 
    1413     AutoCaller autoCaller(this);
    1414     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1415 
    1416     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1417 
    1418     HRESULT rc = checkStateDependency(MutableStateDep);
    1419     if (FAILED(rc)) return rc;
    1420 
    1421     setModified(IsModified_MachineData);
    1422     mHWData.backup();
    1423     mHWData->mMemorySize = memorySize;
    1424 
    1425     return S_OK;
    1426 }
    1427 
    1428 STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
    1429 {
    1430     CheckComArgOutPointerValid(CPUCount);
    1431 
    1432     AutoCaller autoCaller(this);
    1433     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1434 
    1435     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1436 
    1437     *CPUCount = mHWData->mCPUCount;
    1438 
    1439     return S_OK;
    1440 }
    1441 
    1442 STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
    1443 {
    1444     /* check CPU limits */
    1445     if (    CPUCount < SchemaDefs::MinCPUCount
    1446          || CPUCount > SchemaDefs::MaxCPUCount
    1447        )
    1448         return setError(E_INVALIDARG,
    1449                         tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
    1450                         CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
    1451 
    1452     AutoCaller autoCaller(this);
    1453     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1454 
    1455     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1456 
    1457     /* We cant go below the current number of CPUs attached if hotplug is enabled*/
    1458     if (mHWData->mCPUHotPlugEnabled)
    1459     {
    1460         for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
    1461         {
    1462             if (mHWData->mCPUAttached[idx])
    1463                 return setError(E_INVALIDARG,
    1464                                 tr("There is still a CPU attached to socket %lu."
    1465                                    "Detach the CPU before removing the socket"),
    1466                                 CPUCount, idx+1);
    1467         }
    1468     }
    1469 
    1470     HRESULT rc = checkStateDependency(MutableStateDep);
    1471     if (FAILED(rc)) return rc;
    1472 
    1473     setModified(IsModified_MachineData);
    1474     mHWData.backup();
    1475     mHWData->mCPUCount = CPUCount;
    1476 
    1477     return S_OK;
    1478 }
    1479 
    1480 STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
    1481 {
    1482     CheckComArgOutPointerValid(aExecutionCap);
    1483 
    1484     AutoCaller autoCaller(this);
    1485     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1486 
    1487     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1488 
    1489     *aExecutionCap = mHWData->mCpuExecutionCap;
    1490 
    1491     return S_OK;
    1492 }
    1493 
    1494 STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
    1495 {
    1496     HRESULT rc = S_OK;
    1497 
    1498     /* check throttle limits */
    1499     if (    aExecutionCap < 1
    1500          || aExecutionCap > 100
    1501        )
    1502         return setError(E_INVALIDARG,
    1503                         tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
    1504                         aExecutionCap, 1, 100);
    1505 
    1506     AutoCaller autoCaller(this);
    1507     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1508 
    1509     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1510 
    1511     alock.release();
    1512     rc = onCPUExecutionCapChange(aExecutionCap);
    1513     alock.acquire();
    1514     if (FAILED(rc)) return rc;
    1515 
    1516     setModified(IsModified_MachineData);
    1517     mHWData.backup();
    1518     mHWData->mCpuExecutionCap = aExecutionCap;
    1519 
    1520     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    1521     if (Global::IsOnline(mData->mMachineState))
    1522         saveSettings(NULL);
    1523 
    1524     return S_OK;
    1525 }
    1526 
    1527 
    1528 STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
    1529 {
    1530     CheckComArgOutPointerValid(aEnabled);
    1531 
    1532     AutoCaller autoCaller(this);
    1533     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1534 
    1535     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1536 
    1537     *aEnabled = mHWData->mCPUHotPlugEnabled;
    1538 
    1539     return S_OK;
    1540 }
    1541 
    1542 STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
    1543 {
    1544     HRESULT rc = S_OK;
    1545 
    1546     AutoCaller autoCaller(this);
    1547     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1548 
    1549     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1550 
    1551     rc = checkStateDependency(MutableStateDep);
    1552     if (FAILED(rc)) return rc;
    1553 
    1554     if (mHWData->mCPUHotPlugEnabled != aEnabled)
    1555     {
    1556         if (aEnabled)
    1557         {
    1558             setModified(IsModified_MachineData);
    1559             mHWData.backup();
    1560 
    1561             /* Add the amount of CPUs currently attached */
    1562             for (unsigned i = 0; i < mHWData->mCPUCount; i++)
    1563             {
    1564                 mHWData->mCPUAttached[i] = true;
    1565             }
    1566         }
    1567         else
    1568         {
    1569             /*
    1570              * We can disable hotplug only if the amount of maximum CPUs is equal
    1571              * to the amount of attached CPUs
    1572              */
    1573             unsigned cCpusAttached = 0;
    1574             unsigned iHighestId = 0;
    1575 
    1576             for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
    1577             {
    1578                 if (mHWData->mCPUAttached[i])
    1579                 {
    1580                     cCpusAttached++;
    1581                     iHighestId = i;
    1582                 }
    1583             }
    1584 
    1585             if (   (cCpusAttached != mHWData->mCPUCount)
    1586                 || (iHighestId >= mHWData->mCPUCount))
    1587                 return setError(E_INVALIDARG,
    1588                                 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
    1589 
    1590             setModified(IsModified_MachineData);
    1591             mHWData.backup();
    1592         }
    1593     }
    1594 
    1595     mHWData->mCPUHotPlugEnabled = aEnabled;
    1596 
    1597     return rc;
    1598 }
    1599 
    1600 STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
    1601 {
    1602 #ifdef VBOX_WITH_USB_CARDREADER
    1603     CheckComArgOutPointerValid(aEnabled);
    1604 
    1605     AutoCaller autoCaller(this);
    1606     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1607 
    1608     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1609 
    1610     *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
    1611 
    1612     return S_OK;
    1613 #else
    1614     NOREF(aEnabled);
    1615     return E_NOTIMPL;
    1616 #endif
    1617 }
    1618 
    1619 STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
    1620 {
    1621 #ifdef VBOX_WITH_USB_CARDREADER
    1622     AutoCaller autoCaller(this);
    1623     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1624     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1625 
    1626     HRESULT rc = checkStateDependency(MutableStateDep);
    1627     if (FAILED(rc)) return rc;
    1628 
    1629     setModified(IsModified_MachineData);
    1630     mHWData.backup();
    1631     mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
    1632 
    1633     return S_OK;
    1634 #else
    1635     NOREF(aEnabled);
    1636     return E_NOTIMPL;
    1637 #endif
    1638 }
    1639 
    1640 STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
    1641 {
    1642 #ifdef VBOX_WITH_USB_VIDEO
    1643     CheckComArgOutPointerValid(aEnabled);
    1644 
    1645     AutoCaller autoCaller(this);
    1646     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1647 
    1648     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1649 
    1650     *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
    1651 
    1652     return S_OK;
    1653 #else
    1654     NOREF(aEnabled);
    1655     return E_NOTIMPL;
    1656 #endif
    1657 }
    1658 
    1659 STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
    1660 {
    1661 #ifdef VBOX_WITH_USB_VIDEO
    1662     AutoCaller autoCaller(this);
    1663     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1664     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1665 
    1666     HRESULT rc = checkStateDependency(MutableStateDep);
    1667     if (FAILED(rc)) return rc;
    1668 
    1669     setModified(IsModified_MachineData);
    1670     mHWData.backup();
    1671     mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
    1672 
    1673     return S_OK;
    1674 #else
    1675     NOREF(aEnabled);
    1676     return E_NOTIMPL;
    1677 #endif
    1678 }
    1679 
    1680 STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
    1681 {
    1682     CheckComArgOutPointerValid(aEnabled);
    1683 
    1684     AutoCaller autoCaller(this);
    1685     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1686     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1687 
    1688     *aEnabled = mHWData->mHPETEnabled;
    1689 
    1690     return S_OK;
    1691 }
    1692 
    1693 STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
    1694 {
    1695     HRESULT rc = S_OK;
    1696 
    1697     AutoCaller autoCaller(this);
    1698     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1699     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1700 
    1701     rc = checkStateDependency(MutableStateDep);
    1702     if (FAILED(rc)) return rc;
    1703 
    1704     setModified(IsModified_MachineData);
    1705     mHWData.backup();
    1706 
    1707     mHWData->mHPETEnabled = aEnabled;
    1708 
    1709     return rc;
    1710 }
    1711 
    1712 STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
    1713 {
    1714     AutoCaller autoCaller(this);
    1715     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1716 
    1717     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1718 
    1719     *fEnabled = mHWData->mVideoCaptureEnabled;
    1720     return S_OK;
    1721 }
    1722 
    1723 STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
    1724 {
    1725     HRESULT rc = S_OK;
    1726 
    1727     AutoCaller autoCaller(this);
    1728     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1729     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1730 
    1731     setModified(IsModified_MachineData);
    1732     mHWData.backup();
    1733     mHWData->mVideoCaptureEnabled = fEnabled;
    1734 
    1735     alock.release();
    1736     rc = onVideoCaptureChange();
    1737     alock.acquire();
    1738     if (FAILED(rc))
    1739     {
    1740         /*
    1741          * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
    1742          * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
    1743          * determine if it should start or stop capturing. Therefore we need to manually
    1744          * undo change.
    1745          */
    1746         mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
    1747         return rc;
    1748     }
    1749 
    1750     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    1751     if (Global::IsOnline(mData->mMachineState))
    1752         saveSettings(NULL);
    1753 
    1754     return rc;
    1755 }
    1756 
    1757 STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
    1758 {
    1759     CheckComArgOutSafeArrayPointerValid(aScreens);
    1760 
    1761     AutoCaller autoCaller(this);
    1762     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1763 
    1764     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1765 
    1766     SafeArray<BOOL> screens(mHWData->mMonitorCount);
    1767     for (unsigned i = 0; i < screens.size(); i++)
    1768         screens[i] = mHWData->maVideoCaptureScreens[i];
    1769     screens.detachTo(ComSafeArrayOutArg(aScreens));
    1770     return S_OK;
    1771 }
    1772 
    1773 STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
    1774 {
    1775     SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
    1776     AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
    1777     bool fChanged = false;
    1778 
    1779     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1780 
    1781     for (unsigned i = 0; i < screens.size(); i++)
    1782     {
    1783         if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
    1784         {
    1785             mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
    1786             fChanged = true;
    1787         }
    1788     }
    1789     if (fChanged)
    1790     {
    1791         alock.release();
    1792         HRESULT rc = onVideoCaptureChange();
    1793         alock.acquire();
    1794         if (FAILED(rc)) return rc;
    1795         setModified(IsModified_MachineData);
    1796 
    1797         /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    1798         if (Global::IsOnline(mData->mMachineState))
    1799             saveSettings(NULL);
    1800     }
    1801 
    1802     return S_OK;
    1803 }
    1804 
    1805 STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
    1806 {
    1807     AutoCaller autoCaller(this);
    1808     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1809 
    1810     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1811     if (mHWData->mVideoCaptureFile.isEmpty())
    1812     {
    1813         Utf8Str defaultFile;
    1814         getDefaultVideoCaptureFile(defaultFile);
    1815         defaultFile.cloneTo(apFile);
    1816     }
    1817     else
    1818         mHWData->mVideoCaptureFile.cloneTo(apFile);
    1819     return S_OK;
    1820 }
    1821 
    1822 STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
    1823 {
    1824     Utf8Str strFile(aFile);
    1825     AutoCaller autoCaller(this);
    1826     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1827 
    1828     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1829 
    1830     if (   Global::IsOnline(mData->mMachineState)
    1831         && mHWData->mVideoCaptureEnabled)
    1832         return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
    1833 
    1834     if (!RTPathStartsWithRoot(strFile.c_str()))
    1835         return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
    1836 
    1837     if (!strFile.isEmpty())
    1838     {
    1839         Utf8Str defaultFile;
    1840         getDefaultVideoCaptureFile(defaultFile);
    1841         if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
    1842             strFile.setNull();
    1843     }
    1844 
    1845     setModified(IsModified_MachineData);
    1846     mHWData.backup();
    1847     mHWData->mVideoCaptureFile = strFile;
    1848 
    1849     return S_OK;
    1850 }
    1851 
    1852 STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
    1853 {
    1854     AutoCaller autoCaller(this);
    1855     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1856 
    1857     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1858     *aHorzRes = mHWData->mVideoCaptureWidth;
    1859     return S_OK;
    1860 }
    1861 
    1862 STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
    1863 {
    1864     AutoCaller autoCaller(this);
    1865     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1866 
    1867     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1868 
    1869     if (   Global::IsOnline(mData->mMachineState)
    1870         && mHWData->mVideoCaptureEnabled)
    1871         return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
    1872 
    1873     setModified(IsModified_MachineData);
    1874     mHWData.backup();
    1875     mHWData->mVideoCaptureWidth = aHorzRes;
    1876 
    1877     return S_OK;
    1878 }
    1879 
    1880 STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
    1881 {
    1882     AutoCaller autoCaller(this);
    1883     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1884 
    1885     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1886      *aVertRes = mHWData->mVideoCaptureHeight;
    1887     return S_OK;
    1888 }
    1889 
    1890 STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
    1891 {
    1892     AutoCaller autoCaller(this);
    1893     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1894 
    1895     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1896 
    1897     if (   Global::IsOnline(mData->mMachineState)
    1898         && mHWData->mVideoCaptureEnabled)
    1899         return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
    1900 
    1901     setModified(IsModified_MachineData);
    1902     mHWData.backup();
    1903     mHWData->mVideoCaptureHeight = aVertRes;
    1904 
    1905     return S_OK;
    1906 }
    1907 
    1908 STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
    1909 {
    1910     AutoCaller autoCaller(this);
    1911     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1912 
    1913     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1914      *aRate = mHWData->mVideoCaptureRate;
    1915     return S_OK;
    1916 }
    1917 
    1918 STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
    1919 {
    1920     AutoCaller autoCaller(this);
    1921     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1922 
    1923     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1924 
    1925     if (   Global::IsOnline(mData->mMachineState)
    1926         && mHWData->mVideoCaptureEnabled)
    1927         return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
    1928 
    1929     setModified(IsModified_MachineData);
    1930     mHWData.backup();
    1931     mHWData->mVideoCaptureRate = aRate;
    1932 
    1933     return S_OK;
    1934 }
    1935 
    1936 STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
    1937 {
    1938     AutoCaller autoCaller(this);
    1939     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1940 
    1941     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1942      *aFPS = mHWData->mVideoCaptureFPS;
    1943     return S_OK;
    1944 }
    1945 
    1946 STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
    1947 {
    1948     AutoCaller autoCaller(this);
    1949     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1950 
    1951     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1952 
    1953     if (   Global::IsOnline(mData->mMachineState)
    1954         && mHWData->mVideoCaptureEnabled)
    1955         return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
    1956 
    1957     setModified(IsModified_MachineData);
    1958     mHWData.backup();
    1959     mHWData->mVideoCaptureFPS = aFPS;
    1960 
    1961     return S_OK;
    1962 }
    1963 
    1964 STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
    1965 {
    1966     CheckComArgOutPointerValid(aGraphicsControllerType);
    1967 
    1968     AutoCaller autoCaller(this);
    1969     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1970 
    1971     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1972 
    1973     *aGraphicsControllerType = mHWData->mGraphicsControllerType;
    1974 
    1975     return S_OK;
    1976 }
    1977 
    1978 STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
    1979 {
    1980     switch (aGraphicsControllerType)
    1981     {
    1982         case GraphicsControllerType_Null:
    1983         case GraphicsControllerType_VBoxVGA:
    1984             break;
    1985         default:
    1986             return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
    1987     }
    1988 
    1989     AutoCaller autoCaller(this);
    1990     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1991 
    1992     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1993 
    1994     HRESULT rc = checkStateDependency(MutableStateDep);
    1995     if (FAILED(rc)) return rc;
    1996 
    1997     setModified(IsModified_MachineData);
    1998     mHWData.backup();
    1999     mHWData->mGraphicsControllerType = aGraphicsControllerType;
    2000 
    2001     return S_OK;
    2002 }
    2003 
    2004 STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
    2005 {
    2006     CheckComArgOutPointerValid(memorySize);
    2007 
    2008     AutoCaller autoCaller(this);
    2009     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2010 
    2011     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2012 
    2013     *memorySize = mHWData->mVRAMSize;
    2014 
    2015     return S_OK;
    2016 }
    2017 
    2018 STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
    2019 {
    2020     /* check VRAM limits */
    2021     if (memorySize < SchemaDefs::MinGuestVRAM ||
    2022         memorySize > SchemaDefs::MaxGuestVRAM)
    2023         return setError(E_INVALIDARG,
    2024                         tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
    2025                         memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
    2026 
    2027     AutoCaller autoCaller(this);
    2028     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2029 
    2030     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2031 
    2032     HRESULT rc = checkStateDependency(MutableStateDep);
    2033     if (FAILED(rc)) return rc;
    2034 
    2035     setModified(IsModified_MachineData);
    2036     mHWData.backup();
    2037     mHWData->mVRAMSize = memorySize;
    2038 
    2039     return S_OK;
    2040 }
    2041 
    2042 /** @todo this method should not be public */
    2043 STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
    2044 {
    2045     CheckComArgOutPointerValid(memoryBalloonSize);
    2046 
    2047     AutoCaller autoCaller(this);
    2048     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2049 
    2050     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2051 
    2052     *memoryBalloonSize = mHWData->mMemoryBalloonSize;
    2053 
    2054     return S_OK;
    2055 }
    2056 
    2057 /**
    2058  * Set the memory balloon size.
    2059  *
    2060  * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
    2061  * we have to make sure that we never call IGuest from here.
    2062  */
    2063 STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
    2064 {
    2065     /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
    2066 #if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
    2067     /* check limits */
    2068     if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
    2069         return setError(E_INVALIDARG,
    2070                         tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
    2071                         memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
    2072 
    2073     AutoCaller autoCaller(this);
    2074     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2075 
    2076     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2077 
    2078     setModified(IsModified_MachineData);
    2079     mHWData.backup();
    2080     mHWData->mMemoryBalloonSize = memoryBalloonSize;
    2081 
    2082     return S_OK;
    2083 #else
    2084     NOREF(memoryBalloonSize);
    2085     return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
    2086 #endif
    2087 }
    2088 
    2089 STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
    2090 {
    2091     CheckComArgOutPointerValid(aEnabled);
    2092 
    2093     AutoCaller autoCaller(this);
    2094     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2095 
    2096     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2097 
    2098     *aEnabled = mHWData->mPageFusionEnabled;
    2099     return S_OK;
    2100 }
    2101 
    2102 STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
    2103 {
    2104 #ifdef VBOX_WITH_PAGE_SHARING
    2105     AutoCaller autoCaller(this);
    2106     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2107 
    2108     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2109 
    2110     /** @todo must support changes for running vms and keep this in sync with IGuest. */
    2111     setModified(IsModified_MachineData);
    2112     mHWData.backup();
    2113     mHWData->mPageFusionEnabled = aEnabled;
    2114     return S_OK;
    2115 #else
    2116     NOREF(aEnabled);
    2117     return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
    2118 #endif
    2119 }
    2120 
    2121 STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
    2122 {
    2123     CheckComArgOutPointerValid(aEnabled);
    2124 
    2125     AutoCaller autoCaller(this);
    2126     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2127 
    2128     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2129 
    2130     *aEnabled = mHWData->mAccelerate3DEnabled;
    2131 
    2132     return S_OK;
    2133 }
    2134 
    2135 STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
    2136 {
    2137     AutoCaller autoCaller(this);
    2138     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2139 
    2140     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2141 
    2142     HRESULT rc = checkStateDependency(MutableStateDep);
    2143     if (FAILED(rc)) return rc;
    2144 
    2145     /** @todo check validity! */
    2146 
    2147     setModified(IsModified_MachineData);
    2148     mHWData.backup();
    2149     mHWData->mAccelerate3DEnabled = enable;
    2150 
    2151     return S_OK;
    2152 }
    2153 
    2154 
    2155 STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
    2156 {
    2157     CheckComArgOutPointerValid(aEnabled);
    2158 
    2159     AutoCaller autoCaller(this);
    2160     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2161 
    2162     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2163 
    2164     *aEnabled = mHWData->mAccelerate2DVideoEnabled;
    2165 
    2166     return S_OK;
    2167 }
    2168 
    2169 STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
    2170 {
    2171     AutoCaller autoCaller(this);
    2172     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2173 
    2174     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2175 
    2176     HRESULT rc = checkStateDependency(MutableStateDep);
    2177     if (FAILED(rc)) return rc;
    2178 
    2179     /** @todo check validity! */
    2180 
    2181     setModified(IsModified_MachineData);
    2182     mHWData.backup();
    2183     mHWData->mAccelerate2DVideoEnabled = enable;
    2184 
    2185     return S_OK;
    2186 }
    2187 
    2188 STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
    2189 {
    2190     CheckComArgOutPointerValid(monitorCount);
    2191 
    2192     AutoCaller autoCaller(this);
    2193     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2194 
    2195     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2196 
    2197     *monitorCount = mHWData->mMonitorCount;
    2198 
    2199     return S_OK;
    2200 }
    2201 
    2202 STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
    2203 {
    2204     /* make sure monitor count is a sensible number */
    2205     if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
    2206         return setError(E_INVALIDARG,
    2207                         tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
    2208                         monitorCount, 1, SchemaDefs::MaxGuestMonitors);
    2209 
    2210     AutoCaller autoCaller(this);
    2211     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2212 
    2213     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2214 
    2215     HRESULT rc = checkStateDependency(MutableStateDep);
    2216     if (FAILED(rc)) return rc;
    2217 
    2218     setModified(IsModified_MachineData);
    2219     mHWData.backup();
    2220     mHWData->mMonitorCount = monitorCount;
    2221 
    2222     return S_OK;
    2223 }
    2224 
    2225 STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
    2226 {
    2227     CheckComArgOutPointerValid(biosSettings);
    2228 
    2229     AutoCaller autoCaller(this);
    2230     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2231 
    2232     /* mBIOSSettings is constant during life time, no need to lock */
    2233     mBIOSSettings.queryInterfaceTo(biosSettings);
    2234 
    2235     return S_OK;
    2236 }
    2237 
    2238 STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
    2239 {
    2240     CheckComArgOutPointerValid(aVal);
    2241 
    2242     AutoCaller autoCaller(this);
    2243     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2244 
    2245     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2246 
    2247     switch (property)
    2248     {
    2249         case CPUPropertyType_PAE:
    2250             *aVal = mHWData->mPAEEnabled;
    2251             break;
    2252 
    2253         case CPUPropertyType_Synthetic:
    2254             *aVal = mHWData->mSyntheticCpu;
    2255             break;
    2256 
    2257         case CPUPropertyType_LongMode:
    2258             if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
    2259                 *aVal = TRUE;
    2260             else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
    2261                 *aVal = FALSE;
    2262 #if HC_ARCH_BITS == 64
    2263             else
    2264                 *aVal = TRUE;
    2265 #else
    2266             else
    2267             {
    2268                 *aVal = FALSE;
    2269 
    2270                 ComPtr<IGuestOSType> ptrGuestOSType;
    2271                 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
    2272                 if (SUCCEEDED(hrc2))
    2273                 {
    2274                     BOOL fIs64Bit = FALSE;
    2275                     hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
    2276                     if (SUCCEEDED(hrc2) && fIs64Bit)
    2277                     {
    2278                         ComObjPtr<Host> ptrHost = mParent->host();
    2279                         alock.release();
    2280 
    2281                         hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
    2282                         if (FAILED(hrc2))
    2283                             *aVal = FALSE;
    2284                     }
    2285                 }
    2286             }
    2287 #endif
    2288             break;
    2289 
    2290         default:
    2291             return E_INVALIDARG;
    2292     }
    2293     return S_OK;
    2294 }
    2295 
    2296 STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
    2297 {
    2298     AutoCaller autoCaller(this);
    2299     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2300 
    2301     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2302 
    2303     HRESULT rc = checkStateDependency(MutableStateDep);
    2304     if (FAILED(rc)) return rc;
    2305 
    2306     switch (property)
    2307     {
    2308         case CPUPropertyType_PAE:
    2309             setModified(IsModified_MachineData);
    2310             mHWData.backup();
    2311             mHWData->mPAEEnabled = !!aVal;
    2312             break;
    2313 
    2314         case CPUPropertyType_Synthetic:
    2315             setModified(IsModified_MachineData);
    2316             mHWData.backup();
    2317             mHWData->mSyntheticCpu = !!aVal;
    2318             break;
    2319 
    2320         case CPUPropertyType_LongMode:
    2321             setModified(IsModified_MachineData);
    2322             mHWData.backup();
    2323             mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
    2324             break;
    2325 
    2326         default:
    2327             return E_INVALIDARG;
    2328     }
    2329     return S_OK;
    2330 }
    2331 
    2332 STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
    2333 {
    2334     CheckComArgOutPointerValid(aValEax);
    2335     CheckComArgOutPointerValid(aValEbx);
    2336     CheckComArgOutPointerValid(aValEcx);
    2337     CheckComArgOutPointerValid(aValEdx);
    2338 
    2339     AutoCaller autoCaller(this);
    2340     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2341 
    2342     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2343 
    2344     switch(aId)
    2345     {
    2346         case 0x0:
    2347         case 0x1:
    2348         case 0x2:
    2349         case 0x3:
    2350         case 0x4:
    2351         case 0x5:
    2352         case 0x6:
    2353         case 0x7:
    2354         case 0x8:
    2355         case 0x9:
    2356         case 0xA:
    2357             if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
    2358                 return E_INVALIDARG;
    2359 
    2360             *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
    2361             *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
    2362             *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
    2363             *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
    2364             break;
    2365 
    2366         case 0x80000000:
    2367         case 0x80000001:
    2368         case 0x80000002:
    2369         case 0x80000003:
    2370         case 0x80000004:
    2371         case 0x80000005:
    2372         case 0x80000006:
    2373         case 0x80000007:
    2374         case 0x80000008:
    2375         case 0x80000009:
    2376         case 0x8000000A:
    2377             if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
    2378                 return E_INVALIDARG;
    2379 
    2380             *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
    2381             *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
    2382             *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
    2383             *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
    2384             break;
    2385 
    2386         default:
    2387             return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
    2388     }
    2389     return S_OK;
    2390 }
    2391 
    2392 STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
    2393 {
    2394     AutoCaller autoCaller(this);
    2395     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2396 
    2397     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2398 
    2399     HRESULT rc = checkStateDependency(MutableStateDep);
    2400     if (FAILED(rc)) return rc;
    2401 
    2402     switch(aId)
    2403     {
    2404         case 0x0:
    2405         case 0x1:
    2406         case 0x2:
    2407         case 0x3:
    2408         case 0x4:
    2409         case 0x5:
    2410         case 0x6:
    2411         case 0x7:
    2412         case 0x8:
    2413         case 0x9:
    2414         case 0xA:
    2415             AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
    2416             AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
    2417             setModified(IsModified_MachineData);
    2418             mHWData.backup();
    2419             mHWData->mCpuIdStdLeafs[aId].ulId  = aId;
    2420             mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
    2421             mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
    2422             mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
    2423             mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
    2424             break;
    2425 
    2426         case 0x80000000:
    2427         case 0x80000001:
    2428         case 0x80000002:
    2429         case 0x80000003:
    2430         case 0x80000004:
    2431         case 0x80000005:
    2432         case 0x80000006:
    2433         case 0x80000007:
    2434         case 0x80000008:
    2435         case 0x80000009:
    2436         case 0x8000000A:
    2437             AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
    2438             AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
    2439             setModified(IsModified_MachineData);
    2440             mHWData.backup();
    2441             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId  = aId;
    2442             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
    2443             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
    2444             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
    2445             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
    2446             break;
    2447 
    2448         default:
    2449             return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
    2450     }
    2451     return S_OK;
    2452 }
    2453 
    2454 STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
    2455 {
    2456     AutoCaller autoCaller(this);
    2457     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2458 
    2459     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2460 
    2461     HRESULT rc = checkStateDependency(MutableStateDep);
    2462     if (FAILED(rc)) return rc;
    2463 
    2464     switch(aId)
    2465     {
    2466         case 0x0:
    2467         case 0x1:
    2468         case 0x2:
    2469         case 0x3:
    2470         case 0x4:
    2471         case 0x5:
    2472         case 0x6:
    2473         case 0x7:
    2474         case 0x8:
    2475         case 0x9:
    2476         case 0xA:
    2477             AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
    2478             AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
    2479             setModified(IsModified_MachineData);
    2480             mHWData.backup();
    2481             /* Invalidate leaf. */
    2482             mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
    2483             break;
    2484 
    2485         case 0x80000000:
    2486         case 0x80000001:
    2487         case 0x80000002:
    2488         case 0x80000003:
    2489         case 0x80000004:
    2490         case 0x80000005:
    2491         case 0x80000006:
    2492         case 0x80000007:
    2493         case 0x80000008:
    2494         case 0x80000009:
    2495         case 0x8000000A:
    2496             AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
    2497             AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
    2498             setModified(IsModified_MachineData);
    2499             mHWData.backup();
    2500             /* Invalidate leaf. */
    2501             mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
    2502             break;
    2503 
    2504         default:
    2505             return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
    2506     }
    2507     return S_OK;
    2508 }
    2509 
    2510 STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
    2511 {
    2512     AutoCaller autoCaller(this);
    2513     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2514 
    2515     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2516 
    2517     HRESULT rc = checkStateDependency(MutableStateDep);
    2518     if (FAILED(rc)) return rc;
    2519 
    2520     setModified(IsModified_MachineData);
    2521     mHWData.backup();
    2522 
    2523     /* Invalidate all standard leafs. */
    2524     for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
    2525         mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
    2526 
    2527     /* Invalidate all extended leafs. */
    2528     for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
    2529         mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
    2530 
    2531     return S_OK;
    2532 }
    2533 
    2534 STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
    2535 {
    2536     CheckComArgOutPointerValid(aVal);
    2537 
    2538     AutoCaller autoCaller(this);
    2539     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2540 
    2541     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2542 
    2543     switch(property)
    2544     {
    2545         case HWVirtExPropertyType_Enabled:
    2546             *aVal = mHWData->mHWVirtExEnabled;
    2547             break;
    2548 
    2549         case HWVirtExPropertyType_Exclusive:
    2550             *aVal = mHWData->mHWVirtExExclusive;
    2551             break;
    2552 
    2553         case HWVirtExPropertyType_VPID:
    2554             *aVal = mHWData->mHWVirtExVPIDEnabled;
    2555             break;
    2556 
    2557         case HWVirtExPropertyType_NestedPaging:
    2558             *aVal = mHWData->mHWVirtExNestedPagingEnabled;
    2559             break;
    2560 
    2561         case HWVirtExPropertyType_UnrestrictedExecution:
    2562             *aVal = mHWData->mHWVirtExUXEnabled;
    2563             break;
    2564 
    2565         case HWVirtExPropertyType_LargePages:
    2566             *aVal = mHWData->mHWVirtExLargePagesEnabled;
    2567 #if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
    2568             *aVal = FALSE;
    2569 #endif
    2570             break;
    2571 
    2572         case HWVirtExPropertyType_Force:
    2573             *aVal = mHWData->mHWVirtExForceEnabled;
    2574             break;
    2575 
    2576         default:
    2577             return E_INVALIDARG;
    2578     }
    2579     return S_OK;
    2580 }
    2581 
    2582 STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
    2583 {
    2584     AutoCaller autoCaller(this);
    2585     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2586 
    2587     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2588 
    2589     HRESULT rc = checkStateDependency(MutableStateDep);
    2590     if (FAILED(rc)) return rc;
    2591 
    2592     switch(property)
    2593     {
    2594         case HWVirtExPropertyType_Enabled:
    2595             setModified(IsModified_MachineData);
    2596             mHWData.backup();
    2597             mHWData->mHWVirtExEnabled = !!aVal;
    2598             break;
    2599 
    2600         case HWVirtExPropertyType_Exclusive:
    2601             setModified(IsModified_MachineData);
    2602             mHWData.backup();
    2603             mHWData->mHWVirtExExclusive = !!aVal;
    2604             break;
    2605 
    2606         case HWVirtExPropertyType_VPID:
    2607             setModified(IsModified_MachineData);
    2608             mHWData.backup();
    2609             mHWData->mHWVirtExVPIDEnabled = !!aVal;
    2610             break;
    2611 
    2612         case HWVirtExPropertyType_NestedPaging:
    2613             setModified(IsModified_MachineData);
    2614             mHWData.backup();
    2615             mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
    2616             break;
    2617 
    2618         case HWVirtExPropertyType_UnrestrictedExecution:
    2619             setModified(IsModified_MachineData);
    2620             mHWData.backup();
    2621             mHWData->mHWVirtExUXEnabled = !!aVal;
    2622             break;
    2623 
    2624         case HWVirtExPropertyType_LargePages:
    2625             setModified(IsModified_MachineData);
    2626             mHWData.backup();
    2627             mHWData->mHWVirtExLargePagesEnabled = !!aVal;
    2628             break;
    2629 
    2630         case HWVirtExPropertyType_Force:
    2631             setModified(IsModified_MachineData);
    2632             mHWData.backup();
    2633             mHWData->mHWVirtExForceEnabled = !!aVal;
    2634             break;
    2635 
    2636         default:
    2637             return E_INVALIDARG;
    2638     }
    2639 
    2640     return S_OK;
    2641 }
    2642 
    2643 STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
    2644 {
    2645     CheckComArgOutPointerValid(aSnapshotFolder);
    2646 
    2647     AutoCaller autoCaller(this);
    2648     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2649 
    2650     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2651 
    2652     Utf8Str strFullSnapshotFolder;
    2653     calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    2654     strFullSnapshotFolder.cloneTo(aSnapshotFolder);
    2655 
    2656     return S_OK;
    2657 }
    2658 
    2659 STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
    2660 {
    2661     /* @todo (r=dmik):
    2662      *  1. Allow to change the name of the snapshot folder containing snapshots
    2663      *  2. Rename the folder on disk instead of just changing the property
    2664      *     value (to be smart and not to leave garbage). Note that it cannot be
    2665      *     done here because the change may be rolled back. Thus, the right
    2666      *     place is #saveSettings().
    2667      */
    2668 
    2669     AutoCaller autoCaller(this);
    2670     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2671 
    2672     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2673 
    2674     HRESULT rc = checkStateDependency(MutableStateDep);
    2675     if (FAILED(rc)) return rc;
    2676 
    2677     if (!mData->mCurrentSnapshot.isNull())
    2678         return setError(E_FAIL,
    2679                         tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
    2680 
    2681     Utf8Str strSnapshotFolder0(aSnapshotFolder);       // keep original
    2682 
    2683     Utf8Str strSnapshotFolder(strSnapshotFolder0);
    2684     if (strSnapshotFolder.isEmpty())
    2685         strSnapshotFolder = "Snapshots";
    2686     int vrc = calculateFullPath(strSnapshotFolder,
    2687                                 strSnapshotFolder);
    2688     if (RT_FAILURE(vrc))
    2689         return setError(E_FAIL,
    2690                         tr("Invalid snapshot folder '%ls' (%Rrc)"),
    2691                         aSnapshotFolder, vrc);
    2692 
    2693     setModified(IsModified_MachineData);
    2694     mUserData.backup();
    2695 
    2696     copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
    2697 
    2698     return S_OK;
    2699 }
    2700 
    2701 STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
    2702 {
    2703     CheckComArgOutSafeArrayPointerValid(aAttachments);
    2704 
    2705     AutoCaller autoCaller(this);
    2706     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2707 
    2708     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2709 
    2710     SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
    2711     attachments.detachTo(ComSafeArrayOutArg(aAttachments));
    2712 
    2713     return S_OK;
    2714 }
    2715 
    2716 STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
    2717 {
    2718     CheckComArgOutPointerValid(vrdeServer);
    2719 
    2720     AutoCaller autoCaller(this);
    2721     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2722 
    2723     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2724 
    2725     Assert(!!mVRDEServer);
    2726     mVRDEServer.queryInterfaceTo(vrdeServer);
    2727 
    2728     return S_OK;
    2729 }
    2730 
    2731 STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
    2732 {
    2733     CheckComArgOutPointerValid(audioAdapter);
    2734 
    2735     AutoCaller autoCaller(this);
    2736     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2737 
    2738     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2739 
    2740     mAudioAdapter.queryInterfaceTo(audioAdapter);
    2741     return S_OK;
    2742 }
    2743 
    2744 STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
    2745 {
    2746 #ifdef VBOX_WITH_VUSB
    2747     CheckComArgOutPointerValid(aUSBControllers);
    2748 
    2749     AutoCaller autoCaller(this);
    2750     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2751 
    2752     clearError();
    2753     MultiResult rc(S_OK);
    2754 
    2755 # ifdef VBOX_WITH_USB
    2756     rc = mParent->host()->checkUSBProxyService();
    2757     if (FAILED(rc)) return rc;
    2758 # endif
    2759 
    2760     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2761 
    2762     SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
    2763     ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
    2764     return S_OK;
    2765 #else
    2766     /* Note: The GUI depends on this method returning E_NOTIMPL with no
    2767      * extended error info to indicate that USB is simply not available
    2768      * (w/o treating it as a failure), for example, as in OSE */
    2769     NOREF(aUSBControllers);
    2770     ReturnComNotImplemented();
    2771 #endif /* VBOX_WITH_VUSB */
    2772 }
    2773 
    2774 STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
    2775 {
    2776 #ifdef VBOX_WITH_VUSB
    2777     CheckComArgOutPointerValid(aUSBDeviceFilters);
    2778 
    2779     AutoCaller autoCaller(this);
    2780     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2781 
    2782     clearError();
    2783     MultiResult rc(S_OK);
    2784 
    2785 # ifdef VBOX_WITH_USB
    2786     rc = mParent->host()->checkUSBProxyService();
    2787     if (FAILED(rc)) return rc;
    2788 # endif
    2789 
    2790     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2791 
    2792     return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
    2793 #else
    2794     /* Note: The GUI depends on this method returning E_NOTIMPL with no
    2795      * extended error info to indicate that USB is simply not available
    2796      * (w/o treating it as a failure), for example, as in OSE */
    2797     NOREF(aUSBDeviceFilters);
    2798     ReturnComNotImplemented();
    2799 #endif /* VBOX_WITH_VUSB */
    2800 }
    2801 
    2802 STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
    2803 {
    2804     CheckComArgOutPointerValid(aFilePath);
    2805 
    2806     AutoLimitedCaller autoCaller(this);
    2807     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2808 
    2809     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2810 
    2811     mData->m_strConfigFileFull.cloneTo(aFilePath);
    2812     return S_OK;
    2813 }
    2814 
    2815 STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
    2816 {
    2817     CheckComArgOutPointerValid(aModified);
    2818 
    2819     AutoCaller autoCaller(this);
    2820     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2821 
    2822     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2823 
    2824     HRESULT rc = checkStateDependency(MutableStateDep);
    2825     if (FAILED(rc)) return rc;
    2826 
    2827     if (!mData->pMachineConfigFile->fileExists())
    2828         // this is a new machine, and no config file exists yet:
    2829         *aModified = TRUE;
    2830     else
    2831         *aModified = (mData->flModifications != 0);
    2832 
    2833     return S_OK;
    2834 }
    2835 
    2836 STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
    2837 {
    2838     CheckComArgOutPointerValid(aSessionState);
    2839 
    2840     AutoCaller autoCaller(this);
    2841     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2842 
    2843     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2844 
    2845     *aSessionState = mData->mSession.mState;
    2846 
    2847     return S_OK;
    2848 }
    2849 
    2850 STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
    2851 {
    2852     CheckComArgOutPointerValid(aSessionType);
    2853 
    2854     AutoCaller autoCaller(this);
    2855     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2856 
    2857     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2858 
    2859     mData->mSession.mType.cloneTo(aSessionType);
    2860 
    2861     return S_OK;
    2862 }
    2863 
    2864 STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
    2865 {
    2866     CheckComArgOutPointerValid(aSessionPID);
    2867 
    2868     AutoCaller autoCaller(this);
    2869     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2870 
    2871     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2872 
    2873     *aSessionPID = mData->mSession.mPID;
    2874 
    2875     return S_OK;
    2876 }
    2877 
    2878 STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
    2879 {
    2880     CheckComArgOutPointerValid(machineState);
    2881 
    2882     AutoCaller autoCaller(this);
    2883     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2884 
    2885     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2886 
    2887     *machineState = mData->mMachineState;
    2888 
    2889     return S_OK;
    2890 }
    2891 
    2892 STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
    2893 {
    2894     CheckComArgOutPointerValid(aLastStateChange);
    2895 
    2896     AutoCaller autoCaller(this);
    2897     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2898 
    2899     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2900 
    2901     *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
    2902 
    2903     return S_OK;
    2904 }
    2905 
    2906 STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
    2907 {
    2908     CheckComArgOutPointerValid(aStateFilePath);
    2909 
    2910     AutoCaller autoCaller(this);
    2911     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2912 
    2913     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2914 
    2915     mSSData->strStateFilePath.cloneTo(aStateFilePath);
    2916 
    2917     return S_OK;
    2918 }
    2919 
    2920 STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
    2921 {
    2922     CheckComArgOutPointerValid(aLogFolder);
    2923 
    2924     AutoCaller autoCaller(this);
    2925     AssertComRCReturnRC(autoCaller.rc());
    2926 
    2927     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2928 
    2929     Utf8Str logFolder;
    2930     getLogFolder(logFolder);
    2931     logFolder.cloneTo(aLogFolder);
    2932 
    2933     return S_OK;
    2934 }
    2935 
    2936 STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
    2937 {
    2938     CheckComArgOutPointerValid(aCurrentSnapshot);
    2939 
    2940     AutoCaller autoCaller(this);
    2941     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2942 
    2943     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2944 
    2945     mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
    2946 
    2947     return S_OK;
    2948 }
    2949 
    2950 STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
    2951 {
    2952     CheckComArgOutPointerValid(aSnapshotCount);
    2953 
    2954     AutoCaller autoCaller(this);
    2955     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2956 
    2957     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2958 
    2959     *aSnapshotCount = mData->mFirstSnapshot.isNull()
    2960                           ? 0
    2961                           : mData->mFirstSnapshot->getAllChildrenCount() + 1;
    2962 
    2963     return S_OK;
    2964 }
    2965 
    2966 STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
    2967 {
    2968     CheckComArgOutPointerValid(aCurrentStateModified);
    2969 
    2970     AutoCaller autoCaller(this);
    2971     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2972 
    2973     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2974 
    2975     /* Note: for machines with no snapshots, we always return FALSE
    2976      * (mData->mCurrentStateModified will be TRUE in this case, for historical
    2977      * reasons :) */
    2978 
    2979     *aCurrentStateModified = mData->mFirstSnapshot.isNull()
    2980                             ? FALSE
    2981                             : mData->mCurrentStateModified;
    2982 
    2983     return S_OK;
    2984 }
    2985 
    2986 STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
    2987 {
    2988     CheckComArgOutSafeArrayPointerValid(aSharedFolders);
    2989 
    2990     AutoCaller autoCaller(this);
    2991     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2992 
    2993     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2994 
    2995     SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
    2996     folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
    2997 
    2998     return S_OK;
    2999 }
    3000 
    3001 STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
    3002 {
    3003     CheckComArgOutPointerValid(aClipboardMode);
    3004 
    3005     AutoCaller autoCaller(this);
    3006     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3007 
    3008     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3009 
    3010     *aClipboardMode = mHWData->mClipboardMode;
    3011 
    3012     return S_OK;
    3013 }
    3014 
    3015 STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
    3016 {
    3017     HRESULT rc = S_OK;
    3018 
    3019     AutoCaller autoCaller(this);
    3020     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3021 
    3022     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3023 
    3024     alock.release();
    3025     rc = onClipboardModeChange(aClipboardMode);
    3026     alock.acquire();
    3027     if (FAILED(rc)) return rc;
    3028 
    3029     setModified(IsModified_MachineData);
    3030     mHWData.backup();
    3031     mHWData->mClipboardMode = aClipboardMode;
    3032 
    3033     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    3034     if (Global::IsOnline(mData->mMachineState))
    3035         saveSettings(NULL);
    3036 
    3037     return S_OK;
    3038 }
    3039 
    3040 STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
    3041 {
    3042     CheckComArgOutPointerValid(aDragAndDropMode);
    3043 
    3044     AutoCaller autoCaller(this);
    3045     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3046 
    3047     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3048 
    3049     *aDragAndDropMode = mHWData->mDragAndDropMode;
    3050 
    3051     return S_OK;
    3052 }
    3053 
    3054 STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
    3055 {
    3056     HRESULT rc = S_OK;
    3057 
    3058     AutoCaller autoCaller(this);
    3059     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3060 
    3061     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3062 
    3063     alock.release();
    3064     rc = onDragAndDropModeChange(aDragAndDropMode);
    3065     alock.acquire();
    3066     if (FAILED(rc)) return rc;
    3067 
    3068     setModified(IsModified_MachineData);
    3069     mHWData.backup();
    3070     mHWData->mDragAndDropMode = aDragAndDropMode;
    3071 
    3072     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    3073     if (Global::IsOnline(mData->mMachineState))
    3074         saveSettings(NULL);
    3075 
    3076     return S_OK;
    3077 }
    3078 
    3079 STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
    3080 {
    3081     CheckComArgOutPointerValid(aPatterns);
    3082 
    3083     AutoCaller autoCaller(this);
    3084     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3085 
    3086     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3087 
    3088     try
    3089     {
    3090         mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
    3091     }
    3092     catch (...)
    3093     {
    3094         return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    3095     }
    3096 
    3097     return S_OK;
    3098 }
    3099 
    3100 STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
    3101 {
    3102     AutoCaller autoCaller(this);
    3103     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3104 
    3105     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3106 
    3107     HRESULT rc = checkStateDependency(MutableStateDep);
    3108     if (FAILED(rc)) return rc;
    3109 
    3110     setModified(IsModified_MachineData);
    3111     mHWData.backup();
    3112     mHWData->mGuestPropertyNotificationPatterns = aPatterns;
    3113     return rc;
    3114 }
    3115 
    3116 STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
    3117 {
    3118     CheckComArgOutSafeArrayPointerValid(aStorageControllers);
    3119 
    3120     AutoCaller autoCaller(this);
    3121     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3122 
    3123     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3124 
    3125     SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
    3126     ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
    3127 
    3128     return S_OK;
    3129 }
    3130 
    3131 STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
    3132 {
    3133     CheckComArgOutPointerValid(aEnabled);
    3134 
    3135     AutoCaller autoCaller(this);
    3136     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3137 
    3138     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3139 
    3140     *aEnabled = mUserData->s.fTeleporterEnabled;
    3141 
    3142     return S_OK;
    3143 }
    3144 
    3145 STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
    3146 {
    3147     AutoCaller autoCaller(this);
    3148     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3149 
    3150     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3151 
    3152     /* Only allow it to be set to true when PoweredOff or Aborted.
    3153        (Clearing it is always permitted.) */
    3154     if (    aEnabled
    3155         &&  mData->mRegistered
    3156         &&  (   !isSessionMachine()
    3157              || (   mData->mMachineState != MachineState_PoweredOff
    3158                  && mData->mMachineState != MachineState_Teleported
    3159                  && mData->mMachineState != MachineState_Aborted
    3160                 )
    3161             )
    3162        )
    3163         return setError(VBOX_E_INVALID_VM_STATE,
    3164                         tr("The machine is not powered off (state is %s)"),
    3165                         Global::stringifyMachineState(mData->mMachineState));
    3166 
    3167     setModified(IsModified_MachineData);
    3168     mUserData.backup();
    3169     mUserData->s.fTeleporterEnabled = !!aEnabled;
    3170 
    3171     return S_OK;
    3172 }
    3173 
    3174 STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
    3175 {
    3176     CheckComArgOutPointerValid(aPort);
    3177 
    3178     AutoCaller autoCaller(this);
    3179     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3180 
    3181     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3182 
    3183     *aPort = (ULONG)mUserData->s.uTeleporterPort;
    3184 
    3185     return S_OK;
    3186 }
    3187 
    3188 STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
    3189 {
    3190     if (aPort >= _64K)
    3191         return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
    3192 
    3193     AutoCaller autoCaller(this);
    3194     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3195 
    3196     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3197 
    3198     HRESULT rc = checkStateDependency(MutableStateDep);
    3199     if (FAILED(rc)) return rc;
    3200 
    3201     setModified(IsModified_MachineData);
    3202     mUserData.backup();
    3203     mUserData->s.uTeleporterPort = (uint32_t)aPort;
    3204 
    3205     return S_OK;
    3206 }
    3207 
    3208 STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
    3209 {
    3210     CheckComArgOutPointerValid(aAddress);
    3211 
    3212     AutoCaller autoCaller(this);
    3213     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3214 
    3215     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3216 
    3217     mUserData->s.strTeleporterAddress.cloneTo(aAddress);
    3218 
    3219     return S_OK;
    3220 }
    3221 
    3222 STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
    3223 {
    3224     AutoCaller autoCaller(this);
    3225     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3226 
    3227     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3228 
    3229     HRESULT rc = checkStateDependency(MutableStateDep);
    3230     if (FAILED(rc)) return rc;
    3231 
    3232     setModified(IsModified_MachineData);
    3233     mUserData.backup();
    3234     mUserData->s.strTeleporterAddress = aAddress;
    3235 
    3236     return S_OK;
    3237 }
    3238 
    3239 STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
    3240 {
    3241     CheckComArgOutPointerValid(aPassword);
    3242 
    3243     AutoCaller autoCaller(this);
    3244     HRESULT hrc = autoCaller.rc();
    3245     if (SUCCEEDED(hrc))
    3246     {
    3247         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3248         mUserData->s.strTeleporterPassword.cloneTo(aPassword);
    3249     }
    3250 
    3251     return hrc;
    3252 }
    3253 
    3254 STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
    3255 {
    3256     /*
    3257      * Hash the password first.
    3258      */
    3259     Utf8Str strPassword(aPassword);
    3260     if (!strPassword.isEmpty())
    3261     {
    3262         if (VBoxIsPasswordHashed(&strPassword))
    3263             return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
    3264         VBoxHashPassword(&strPassword);
    3265     }
    3266 
    3267     /*
    3268      * Do the update.
    3269      */
    3270     AutoCaller autoCaller(this);
    3271     HRESULT hrc = autoCaller.rc();
    3272     if (SUCCEEDED(hrc))
    3273     {
    3274         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3275         hrc = checkStateDependency(MutableStateDep);
    3276         if (SUCCEEDED(hrc))
    3277         {
    3278             setModified(IsModified_MachineData);
    3279             mUserData.backup();
    3280             mUserData->s.strTeleporterPassword = strPassword;
    3281         }
    3282     }
    3283 
    3284     return hrc;
    3285 }
    3286 
    3287 STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
    3288 {
    3289     CheckComArgOutPointerValid(aState);
    3290 
    3291     AutoCaller autoCaller(this);
    3292     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3293 
    3294     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3295 
    3296     *aState = mUserData->s.enmFaultToleranceState;
    3297     return S_OK;
    3298 }
    3299 
    3300 STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
    3301 {
    3302     AutoCaller autoCaller(this);
    3303     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3304 
    3305     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3306 
    3307     /* @todo deal with running state change. */
    3308     HRESULT rc = checkStateDependency(MutableStateDep);
    3309     if (FAILED(rc)) return rc;
    3310 
    3311     setModified(IsModified_MachineData);
    3312     mUserData.backup();
    3313     mUserData->s.enmFaultToleranceState = aState;
    3314     return S_OK;
    3315 }
    3316 
    3317 STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
    3318 {
    3319     CheckComArgOutPointerValid(aAddress);
    3320 
    3321     AutoCaller autoCaller(this);
    3322     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3323 
    3324     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3325 
    3326     mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
    3327     return S_OK;
    3328 }
    3329 
    3330 STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
    3331 {
    3332     AutoCaller autoCaller(this);
    3333     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3334 
    3335     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3336 
    3337     /* @todo deal with running state change. */
    3338     HRESULT rc = checkStateDependency(MutableStateDep);
    3339     if (FAILED(rc)) return rc;
    3340 
    3341     setModified(IsModified_MachineData);
    3342     mUserData.backup();
    3343     mUserData->s.strFaultToleranceAddress = aAddress;
    3344     return S_OK;
    3345 }
    3346 
    3347 STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
    3348 {
    3349     CheckComArgOutPointerValid(aPort);
    3350 
    3351     AutoCaller autoCaller(this);
    3352     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3353 
    3354     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3355 
    3356     *aPort = mUserData->s.uFaultTolerancePort;
    3357     return S_OK;
    3358 }
    3359 
    3360 STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
    3361 {
    3362     AutoCaller autoCaller(this);
    3363     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3364 
    3365     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3366 
    3367     /* @todo deal with running state change. */
    3368     HRESULT rc = checkStateDependency(MutableStateDep);
    3369     if (FAILED(rc)) return rc;
    3370 
    3371     setModified(IsModified_MachineData);
    3372     mUserData.backup();
    3373     mUserData->s.uFaultTolerancePort = aPort;
    3374     return S_OK;
    3375 }
    3376 
    3377 STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
    3378 {
    3379     CheckComArgOutPointerValid(aPassword);
    3380 
    3381     AutoCaller autoCaller(this);
    3382     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3383 
    3384     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3385 
    3386     mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
    3387 
    3388     return S_OK;
    3389 }
    3390 
    3391 STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
    3392 {
    3393     AutoCaller autoCaller(this);
    3394     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3395 
    3396     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3397 
    3398     /* @todo deal with running state change. */
    3399     HRESULT rc = checkStateDependency(MutableStateDep);
    3400     if (FAILED(rc)) return rc;
    3401 
    3402     setModified(IsModified_MachineData);
    3403     mUserData.backup();
    3404     mUserData->s.strFaultTolerancePassword = aPassword;
    3405 
    3406     return S_OK;
    3407 }
    3408 
    3409 STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
    3410 {
    3411     CheckComArgOutPointerValid(aInterval);
    3412 
    3413     AutoCaller autoCaller(this);
    3414     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3415 
    3416     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3417 
    3418     *aInterval = mUserData->s.uFaultToleranceInterval;
    3419     return S_OK;
    3420 }
    3421 
    3422 STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
    3423 {
    3424     AutoCaller autoCaller(this);
    3425     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3426 
    3427     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3428 
    3429     /* @todo deal with running state change. */
    3430     HRESULT rc = checkStateDependency(MutableStateDep);
    3431     if (FAILED(rc)) return rc;
    3432 
    3433     setModified(IsModified_MachineData);
    3434     mUserData.backup();
    3435     mUserData->s.uFaultToleranceInterval = aInterval;
    3436     return S_OK;
    3437 }
    3438 
    3439 STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
    3440 {
    3441     CheckComArgOutPointerValid(aEnabled);
    3442 
    3443     AutoCaller autoCaller(this);
    3444     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3445 
    3446     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3447 
    3448     *aEnabled = mUserData->s.fRTCUseUTC;
    3449 
    3450     return S_OK;
    3451 }
    3452 
    3453 STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
    3454 {
    3455     AutoCaller autoCaller(this);
    3456     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3457 
    3458     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3459 
    3460     /* Only allow it to be set to true when PoweredOff or Aborted.
    3461        (Clearing it is always permitted.) */
    3462     if (    aEnabled
    3463         &&  mData->mRegistered
    3464         &&  (   !isSessionMachine()
    3465              || (   mData->mMachineState != MachineState_PoweredOff
    3466                  && mData->mMachineState != MachineState_Teleported
    3467                  && mData->mMachineState != MachineState_Aborted
    3468                 )
    3469             )
    3470        )
    3471         return setError(VBOX_E_INVALID_VM_STATE,
    3472                         tr("The machine is not powered off (state is %s)"),
    3473                         Global::stringifyMachineState(mData->mMachineState));
    3474 
    3475     setModified(IsModified_MachineData);
    3476     mUserData.backup();
    3477     mUserData->s.fRTCUseUTC = !!aEnabled;
    3478 
    3479     return S_OK;
    3480 }
    3481 
    3482 STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
    3483 {
    3484     CheckComArgOutPointerValid(aEnabled);
    3485 
    3486     AutoCaller autoCaller(this);
    3487     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3488 
    3489     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3490 
    3491     *aEnabled = mHWData->mIOCacheEnabled;
    3492 
    3493     return S_OK;
    3494 }
    3495 
    3496 STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
    3497 {
    3498     AutoCaller autoCaller(this);
    3499     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3500 
    3501     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3502 
    3503     HRESULT rc = checkStateDependency(MutableStateDep);
    3504     if (FAILED(rc)) return rc;
    3505 
    3506     setModified(IsModified_MachineData);
    3507     mHWData.backup();
    3508     mHWData->mIOCacheEnabled = aEnabled;
    3509 
    3510     return S_OK;
    3511 }
    3512 
    3513 STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
    3514 {
    3515     CheckComArgOutPointerValid(aIOCacheSize);
    3516 
    3517     AutoCaller autoCaller(this);
    3518     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3519 
    3520     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3521 
    3522     *aIOCacheSize = mHWData->mIOCacheSize;
    3523 
    3524     return S_OK;
    3525 }
    3526 
    3527 STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG  aIOCacheSize)
    3528 {
    3529     AutoCaller autoCaller(this);
    3530     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3531 
    3532     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3533 
    3534     HRESULT rc = checkStateDependency(MutableStateDep);
    3535     if (FAILED(rc)) return rc;
    3536 
    3537     setModified(IsModified_MachineData);
    3538     mHWData.backup();
    3539     mHWData->mIOCacheSize = aIOCacheSize;
    3540 
    3541     return S_OK;
    3542 }
    3543 
    3544 
    3545 /**
    3546  *  @note Locks objects!
    3547  */
    3548 STDMETHODIMP Machine::LockMachine(ISession *aSession,
    3549                                   LockType_T lockType)
    3550 {
    3551     CheckComArgNotNull(aSession);
    3552 
    3553     AutoCaller autoCaller(this);
    3554     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3555 
    3556     /* check the session state */
    3557     SessionState_T state;
    3558     HRESULT rc = aSession->COMGETTER(State)(&state);
    3559     if (FAILED(rc)) return rc;
    3560 
    3561     if (state != SessionState_Unlocked)
    3562         return setError(VBOX_E_INVALID_OBJECT_STATE,
    3563                         tr("The given session is busy"));
    3564 
    3565     // get the client's IInternalSessionControl interface
    3566     ComPtr<IInternalSessionControl> pSessionControl = aSession;
    3567     ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
    3568                     E_INVALIDARG);
    3569 
    3570     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3571 
    3572     if (!mData->mRegistered)
    3573         return setError(E_UNEXPECTED,
    3574                         tr("The machine '%s' is not registered"),
    3575                         mUserData->s.strName.c_str());
    3576 
    3577     LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
    3578 
    3579     SessionState_T oldState = mData->mSession.mState;
    3580     /* Hack: in case the session is closing and there is a progress object
    3581      * which allows waiting for the session to be closed, take the opportunity
    3582      * and do a limited wait (max. 1 second). This helps a lot when the system
    3583      * is busy and thus session closing can take a little while. */
    3584     if (    mData->mSession.mState == SessionState_Unlocking
    3585         &&  mData->mSession.mProgress)
    3586     {
    3587         alock.release();
    3588         mData->mSession.mProgress->WaitForCompletion(1000);
    3589         alock.acquire();
    3590         LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
    3591     }
    3592 
    3593     // try again now
    3594     if (    (mData->mSession.mState == SessionState_Locked)         // machine is write-locked already (i.e. session machine exists)
    3595          && (lockType == LockType_Shared)                           // caller wants a shared link to the existing session that holds the write lock:
    3596        )
    3597     {
    3598         // OK, share the session... we are now dealing with three processes:
    3599         // 1) VBoxSVC (where this code runs);
    3600         // 2) process C: the caller's client process (who wants a shared session);
    3601         // 3) process W: the process which already holds the write lock on the machine (write-locking session)
    3602 
    3603         // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
    3604         ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
    3605         ComAssertRet(!pSessionW.isNull(), E_FAIL);
    3606         ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
    3607         AssertReturn(!pSessionMachine.isNull(), E_FAIL);
    3608 
    3609         /*
    3610          *  Release the lock before calling the client process. It's safe here
    3611          *  since the only thing to do after we get the lock again is to add
    3612          *  the remote control to the list (which doesn't directly influence
    3613          *  anything).
    3614          */
    3615         alock.release();
    3616 
    3617         // get the console of the session holding the write lock (this is a remote call)
    3618         ComPtr<IConsole> pConsoleW;
    3619         LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
    3620         rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
    3621         LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
    3622         if (FAILED(rc))
    3623             // the failure may occur w/o any error info (from RPC), so provide one
    3624             return setError(VBOX_E_VM_ERROR,
    3625                             tr("Failed to get a console object from the direct session (%Rrc)"), rc);
    3626 
    3627         ComAssertRet(!pConsoleW.isNull(), E_FAIL);
    3628 
    3629         // share the session machine and W's console with the caller's session
    3630         LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
    3631         rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
    3632         LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
    3633 
    3634         if (FAILED(rc))
    3635             // the failure may occur w/o any error info (from RPC), so provide one
    3636             return setError(VBOX_E_VM_ERROR,
    3637                             tr("Failed to assign the machine to the session (%Rrc)"), rc);
    3638         alock.acquire();
    3639 
    3640         // need to revalidate the state after acquiring the lock again
    3641         if (mData->mSession.mState != SessionState_Locked)
    3642         {
    3643             pSessionControl->Uninitialize();
    3644             return setError(VBOX_E_INVALID_SESSION_STATE,
    3645                             tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
    3646                                mUserData->s.strName.c_str());
    3647         }
    3648 
    3649         // add the caller's session to the list
    3650         mData->mSession.mRemoteControls.push_back(pSessionControl);
    3651     }
    3652     else if (    mData->mSession.mState == SessionState_Locked
    3653               || mData->mSession.mState == SessionState_Unlocking
    3654             )
    3655     {
    3656         // sharing not permitted, or machine still unlocking:
    3657         return setError(VBOX_E_INVALID_OBJECT_STATE,
    3658                         tr("The machine '%s' is already locked for a session (or being unlocked)"),
    3659                         mUserData->s.strName.c_str());
    3660     }
    3661     else
    3662     {
    3663         // machine is not locked: then write-lock the machine (create the session machine)
    3664 
    3665         // must not be busy
    3666         AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
    3667 
    3668         // get the caller's session PID
    3669         RTPROCESS pid = NIL_RTPROCESS;
    3670         AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
    3671         pSessionControl->GetPID((ULONG*)&pid);
    3672         Assert(pid != NIL_RTPROCESS);
    3673 
    3674         bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
    3675 
    3676         if (fLaunchingVMProcess)
    3677         {
    3678             // this machine is awaiting for a spawning session to be opened:
    3679             // then the calling process must be the one that got started by
    3680             // LaunchVMProcess()
    3681 
    3682             LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
    3683             LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
    3684 
    3685             if (mData->mSession.mPID != pid)
    3686                 return setError(E_ACCESSDENIED,
    3687                                 tr("An unexpected process (PID=0x%08X) has tried to lock the "
    3688                                    "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
    3689                                 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
    3690         }
    3691 
    3692         // create the mutable SessionMachine from the current machine
    3693         ComObjPtr<SessionMachine> sessionMachine;
    3694         sessionMachine.createObject();
    3695         rc = sessionMachine->init(this);
    3696         AssertComRC(rc);
    3697 
    3698         /* NOTE: doing return from this function after this point but
    3699          * before the end is forbidden since it may call SessionMachine::uninit()
    3700          * (through the ComObjPtr's destructor) which requests the VirtualBox write
    3701          * lock while still holding the Machine lock in alock so that a deadlock
    3702          * is possible due to the wrong lock order. */
    3703 
    3704         if (SUCCEEDED(rc))
    3705         {
    3706             /*
    3707              *  Set the session state to Spawning to protect against subsequent
    3708              *  attempts to open a session and to unregister the machine after
    3709              *  we release the lock.
    3710              */
    3711             SessionState_T origState = mData->mSession.mState;
    3712             mData->mSession.mState = SessionState_Spawning;
    3713 
    3714             /*
    3715              *  Release the lock before calling the client process -- it will call
    3716              *  Machine/SessionMachine methods. Releasing the lock here is quite safe
    3717              *  because the state is Spawning, so that LaunchVMProcess() and
    3718              *  LockMachine() calls will fail. This method, called before we
    3719              *  acquire the lock again, will fail because of the wrong PID.
    3720              *
    3721              *  Note that mData->mSession.mRemoteControls accessed outside
    3722              *  the lock may not be modified when state is Spawning, so it's safe.
    3723              */
    3724             alock.release();
    3725 
    3726             LogFlowThisFunc(("Calling AssignMachine()...\n"));
    3727             rc = pSessionControl->AssignMachine(sessionMachine, lockType);
    3728             LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
    3729 
    3730             /* The failure may occur w/o any error info (from RPC), so provide one */
    3731             if (FAILED(rc))
    3732                 setError(VBOX_E_VM_ERROR,
    3733                          tr("Failed to assign the machine to the session (%Rrc)"), rc);
    3734 
    3735             if (    SUCCEEDED(rc)
    3736                  && fLaunchingVMProcess
    3737                )
    3738             {
    3739                 /* complete the remote session initialization */
    3740 
    3741                 /* get the console from the direct session */
    3742                 ComPtr<IConsole> console;
    3743                 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
    3744                 ComAssertComRC(rc);
    3745 
    3746                 if (SUCCEEDED(rc) && !console)
    3747                 {
    3748                     ComAssert(!!console);
    3749                     rc = E_FAIL;
    3750                 }
    3751 
    3752                 /* assign machine & console to the remote session */
    3753                 if (SUCCEEDED(rc))
    3754                 {
    3755                     /*
    3756                      *  after LaunchVMProcess(), the first and the only
    3757                      *  entry in remoteControls is that remote session
    3758                      */
    3759                     LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
    3760                     rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
    3761                     LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
    3762 
    3763                     /* The failure may occur w/o any error info (from RPC), so provide one */
    3764                     if (FAILED(rc))
    3765                         setError(VBOX_E_VM_ERROR,
    3766                                  tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
    3767                 }
    3768 
    3769                 if (FAILED(rc))
    3770                     pSessionControl->Uninitialize();
    3771             }
    3772 
    3773             /* acquire the lock again */
    3774             alock.acquire();
    3775 
    3776             /* Restore the session state */
    3777             mData->mSession.mState = origState;
    3778         }
    3779 
    3780         // finalize spawning anyway (this is why we don't return on errors above)
    3781         if (fLaunchingVMProcess)
    3782         {
    3783             /* Note that the progress object is finalized later */
    3784             /** @todo Consider checking mData->mSession.mProgress for cancellation
    3785              *        around here.  */
    3786 
    3787             /* We don't reset mSession.mPID here because it is necessary for
    3788              * SessionMachine::uninit() to reap the child process later. */
    3789 
    3790             if (FAILED(rc))
    3791             {
    3792                 /* Close the remote session, remove the remote control from the list
    3793                  * and reset session state to Closed (@note keep the code in sync
    3794                  * with the relevant part in openSession()). */
    3795 
    3796                 Assert(mData->mSession.mRemoteControls.size() == 1);
    3797                 if (mData->mSession.mRemoteControls.size() == 1)
    3798                 {
    3799                     ErrorInfoKeeper eik;
    3800                     mData->mSession.mRemoteControls.front()->Uninitialize();
    3801                 }
    3802 
    3803                 mData->mSession.mRemoteControls.clear();
    3804                 mData->mSession.mState = SessionState_Unlocked;
    3805             }
    3806         }
    3807         else
    3808         {
    3809             /* memorize PID of the directly opened session */
    3810             if (SUCCEEDED(rc))
    3811                 mData->mSession.mPID = pid;
    3812         }
    3813 
    3814         if (SUCCEEDED(rc))
    3815         {
    3816             /* memorize the direct session control and cache IUnknown for it */
    3817             mData->mSession.mDirectControl = pSessionControl;
    3818             mData->mSession.mState = SessionState_Locked;
    3819             /* associate the SessionMachine with this Machine */
    3820             mData->mSession.mMachine = sessionMachine;
    3821 
    3822             /* request an IUnknown pointer early from the remote party for later
    3823              * identity checks (it will be internally cached within mDirectControl
    3824              * at least on XPCOM) */
    3825             ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
    3826             NOREF(unk);
    3827         }
    3828 
    3829         /* Release the lock since SessionMachine::uninit() locks VirtualBox which
    3830          * would break the lock order */
    3831         alock.release();
    3832 
    3833         /* uninitialize the created session machine on failure */
    3834         if (FAILED(rc))
    3835             sessionMachine->uninit();
    3836 
    3837     }
    3838 
    3839     if (SUCCEEDED(rc))
    3840     {
    3841         /*
    3842          *  tell the client watcher thread to update the set of
    3843          *  machines that have open sessions
    3844          */
    3845         mParent->updateClientWatcher();
    3846 
    3847         if (oldState != SessionState_Locked)
    3848             /* fire an event */
    3849             mParent->onSessionStateChange(getId(), SessionState_Locked);
    3850     }
    3851 
    3852     return rc;
    3853 }
    3854 
    3855 /**
    3856  *  @note Locks objects!
    3857  */
    3858 STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
    3859                                       IN_BSTR aFrontend,
    3860                                       IN_BSTR aEnvironment,
    3861                                       IProgress **aProgress)
    3862 {
    3863     CheckComArgStr(aFrontend);
    3864     Utf8Str strFrontend(aFrontend);
    3865     Utf8Str strEnvironment(aEnvironment);
    3866     /* "emergencystop" doesn't need the session, so skip the checks/interface
    3867      * retrieval. This code doesn't quite fit in here, but introducing a
    3868      * special API method would be even more effort, and would require explicit
    3869      * support by every API client. It's better to hide the feature a bit. */
    3870     if (strFrontend != "emergencystop")
    3871         CheckComArgNotNull(aSession);
    3872     CheckComArgOutPointerValid(aProgress);
    3873 
    3874     AutoCaller autoCaller(this);
    3875     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3876 
    3877     HRESULT rc = S_OK;
    3878     if (strFrontend.isEmpty())
    3879     {
    3880         Bstr bstrFrontend;
    3881         rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
    3882         if (FAILED(rc))
    3883             return rc;
    3884         strFrontend = bstrFrontend;
    3885         if (strFrontend.isEmpty())
    3886         {
    3887             ComPtr<ISystemProperties> systemProperties;
    3888             rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
    3889             if (FAILED(rc))
    3890                 return rc;
    3891             rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
    3892             if (FAILED(rc))
    3893                 return rc;
    3894             strFrontend = bstrFrontend;
    3895         }
    3896         /* paranoia - emergencystop is not a valid default */
    3897         if (strFrontend == "emergencystop")
    3898             strFrontend = Utf8Str::Empty;
    3899     }
    3900     /* default frontend: Qt GUI */
    3901     if (strFrontend.isEmpty())
    3902         strFrontend = "GUI/Qt";
    3903 
    3904     if (strFrontend != "emergencystop")
    3905     {
    3906         /* check the session state */
    3907         SessionState_T state;
    3908         rc = aSession->COMGETTER(State)(&state);
    3909         if (FAILED(rc))
    3910             return rc;
    3911 
    3912         if (state != SessionState_Unlocked)
    3913             return setError(VBOX_E_INVALID_OBJECT_STATE,
    3914                             tr("The given session is busy"));
    3915 
    3916         /* get the IInternalSessionControl interface */
    3917         ComPtr<IInternalSessionControl> control(aSession);
    3918         ComAssertMsgRet(!control.isNull(),
    3919                         ("No IInternalSessionControl interface"),
    3920                         E_INVALIDARG);
    3921 
    3922         /* get the teleporter enable state for the progress object init. */
    3923         BOOL fTeleporterEnabled;
    3924         rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
    3925         if (FAILED(rc))
    3926             return rc;
    3927 
    3928         /* create a progress object */
    3929         ComObjPtr<ProgressProxy> progress;
    3930         progress.createObject();
    3931         rc = progress->init(mParent,
    3932                             static_cast<IMachine*>(this),
    3933                             Bstr(tr("Starting VM")).raw(),
    3934                             TRUE /* aCancelable */,
    3935                             fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
    3936                             BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
    3937                             2 /* uFirstOperationWeight */,
    3938                             fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
    3939 
    3940         if (SUCCEEDED(rc))
    3941         {
    3942             rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
    3943             if (SUCCEEDED(rc))
    3944             {
    3945                 progress.queryInterfaceTo(aProgress);
    3946 
    3947                 /* signal the client watcher thread */
    3948                 mParent->updateClientWatcher();
    3949 
    3950                 /* fire an event */
    3951                 mParent->onSessionStateChange(getId(), SessionState_Spawning);
    3952             }
    3953         }
    3954     }
    3955     else
    3956     {
    3957         /* no progress object - either instant success or failure */
    3958         *aProgress = NULL;
    3959 
    3960         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3961 
    3962         if (mData->mSession.mState != SessionState_Locked)
    3963             return setError(VBOX_E_INVALID_OBJECT_STATE,
    3964                             tr("The machine '%s' is not locked by a session"),
    3965                             mUserData->s.strName.c_str());
    3966 
    3967         /* must have a VM process associated - do not kill normal API clients
    3968          * with an open session */
    3969         if (!Global::IsOnline(mData->mMachineState))
    3970             return setError(VBOX_E_INVALID_OBJECT_STATE,
    3971                             tr("The machine '%s' does not have a VM process"),
    3972                             mUserData->s.strName.c_str());
    3973 
    3974         /* forcibly terminate the VM process */
    3975         if (mData->mSession.mPID != NIL_RTPROCESS)
    3976             RTProcTerminate(mData->mSession.mPID);
    3977 
    3978         /* signal the client watcher thread, as most likely the client has
    3979          * been terminated */
    3980         mParent->updateClientWatcher();
    3981     }
    3982 
    3983     return rc;
    3984 }
    3985 
    3986 STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
    3987 {
    3988     if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
    3989         return setError(E_INVALIDARG,
    3990                         tr("Invalid boot position: %lu (must be in range [1, %lu])"),
    3991                         aPosition, SchemaDefs::MaxBootPosition);
    3992 
    3993     if (aDevice == DeviceType_USB)
    3994         return setError(E_NOTIMPL,
    3995                         tr("Booting from USB device is currently not supported"));
    3996 
    3997     AutoCaller autoCaller(this);
    3998     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3999 
    4000     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4001 
    4002     HRESULT rc = checkStateDependency(MutableStateDep);
    4003     if (FAILED(rc)) return rc;
    4004 
    4005     setModified(IsModified_MachineData);
    4006     mHWData.backup();
    4007     mHWData->mBootOrder[aPosition - 1] = aDevice;
    4008 
    4009     return S_OK;
    4010 }
    4011 
    4012 STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
    4013 {
    4014     if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
    4015         return setError(E_INVALIDARG,
    4016                        tr("Invalid boot position: %lu (must be in range [1, %lu])"),
    4017                        aPosition, SchemaDefs::MaxBootPosition);
    4018 
    4019     AutoCaller autoCaller(this);
    4020     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4021 
    4022     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4023 
    4024     *aDevice = mHWData->mBootOrder[aPosition - 1];
    4025 
    4026     return S_OK;
    4027 }
    4028 
    4029 STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
    4030                                    LONG aControllerPort,
    4031                                    LONG aDevice,
    4032                                    DeviceType_T aType,
    4033                                    IMedium *aMedium)
    4034 {
    4035     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
    4036                      aControllerName, aControllerPort, aDevice, aType, aMedium));
    4037 
    4038     CheckComArgStrNotEmptyOrNull(aControllerName);
    4039 
    4040     AutoCaller autoCaller(this);
    4041     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4042 
    4043     // request the host lock first, since might be calling Host methods for getting host drives;
    4044     // next, protect the media tree all the while we're in here, as well as our member variables
    4045     AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
    4046     AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4047 
    4048     HRESULT rc = checkStateDependency(MutableStateDep);
    4049     if (FAILED(rc)) return rc;
    4050 
    4051     /// @todo NEWMEDIA implicit machine registration
    4052     if (!mData->mRegistered)
    4053         return setError(VBOX_E_INVALID_OBJECT_STATE,
    4054                         tr("Cannot attach storage devices to an unregistered machine"));
    4055 
    4056     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4057 
    4058     /* Check for an existing controller. */
    4059     ComObjPtr<StorageController> ctl;
    4060     rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
    4061     if (FAILED(rc)) return rc;
    4062 
    4063     StorageControllerType_T ctrlType;
    4064     rc = ctl->COMGETTER(ControllerType)(&ctrlType);
    4065     if (FAILED(rc))
    4066         return setError(E_FAIL,
    4067                         tr("Could not get type of controller '%ls'"),
    4068                         aControllerName);
    4069 
    4070     bool fSilent = false;
    4071     Utf8Str strReconfig;
    4072 
    4073     /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
    4074     strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
    4075     if (FAILED(rc))
    4076         return rc;
    4077     if (   mData->mMachineState == MachineState_Paused
    4078         && strReconfig == "1")
    4079         fSilent = true;
    4080 
    4081     /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
    4082     bool fHotplug = false;
    4083     if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
    4084         fHotplug = true;
    4085 
    4086     if (fHotplug && !isControllerHotplugCapable(ctrlType))
    4087         return setError(VBOX_E_INVALID_VM_STATE,
    4088                         tr("Controller '%ls' does not support hotplugging"),
    4089                         aControllerName);
    4090 
    4091     // check that the port and device are not out of range
    4092     rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
    4093     if (FAILED(rc)) return rc;
    4094 
    4095     /* check if the device slot is already busy */
    4096     MediumAttachment *pAttachTemp;
    4097     if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
    4098                                       aControllerName,
    4099                                       aControllerPort,
    4100                                       aDevice)))
    4101     {
    4102         Medium *pMedium = pAttachTemp->getMedium();
    4103         if (pMedium)
    4104         {
    4105             AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
    4106             return setError(VBOX_E_OBJECT_IN_USE,
    4107                             tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
    4108                             pMedium->getLocationFull().c_str(),
    4109                             aControllerPort,
    4110                             aDevice,
    4111                             aControllerName);
    4112         }
    4113         else
    4114             return setError(VBOX_E_OBJECT_IN_USE,
    4115                             tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
    4116                             aControllerPort, aDevice, aControllerName);
    4117     }
    4118 
    4119     ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
    4120     if (aMedium && medium.isNull())
    4121         return setError(E_INVALIDARG, "The given medium pointer is invalid");
    4122 
    4123     AutoCaller mediumCaller(medium);
    4124     if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
    4125 
    4126     AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
    4127 
    4128     if (    (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
    4129          && !medium.isNull()
    4130        )
    4131         return setError(VBOX_E_OBJECT_IN_USE,
    4132                         tr("Medium '%s' is already attached to this virtual machine"),
    4133                         medium->getLocationFull().c_str());
    4134 
    4135     if (!medium.isNull())
    4136     {
    4137         MediumType_T mtype = medium->getType();
    4138         // MediumType_Readonly is also new, but only applies to DVDs and floppies.
    4139         // For DVDs it's not written to the config file, so needs no global config
    4140         // version bump. For floppies it's a new attribute "type", which is ignored
    4141         // by older VirtualBox version, so needs no global config version bump either.
    4142         // For hard disks this type is not accepted.
    4143         if (mtype == MediumType_MultiAttach)
    4144         {
    4145             // This type is new with VirtualBox 4.0 and therefore requires settings
    4146             // version 1.11 in the settings backend. Unfortunately it is not enough to do
    4147             // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
    4148             // two reasons: The medium type is a property of the media registry tree, which
    4149             // can reside in the global config file (for pre-4.0 media); we would therefore
    4150             // possibly need to bump the global config version. We don't want to do that though
    4151             // because that might make downgrading to pre-4.0 impossible.
    4152             // As a result, we can only use these two new types if the medium is NOT in the
    4153             // global registry:
    4154             const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
    4155             if (    medium->isInRegistry(uuidGlobalRegistry)
    4156                  || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
    4157                )
    4158                 return setError(VBOX_E_INVALID_OBJECT_STATE,
    4159                                 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
    4160                                    "to machines that were created with VirtualBox 4.0 or later"),
    4161                                 medium->getLocationFull().c_str());
    4162         }
    4163     }
    4164 
    4165     bool fIndirect = false;
    4166     if (!medium.isNull())
    4167         fIndirect = medium->isReadOnly();
    4168     bool associate = true;
    4169 
    4170     do
    4171     {
    4172         if (    aType == DeviceType_HardDisk
    4173              && mMediaData.isBackedUp())
    4174         {
    4175             const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
    4176 
    4177             /* check if the medium was attached to the VM before we started
    4178              * changing attachments in which case the attachment just needs to
    4179              * be restored */
    4180             if ((pAttachTemp = findAttachment(oldAtts, medium)))
    4181             {
    4182                 AssertReturn(!fIndirect, E_FAIL);
    4183 
    4184                 /* see if it's the same bus/channel/device */
    4185                 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
    4186                 {
    4187                     /* the simplest case: restore the whole attachment
    4188                      * and return, nothing else to do */
    4189                     mMediaData->mAttachments.push_back(pAttachTemp);
    4190 
    4191                     /* Reattach the medium to the VM. */
    4192                     if (fHotplug || fSilent)
    4193                     {
    4194                         mediumLock.release();
    4195                         treeLock.release();
    4196                         alock.release();
    4197 
    4198                         MediumLockList *pMediumLockList(new MediumLockList());
    4199 
    4200                         rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
    4201                                                           true /* fMediumLockWrite */,
    4202                                                           NULL,
    4203                                                           *pMediumLockList);
    4204                         alock.acquire();
    4205                         if (FAILED(rc))
    4206                             delete pMediumLockList;
    4207                         else
    4208                         {
    4209                             mData->mSession.mLockedMedia.Unlock();
    4210                             alock.release();
    4211                             rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
    4212                             mData->mSession.mLockedMedia.Lock();
    4213                             alock.acquire();
    4214                         }
    4215                         alock.release();
    4216 
    4217                         if (SUCCEEDED(rc))
    4218                         {
    4219                             rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
    4220                             /* Remove lock list in case of error. */
    4221                             if (FAILED(rc))
    4222                             {
    4223                                 mData->mSession.mLockedMedia.Unlock();
    4224                                 mData->mSession.mLockedMedia.Remove(pAttachTemp);
    4225                                 mData->mSession.mLockedMedia.Lock();
    4226                             }
    4227                         }
    4228                     }
    4229 
    4230                     return S_OK;
    4231                 }
    4232 
    4233                 /* bus/channel/device differ; we need a new attachment object,
    4234                  * but don't try to associate it again */
    4235                 associate = false;
    4236                 break;
    4237             }
    4238         }
    4239 
    4240         /* go further only if the attachment is to be indirect */
    4241         if (!fIndirect)
    4242             break;
    4243 
    4244         /* perform the so called smart attachment logic for indirect
    4245          * attachments. Note that smart attachment is only applicable to base
    4246          * hard disks. */
    4247 
    4248         if (medium->getParent().isNull())
    4249         {
    4250             /* first, investigate the backup copy of the current hard disk
    4251              * attachments to make it possible to re-attach existing diffs to
    4252              * another device slot w/o losing their contents */
    4253             if (mMediaData.isBackedUp())
    4254             {
    4255                 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
    4256 
    4257                 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
    4258                 uint32_t foundLevel = 0;
    4259 
    4260                 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
    4261                      it != oldAtts.end();
    4262                      ++it)
    4263                 {
    4264                     uint32_t level = 0;
    4265                     MediumAttachment *pAttach = *it;
    4266                     ComObjPtr<Medium> pMedium = pAttach->getMedium();
    4267                     Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
    4268                     if (pMedium.isNull())
    4269                         continue;
    4270 
    4271                     if (pMedium->getBase(&level) == medium)
    4272                     {
    4273                         /* skip the hard disk if its currently attached (we
    4274                          * cannot attach the same hard disk twice) */
    4275                         if (findAttachment(mMediaData->mAttachments,
    4276                                            pMedium))
    4277                             continue;
    4278 
    4279                         /* matched device, channel and bus (i.e. attached to the
    4280                          * same place) will win and immediately stop the search;
    4281                          * otherwise the attachment that has the youngest
    4282                          * descendant of medium will be used
    4283                          */
    4284                         if (pAttach->matches(aControllerName, aControllerPort, aDevice))
    4285                         {
    4286                             /* the simplest case: restore the whole attachment
    4287                              * and return, nothing else to do */
    4288                             mMediaData->mAttachments.push_back(*it);
    4289 
    4290                             /* Reattach the medium to the VM. */
    4291                             if (fHotplug || fSilent)
    4292                             {
    4293                                 mediumLock.release();
    4294                                 treeLock.release();
    4295                                 alock.release();
    4296 
    4297                                 MediumLockList *pMediumLockList(new MediumLockList());
    4298 
    4299                                 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
    4300                                                                   true /* fMediumLockWrite */,
    4301                                                                   NULL,
    4302                                                                   *pMediumLockList);
    4303                                 alock.acquire();
    4304                                 if (FAILED(rc))
    4305                                     delete pMediumLockList;
    4306                                 else
    4307                                 {
    4308                                     mData->mSession.mLockedMedia.Unlock();
    4309                                     alock.release();
    4310                                     rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
    4311                                     mData->mSession.mLockedMedia.Lock();
    4312                                     alock.acquire();
    4313                                 }
    4314                                 alock.release();
    4315 
    4316                                 if (SUCCEEDED(rc))
    4317                                 {
    4318                                     rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
    4319                                     /* Remove lock list in case of error. */
    4320                                     if (FAILED(rc))
    4321                                     {
    4322                                         mData->mSession.mLockedMedia.Unlock();
    4323                                         mData->mSession.mLockedMedia.Remove(pAttachTemp);
    4324                                         mData->mSession.mLockedMedia.Lock();
    4325                                     }
    4326                                 }
    4327                             }
    4328 
    4329                             return S_OK;
    4330                         }
    4331                         else if (    foundIt == oldAtts.end()
    4332                                   || level > foundLevel /* prefer younger */
    4333                                 )
    4334                         {
    4335                             foundIt = it;
    4336                             foundLevel = level;
    4337                         }
    4338                     }
    4339                 }
    4340 
    4341                 if (foundIt != oldAtts.end())
    4342                 {
    4343                     /* use the previously attached hard disk */
    4344                     medium = (*foundIt)->getMedium();
    4345                     mediumCaller.attach(medium);
    4346                     if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
    4347                     mediumLock.attach(medium);
    4348                     /* not implicit, doesn't require association with this VM */
    4349                     fIndirect = false;
    4350                     associate = false;
    4351                     /* go right to the MediumAttachment creation */
    4352                     break;
    4353                 }
    4354             }
    4355 
    4356             /* must give up the medium lock and medium tree lock as below we
    4357              * go over snapshots, which needs a lock with higher lock order. */
    4358             mediumLock.release();
    4359             treeLock.release();
    4360 
    4361             /* then, search through snapshots for the best diff in the given
    4362              * hard disk's chain to base the new diff on */
    4363 
    4364             ComObjPtr<Medium> base;
    4365             ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
    4366             while (snap)
    4367             {
    4368                 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
    4369 
    4370                 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
    4371 
    4372                 MediumAttachment *pAttachFound = NULL;
    4373                 uint32_t foundLevel = 0;
    4374 
    4375                 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
    4376                      it != snapAtts.end();
    4377                      ++it)
    4378                 {
    4379                     MediumAttachment *pAttach = *it;
    4380                     ComObjPtr<Medium> pMedium = pAttach->getMedium();
    4381                     Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
    4382                     if (pMedium.isNull())
    4383                         continue;
    4384 
    4385                     uint32_t level = 0;
    4386                     if (pMedium->getBase(&level) == medium)
    4387                     {
    4388                         /* matched device, channel and bus (i.e. attached to the
    4389                          * same place) will win and immediately stop the search;
    4390                          * otherwise the attachment that has the youngest
    4391                          * descendant of medium will be used
    4392                          */
    4393                         if (    pAttach->getDevice() == aDevice
    4394                              && pAttach->getPort() == aControllerPort
    4395                              && pAttach->getControllerName() == aControllerName
    4396                            )
    4397                         {
    4398                             pAttachFound = pAttach;
    4399                             break;
    4400                         }
    4401                         else if (    !pAttachFound
    4402                                   || level > foundLevel /* prefer younger */
    4403                                 )
    4404                         {
    4405                             pAttachFound = pAttach;
    4406                             foundLevel = level;
    4407                         }
    4408                     }
    4409                 }
    4410 
    4411                 if (pAttachFound)
    4412                 {
    4413                     base = pAttachFound->getMedium();
    4414                     break;
    4415                 }
    4416 
    4417                 snap = snap->getParent();
    4418             }
    4419 
    4420             /* re-lock medium tree and the medium, as we need it below */
    4421             treeLock.acquire();
    4422             mediumLock.acquire();
    4423 
    4424             /* found a suitable diff, use it as a base */
    4425             if (!base.isNull())
    4426             {
    4427                 medium = base;
    4428                 mediumCaller.attach(medium);
    4429                 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
    4430                 mediumLock.attach(medium);
    4431             }
    4432         }
    4433 
    4434         Utf8Str strFullSnapshotFolder;
    4435         calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    4436 
    4437         ComObjPtr<Medium> diff;
    4438         diff.createObject();
    4439         // store this diff in the same registry as the parent
    4440         Guid uuidRegistryParent;
    4441         if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
    4442         {
    4443             // parent image has no registry: this can happen if we're attaching a new immutable
    4444             // image that has not yet been attached (medium then points to the base and we're
    4445             // creating the diff image for the immutable, and the parent is not yet registered);
    4446             // put the parent in the machine registry then
    4447             mediumLock.release();
    4448             treeLock.release();
    4449             alock.release();
    4450             addMediumToRegistry(medium);
    4451             alock.acquire();
    4452             treeLock.acquire();
    4453             mediumLock.acquire();
    4454             medium->getFirstRegistryMachineId(uuidRegistryParent);
    4455         }
    4456         rc = diff->init(mParent,
    4457                         medium->getPreferredDiffFormat(),
    4458                         strFullSnapshotFolder.append(RTPATH_SLASH_STR),
    4459                         uuidRegistryParent);
    4460         if (FAILED(rc)) return rc;
    4461 
    4462         /* Apply the normal locking logic to the entire chain. */
    4463         MediumLockList *pMediumLockList(new MediumLockList());
    4464         mediumLock.release();
    4465         treeLock.release();
    4466         rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
    4467                                         true /* fMediumLockWrite */,
    4468                                         medium,
    4469                                         *pMediumLockList);
    4470         treeLock.acquire();
    4471         mediumLock.acquire();
    4472         if (SUCCEEDED(rc))
    4473         {
    4474             mediumLock.release();
    4475             treeLock.release();
    4476             rc = pMediumLockList->Lock();
    4477             treeLock.acquire();
    4478             mediumLock.acquire();
    4479             if (FAILED(rc))
    4480                 setError(rc,
    4481                          tr("Could not lock medium when creating diff '%s'"),
    4482                          diff->getLocationFull().c_str());
    4483             else
    4484             {
    4485                 /* will release the lock before the potentially lengthy
    4486                  * operation, so protect with the special state */
    4487                 MachineState_T oldState = mData->mMachineState;
    4488                 setMachineState(MachineState_SettingUp);
    4489 
    4490                 mediumLock.release();
    4491                 treeLock.release();
    4492                 alock.release();
    4493 
    4494                 rc = medium->createDiffStorage(diff,
    4495                                                MediumVariant_Standard,
    4496                                                pMediumLockList,
    4497                                                NULL /* aProgress */,
    4498                                                true /* aWait */);
    4499 
    4500                 alock.acquire();
    4501                 treeLock.acquire();
    4502                 mediumLock.acquire();
    4503 
    4504                 setMachineState(oldState);
    4505             }
    4506         }
    4507 
    4508         /* Unlock the media and free the associated memory. */
    4509         delete pMediumLockList;
    4510 
    4511         if (FAILED(rc)) return rc;
    4512 
    4513         /* use the created diff for the actual attachment */
    4514         medium = diff;
    4515         mediumCaller.attach(medium);
    4516         if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
    4517         mediumLock.attach(medium);
    4518     }
    4519     while (0);
    4520 
    4521     ComObjPtr<MediumAttachment> attachment;
    4522     attachment.createObject();
    4523     rc = attachment->init(this,
    4524                           medium,
    4525                           aControllerName,
    4526                           aControllerPort,
    4527                           aDevice,
    4528                           aType,
    4529                           fIndirect,
    4530                           false /* fPassthrough */,
    4531                           false /* fTempEject */,
    4532                           false /* fNonRotational */,
    4533                           false /* fDiscard */,
    4534                           Utf8Str::Empty);
    4535     if (FAILED(rc)) return rc;
    4536 
    4537     if (associate && !medium.isNull())
    4538     {
    4539         // as the last step, associate the medium to the VM
    4540         rc = medium->addBackReference(mData->mUuid);
    4541         // here we can fail because of Deleting, or being in process of creating a Diff
    4542         if (FAILED(rc)) return rc;
    4543 
    4544         mediumLock.release();
    4545         treeLock.release();
    4546         alock.release();
    4547         addMediumToRegistry(medium);
    4548         alock.acquire();
    4549         treeLock.acquire();
    4550         mediumLock.acquire();
    4551     }
    4552 
    4553     /* success: finally remember the attachment */
    4554     setModified(IsModified_Storage);
    4555     mMediaData.backup();
    4556     mMediaData->mAttachments.push_back(attachment);
    4557 
    4558     mediumLock.release();
    4559     treeLock.release();
    4560     alock.release();
    4561 
    4562     if (fHotplug || fSilent)
    4563     {
    4564         if (!medium.isNull())
    4565         {
    4566             MediumLockList *pMediumLockList(new MediumLockList());
    4567 
    4568             rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
    4569                                               true /* fMediumLockWrite */,
    4570                                               NULL,
    4571                                               *pMediumLockList);
    4572             alock.acquire();
    4573             if (FAILED(rc))
    4574                 delete pMediumLockList;
    4575             else
    4576             {
    4577                 mData->mSession.mLockedMedia.Unlock();
    4578                 alock.release();
    4579                 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
    4580                 mData->mSession.mLockedMedia.Lock();
    4581                 alock.acquire();
    4582             }
    4583             alock.release();
    4584         }
    4585 
    4586         if (SUCCEEDED(rc))
    4587         {
    4588             rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
    4589             /* Remove lock list in case of error. */
    4590             if (FAILED(rc))
    4591             {
    4592                 mData->mSession.mLockedMedia.Unlock();
    4593                 mData->mSession.mLockedMedia.Remove(attachment);
    4594                 mData->mSession.mLockedMedia.Lock();
    4595             }
    4596         }
    4597     }
    4598 
    4599     mParent->saveModifiedRegistries();
    4600 
    4601     return rc;
    4602 }
    4603 
    4604 STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4605                                    LONG aDevice)
    4606 {
    4607     CheckComArgStrNotEmptyOrNull(aControllerName);
    4608 
    4609     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
    4610                      aControllerName, aControllerPort, aDevice));
    4611 
    4612     AutoCaller autoCaller(this);
    4613     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4614 
    4615     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4616 
    4617     HRESULT rc = checkStateDependency(MutableStateDep);
    4618     if (FAILED(rc)) return rc;
    4619 
    4620     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4621 
    4622     /* Check for an existing controller. */
    4623     ComObjPtr<StorageController> ctl;
    4624     rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
    4625     if (FAILED(rc)) return rc;
    4626 
    4627     StorageControllerType_T ctrlType;
    4628     rc = ctl->COMGETTER(ControllerType)(&ctrlType);
    4629     if (FAILED(rc))
    4630         return setError(E_FAIL,
    4631                         tr("Could not get type of controller '%ls'"),
    4632                         aControllerName);
    4633 
    4634     bool fSilent = false;
    4635     Utf8Str strReconfig;
    4636 
    4637     /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
    4638     strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
    4639     if (FAILED(rc))
    4640         return rc;
    4641     if (   mData->mMachineState == MachineState_Paused
    4642         && strReconfig == "1")
    4643         fSilent = true;
    4644 
    4645     /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
    4646     bool fHotplug = false;
    4647     if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
    4648         fHotplug = true;
    4649 
    4650     if (fHotplug && !isControllerHotplugCapable(ctrlType))
    4651         return setError(VBOX_E_INVALID_VM_STATE,
    4652                         tr("Controller '%ls' does not support hotplugging"),
    4653                         aControllerName);
    4654 
    4655     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4656                                                aControllerName,
    4657                                                aControllerPort,
    4658                                                aDevice);
    4659     if (!pAttach)
    4660         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4661                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4662                         aDevice, aControllerPort, aControllerName);
    4663 
    4664     /*
    4665      * The VM has to detach the device before we delete any implicit diffs.
    4666      * If this fails we can roll back without loosing data.
    4667      */
    4668     if (fHotplug || fSilent)
    4669     {
    4670         alock.release();
    4671         rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
    4672         alock.acquire();
    4673     }
    4674     if (FAILED(rc)) return rc;
    4675 
    4676     /* If we are here everything went well and we can delete the implicit now. */
    4677     rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
    4678 
    4679     alock.release();
    4680 
    4681     mParent->saveModifiedRegistries();
    4682 
    4683     return rc;
    4684 }
    4685 
    4686 STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4687                                         LONG aDevice, BOOL aPassthrough)
    4688 {
    4689     CheckComArgStrNotEmptyOrNull(aControllerName);
    4690 
    4691     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
    4692                      aControllerName, aControllerPort, aDevice, aPassthrough));
    4693 
    4694     AutoCaller autoCaller(this);
    4695     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4696 
    4697     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4698 
    4699     HRESULT rc = checkStateDependency(MutableStateDep);
    4700     if (FAILED(rc)) return rc;
    4701 
    4702     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4703 
    4704     if (Global::IsOnlineOrTransient(mData->mMachineState))
    4705         return setError(VBOX_E_INVALID_VM_STATE,
    4706                         tr("Invalid machine state: %s"),
    4707                         Global::stringifyMachineState(mData->mMachineState));
    4708 
    4709     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4710                                                aControllerName,
    4711                                                aControllerPort,
    4712                                                aDevice);
    4713     if (!pAttach)
    4714         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4715                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4716                         aDevice, aControllerPort, aControllerName);
    4717 
    4718 
    4719     setModified(IsModified_Storage);
    4720     mMediaData.backup();
    4721 
    4722     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    4723 
    4724     if (pAttach->getType() != DeviceType_DVD)
    4725         return setError(E_INVALIDARG,
    4726                         tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
    4727                         aDevice, aControllerPort, aControllerName);
    4728     pAttach->updatePassthrough(!!aPassthrough);
    4729 
    4730     return S_OK;
    4731 }
    4732 
    4733 STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4734                                            LONG aDevice, BOOL aTemporaryEject)
    4735 {
    4736     CheckComArgStrNotEmptyOrNull(aControllerName);
    4737 
    4738     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
    4739                      aControllerName, aControllerPort, aDevice, aTemporaryEject));
    4740 
    4741     AutoCaller autoCaller(this);
    4742     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4743 
    4744     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4745 
    4746     HRESULT rc = checkStateDependency(MutableStateDep);
    4747     if (FAILED(rc)) return rc;
    4748 
    4749     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4750                                                aControllerName,
    4751                                                aControllerPort,
    4752                                                aDevice);
    4753     if (!pAttach)
    4754         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4755                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4756                         aDevice, aControllerPort, aControllerName);
    4757 
    4758 
    4759     setModified(IsModified_Storage);
    4760     mMediaData.backup();
    4761 
    4762     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    4763 
    4764     if (pAttach->getType() != DeviceType_DVD)
    4765         return setError(E_INVALIDARG,
    4766                         tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
    4767                         aDevice, aControllerPort, aControllerName);
    4768     pAttach->updateTempEject(!!aTemporaryEject);
    4769 
    4770     return S_OK;
    4771 }
    4772 
    4773 STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4774                                           LONG aDevice, BOOL aNonRotational)
    4775 {
    4776     CheckComArgStrNotEmptyOrNull(aControllerName);
    4777 
    4778     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
    4779                      aControllerName, aControllerPort, aDevice, aNonRotational));
    4780 
    4781     AutoCaller autoCaller(this);
    4782     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4783 
    4784     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4785 
    4786     HRESULT rc = checkStateDependency(MutableStateDep);
    4787     if (FAILED(rc)) return rc;
    4788 
    4789     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4790 
    4791     if (Global::IsOnlineOrTransient(mData->mMachineState))
    4792         return setError(VBOX_E_INVALID_VM_STATE,
    4793                         tr("Invalid machine state: %s"),
    4794                         Global::stringifyMachineState(mData->mMachineState));
    4795 
    4796     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4797                                                aControllerName,
    4798                                                aControllerPort,
    4799                                                aDevice);
    4800     if (!pAttach)
    4801         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4802                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4803                         aDevice, aControllerPort, aControllerName);
    4804 
    4805 
    4806     setModified(IsModified_Storage);
    4807     mMediaData.backup();
    4808 
    4809     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    4810 
    4811     if (pAttach->getType() != DeviceType_HardDisk)
    4812         return setError(E_INVALIDARG,
    4813                         tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
    4814                         aDevice, aControllerPort, aControllerName);
    4815     pAttach->updateNonRotational(!!aNonRotational);
    4816 
    4817     return S_OK;
    4818 }
    4819 
    4820 STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4821                                               LONG aDevice, BOOL aDiscard)
    4822 {
    4823     CheckComArgStrNotEmptyOrNull(aControllerName);
    4824 
    4825     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
    4826                      aControllerName, aControllerPort, aDevice, aDiscard));
    4827 
    4828     AutoCaller autoCaller(this);
    4829     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4830 
    4831     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4832 
    4833     HRESULT rc = checkStateDependency(MutableStateDep);
    4834     if (FAILED(rc)) return rc;
    4835 
    4836     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4837 
    4838     if (Global::IsOnlineOrTransient(mData->mMachineState))
    4839         return setError(VBOX_E_INVALID_VM_STATE,
    4840                         tr("Invalid machine state: %s"),
    4841                         Global::stringifyMachineState(mData->mMachineState));
    4842 
    4843     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4844                                                aControllerName,
    4845                                                aControllerPort,
    4846                                                aDevice);
    4847     if (!pAttach)
    4848         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4849                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4850                         aDevice, aControllerPort, aControllerName);
    4851 
    4852 
    4853     setModified(IsModified_Storage);
    4854     mMediaData.backup();
    4855 
    4856     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    4857 
    4858     if (pAttach->getType() != DeviceType_HardDisk)
    4859         return setError(E_INVALIDARG,
    4860                         tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
    4861                         aDevice, aControllerPort, aControllerName);
    4862     pAttach->updateDiscard(!!aDiscard);
    4863 
    4864     return S_OK;
    4865 }
    4866 
    4867 STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4868                                                    LONG aDevice)
    4869 {
    4870     int rc = S_OK;
    4871     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
    4872                      aControllerName, aControllerPort, aDevice));
    4873 
    4874     rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
    4875 
    4876     return rc;
    4877 }
    4878 
    4879 STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
    4880                                                  LONG aDevice, IBandwidthGroup *aBandwidthGroup)
    4881 {
    4882     CheckComArgStrNotEmptyOrNull(aControllerName);
    4883 
    4884     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
    4885                      aControllerName, aControllerPort, aDevice));
    4886 
    4887     AutoCaller autoCaller(this);
    4888     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4889 
    4890     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4891 
    4892     HRESULT rc = checkStateDependency(MutableStateDep);
    4893     if (FAILED(rc)) return rc;
    4894 
    4895     AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
    4896 
    4897     if (Global::IsOnlineOrTransient(mData->mMachineState))
    4898         return setError(VBOX_E_INVALID_VM_STATE,
    4899                         tr("Invalid machine state: %s"),
    4900                         Global::stringifyMachineState(mData->mMachineState));
    4901 
    4902     MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
    4903                                                aControllerName,
    4904                                                aControllerPort,
    4905                                                aDevice);
    4906     if (!pAttach)
    4907         return setError(VBOX_E_OBJECT_NOT_FOUND,
    4908                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    4909                         aDevice, aControllerPort, aControllerName);
    4910 
    4911 
    4912     setModified(IsModified_Storage);
    4913     mMediaData.backup();
    4914 
    4915     ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
    4916     if (aBandwidthGroup && group.isNull())
    4917         return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
    4918 
    4919     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    4920 
    4921     const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
    4922     if (strBandwidthGroupOld.isNotEmpty())
    4923     {
    4924         /* Get the bandwidth group object and release it - this must not fail. */
    4925         ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
    4926         rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
    4927         Assert(SUCCEEDED(rc));
    4928 
    4929         pBandwidthGroupOld->release();
    4930         pAttach->updateBandwidthGroup(Utf8Str::Empty);
    4931     }
    4932 
    4933     if (!group.isNull())
    4934     {
    4935         group->reference();
    4936         pAttach->updateBandwidthGroup(group->getName());
    4937     }
    4938 
    4939     return S_OK;
    4940 }
    4941 
    4942 STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
    4943                                                 LONG    aControllerPort,
    4944                                                 LONG    aDevice,
    4945                                                 DeviceType_T aType)
    4946 {
    4947      HRESULT rc = S_OK;
    4948 
    4949      LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
    4950                       aControllerName, aControllerPort, aDevice, aType));
    4951 
    4952      rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
    4953 
    4954      return rc;
    4955 }
    4956 
    4957 
    4958 
    4959 STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
    4960                                     LONG    aControllerPort,
    4961                                     LONG    aDevice,
    4962                                     BOOL    aForce)
    4963 {
    4964      int rc = S_OK;
    4965      LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
    4966                       aControllerName, aControllerPort, aForce));
    4967 
    4968      rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
    4969 
    4970      return rc;
    4971 }
    4972 
    4973 STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
    4974                                   LONG aControllerPort,
    4975                                   LONG aDevice,
    4976                                   IMedium *aMedium,
    4977                                   BOOL aForce)
    4978 {
    4979     int rc = S_OK;
    4980     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
    4981                      aControllerName, aControllerPort, aDevice, aForce));
    4982 
    4983     CheckComArgStrNotEmptyOrNull(aControllerName);
    4984 
    4985     AutoCaller autoCaller(this);
    4986     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4987 
    4988     // request the host lock first, since might be calling Host methods for getting host drives;
    4989     // next, protect the media tree all the while we're in here, as well as our member variables
    4990     AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
    4991                                   this->lockHandle(),
    4992                                   &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4993 
    4994     ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
    4995                                                          aControllerName,
    4996                                                          aControllerPort,
    4997                                                          aDevice);
    4998     if (pAttach.isNull())
    4999         return setError(VBOX_E_OBJECT_NOT_FOUND,
    5000                         tr("No drive attached to device slot %d on port %d of controller '%ls'"),
    5001                         aDevice, aControllerPort, aControllerName);
    5002 
    5003     /* Remember previously mounted medium. The medium before taking the
    5004      * backup is not necessarily the same thing. */
    5005     ComObjPtr<Medium> oldmedium;
    5006     oldmedium = pAttach->getMedium();
    5007 
    5008     ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
    5009     if (aMedium && pMedium.isNull())
    5010         return setError(E_INVALIDARG, "The given medium pointer is invalid");
    5011 
    5012     AutoCaller mediumCaller(pMedium);
    5013     if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
    5014 
    5015     AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
    5016     if (pMedium)
    5017     {
    5018         DeviceType_T mediumType = pAttach->getType();
    5019         switch (mediumType)
    5020         {
    5021             case DeviceType_DVD:
    5022             case DeviceType_Floppy:
    5023             break;
    5024 
    5025             default:
    5026                 return setError(VBOX_E_INVALID_OBJECT_STATE,
    5027                                 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
    5028                                 aControllerPort,
    5029                                 aDevice,
    5030                                 aControllerName);
    5031         }
    5032     }
    5033 
    5034     setModified(IsModified_Storage);
    5035     mMediaData.backup();
    5036 
    5037     {
    5038         // The backup operation makes the pAttach reference point to the
    5039         // old settings. Re-get the correct reference.
    5040         pAttach = findAttachment(mMediaData->mAttachments,
    5041                                  aControllerName,
    5042                                  aControllerPort,
    5043                                  aDevice);
    5044         if (!oldmedium.isNull())
    5045             oldmedium->removeBackReference(mData->mUuid);
    5046         if (!pMedium.isNull())
    5047         {
    5048             pMedium->addBackReference(mData->mUuid);
    5049 
    5050             mediumLock.release();
    5051             multiLock.release();
    5052             addMediumToRegistry(pMedium);
    5053             multiLock.acquire();
    5054             mediumLock.acquire();
    5055         }
    5056 
    5057         AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    5058         pAttach->updateMedium(pMedium);
    5059     }
    5060 
    5061     setModified(IsModified_Storage);
    5062 
    5063     mediumLock.release();
    5064     multiLock.release();
    5065     rc = onMediumChange(pAttach, aForce);
    5066     multiLock.acquire();
    5067     mediumLock.acquire();
    5068 
    5069     /* On error roll back this change only. */
    5070     if (FAILED(rc))
    5071     {
    5072         if (!pMedium.isNull())
    5073             pMedium->removeBackReference(mData->mUuid);
    5074         pAttach = findAttachment(mMediaData->mAttachments,
    5075                                  aControllerName,
    5076                                  aControllerPort,
    5077                                  aDevice);
    5078         /* If the attachment is gone in the meantime, bail out. */
    5079         if (pAttach.isNull())
    5080             return rc;
    5081         AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    5082         if (!oldmedium.isNull())
    5083             oldmedium->addBackReference(mData->mUuid);
    5084         pAttach->updateMedium(oldmedium);
    5085     }
    5086 
    5087     mediumLock.release();
    5088     multiLock.release();
    5089 
    5090     mParent->saveModifiedRegistries();
    5091 
    5092     return rc;
    5093 }
    5094 
    5095 STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
    5096                                 LONG aControllerPort,
    5097                                 LONG aDevice,
    5098                                 IMedium **aMedium)
    5099 {
    5100     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
    5101                      aControllerName, aControllerPort, aDevice));
    5102 
    5103     CheckComArgStrNotEmptyOrNull(aControllerName);
    5104     CheckComArgOutPointerValid(aMedium);
    5105 
    5106     AutoCaller autoCaller(this);
    5107     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5108 
    5109     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5110 
    5111     *aMedium = NULL;
    5112 
    5113     ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
    5114                                                          aControllerName,
    5115                                                          aControllerPort,
    5116                                                          aDevice);
    5117     if (pAttach.isNull())
    5118         return setError(VBOX_E_OBJECT_NOT_FOUND,
    5119                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    5120                         aDevice, aControllerPort, aControllerName);
    5121 
    5122     pAttach->getMedium().queryInterfaceTo(aMedium);
    5123 
    5124     return S_OK;
    5125 }
    5126 
    5127 STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
    5128 {
    5129     CheckComArgOutPointerValid(port);
    5130     CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
    5131 
    5132     AutoCaller autoCaller(this);
    5133     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5134 
    5135     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5136 
    5137     mSerialPorts[slot].queryInterfaceTo(port);
    5138 
    5139     return S_OK;
    5140 }
    5141 
    5142 STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
    5143 {
    5144     CheckComArgOutPointerValid(port);
    5145     CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
    5146 
    5147     AutoCaller autoCaller(this);
    5148     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5149 
    5150     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5151 
    5152     mParallelPorts[slot].queryInterfaceTo(port);
    5153 
    5154     return S_OK;
    5155 }
    5156 
    5157 STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
    5158 {
    5159     CheckComArgOutPointerValid(adapter);
    5160     /* Do not assert if slot is out of range, just return the advertised
    5161        status.  testdriver/vbox.py triggers this in logVmInfo. */
    5162     if (slot >= mNetworkAdapters.size())
    5163         return setError(E_INVALIDARG,
    5164                         tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
    5165                         slot, mNetworkAdapters.size());
    5166 
    5167     AutoCaller autoCaller(this);
    5168     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5169 
    5170     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5171 
    5172     mNetworkAdapters[slot].queryInterfaceTo(adapter);
    5173 
    5174     return S_OK;
    5175 }
    5176 
    5177 STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
    5178 {
    5179     CheckComArgOutSafeArrayPointerValid(aKeys);
    5180 
    5181     AutoCaller autoCaller(this);
    5182     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5183 
    5184     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5185 
    5186     com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
    5187     int i = 0;
    5188     for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
    5189          it != mData->pMachineConfigFile->mapExtraDataItems.end();
    5190          ++it, ++i)
    5191     {
    5192         const Utf8Str &strKey = it->first;
    5193         strKey.cloneTo(&saKeys[i]);
    5194     }
    5195     saKeys.detachTo(ComSafeArrayOutArg(aKeys));
    5196 
    5197     return S_OK;
    5198   }
    5199 
    5200   /**
    5201    *  @note Locks this object for reading.
    5202    */
    5203 STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
    5204                                    BSTR *aValue)
    5205 {
    5206     CheckComArgStrNotEmptyOrNull(aKey);
    5207     CheckComArgOutPointerValid(aValue);
    5208 
    5209     AutoCaller autoCaller(this);
    5210     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5211 
    5212     /* start with nothing found */
    5213     Bstr bstrResult("");
    5214 
    5215     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5216 
    5217     settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
    5218     if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
    5219         // found:
    5220         bstrResult = it->second; // source is a Utf8Str
    5221 
    5222     /* return the result to caller (may be empty) */
    5223     bstrResult.cloneTo(aValue);
    5224 
    5225     return S_OK;
    5226 }
    5227 
    5228   /**
    5229    *  @note Locks mParent for writing + this object for writing.
    5230    */
    5231 STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
    5232 {
    5233     CheckComArgStrNotEmptyOrNull(aKey);
    5234 
    5235     AutoCaller autoCaller(this);
    5236     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5237 
    5238     Utf8Str strKey(aKey);
    5239     Utf8Str strValue(aValue);
    5240     Utf8Str strOldValue;            // empty
    5241 
    5242     // locking note: we only hold the read lock briefly to look up the old value,
    5243     // then release it and call the onExtraCanChange callbacks. There is a small
    5244     // chance of a race insofar as the callback might be called twice if two callers
    5245     // change the same key at the same time, but that's a much better solution
    5246     // than the deadlock we had here before. The actual changing of the extradata
    5247     // is then performed under the write lock and race-free.
    5248 
    5249     // look up the old value first; if nothing has changed then we need not do anything
    5250     {
    5251         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
    5252         settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
    5253         if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
    5254             strOldValue = it->second;
    5255     }
    5256 
    5257     bool fChanged;
    5258     if ((fChanged = (strOldValue != strValue)))
    5259     {
    5260         // ask for permission from all listeners outside the locks;
    5261         // onExtraDataCanChange() only briefly requests the VirtualBox
    5262         // lock to copy the list of callbacks to invoke
    5263         Bstr error;
    5264         Bstr bstrValue(aValue);
    5265 
    5266         if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
    5267         {
    5268             const char *sep = error.isEmpty() ? "" : ": ";
    5269             CBSTR err = error.raw();
    5270             LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
    5271                             sep, err));
    5272             return setError(E_ACCESSDENIED,
    5273                             tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
    5274                             aKey,
    5275                             bstrValue.raw(),
    5276                             sep,
    5277                             err);
    5278         }
    5279 
    5280         // data is changing and change not vetoed: then write it out under the lock
    5281         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5282 
    5283         if (isSnapshotMachine())
    5284         {
    5285             HRESULT rc = checkStateDependency(MutableStateDep);
    5286             if (FAILED(rc)) return rc;
    5287         }
    5288 
    5289         if (strValue.isEmpty())
    5290             mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
    5291         else
    5292             mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
    5293                 // creates a new key if needed
    5294 
    5295         bool fNeedsGlobalSaveSettings = false;
    5296         saveSettings(&fNeedsGlobalSaveSettings);
    5297 
    5298         if (fNeedsGlobalSaveSettings)
    5299         {
    5300             // save the global settings; for that we should hold only the VirtualBox lock
    5301             alock.release();
    5302             AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
    5303             mParent->saveSettings();
    5304         }
    5305     }
    5306 
    5307     // fire notification outside the lock
    5308     if (fChanged)
    5309         mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
    5310 
    5311     return S_OK;
    5312 }
    5313 
    5314 STDMETHODIMP Machine::SaveSettings()
    5315 {
    5316     AutoCaller autoCaller(this);
    5317     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5318 
    5319     AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
    5320 
    5321     /* when there was auto-conversion, we want to save the file even if
    5322      * the VM is saved */
    5323     HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
    5324     if (FAILED(rc)) return rc;
    5325 
    5326     /* the settings file path may never be null */
    5327     ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
    5328 
    5329     /* save all VM data excluding snapshots */
    5330     bool fNeedsGlobalSaveSettings = false;
    5331     rc = saveSettings(&fNeedsGlobalSaveSettings);
    5332     mlock.release();
    5333 
    5334     if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
    5335     {
    5336         // save the global settings; for that we should hold only the VirtualBox lock
    5337         AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
    5338         rc = mParent->saveSettings();
    5339     }
    5340 
    5341     return rc;
    5342 }
    5343 
    5344 STDMETHODIMP Machine::DiscardSettings()
    5345 {
    5346     AutoCaller autoCaller(this);
    5347     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5348 
    5349     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5350 
    5351     HRESULT rc = checkStateDependency(MutableStateDep);
    5352     if (FAILED(rc)) return rc;
    5353 
    5354     /*
    5355      *  during this rollback, the session will be notified if data has
    5356      *  been actually changed
    5357      */
    5358     rollback(true /* aNotify */);
    5359 
    5360     return S_OK;
    5361 }
    5362 
    5363 /** @note Locks objects! */
    5364 STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
    5365                                  ComSafeArrayOut(IMedium*, aMedia))
    5366 {
    5367     // use AutoLimitedCaller because this call is valid on inaccessible machines as well
    5368     AutoLimitedCaller autoCaller(this);
    5369     AssertComRCReturnRC(autoCaller.rc());
    5370 
    5371     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5372 
    5373     Guid id(getId());
    5374 
    5375     if (mData->mSession.mState != SessionState_Unlocked)
    5376         return setError(VBOX_E_INVALID_OBJECT_STATE,
    5377                         tr("Cannot unregister the machine '%s' while it is locked"),
    5378                         mUserData->s.strName.c_str());
    5379 
    5380     // wait for state dependents to drop to zero
    5381     ensureNoStateDependencies();
    5382 
    5383     if (!mData->mAccessible)
    5384     {
    5385         // inaccessible maschines can only be unregistered; uninitialize ourselves
    5386         // here because currently there may be no unregistered that are inaccessible
    5387         // (this state combination is not supported). Note releasing the caller and
    5388         // leaving the lock before calling uninit()
    5389         alock.release();
    5390         autoCaller.release();
    5391 
    5392         uninit();
    5393 
    5394         mParent->unregisterMachine(this, id);
    5395             // calls VirtualBox::saveSettings()
    5396 
    5397         return S_OK;
    5398     }
    5399 
    5400     HRESULT rc = S_OK;
    5401 
    5402     // discard saved state
    5403     if (mData->mMachineState == MachineState_Saved)
    5404     {
    5405         // add the saved state file to the list of files the caller should delete
    5406         Assert(!mSSData->strStateFilePath.isEmpty());
    5407         mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
    5408 
    5409         mSSData->strStateFilePath.setNull();
    5410 
    5411         // unconditionally set the machine state to powered off, we now
    5412         // know no session has locked the machine
    5413         mData->mMachineState = MachineState_PoweredOff;
    5414     }
    5415 
    5416     size_t cSnapshots = 0;
    5417     if (mData->mFirstSnapshot)
    5418         cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
    5419     if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
    5420         // fail now before we start detaching media
    5421         return setError(VBOX_E_INVALID_OBJECT_STATE,
    5422                         tr("Cannot unregister the machine '%s' because it has %d snapshots"),
    5423                            mUserData->s.strName.c_str(), cSnapshots);
    5424 
    5425     // This list collects the medium objects from all medium attachments
    5426     // which we will detach from the machine and its snapshots, in a specific
    5427     // order which allows for closing all media without getting "media in use"
    5428     // errors, simply by going through the list from the front to the back:
    5429     // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
    5430     //    and must be closed before the parent media from the snapshots, or closing the parents
    5431     //    will fail because they still have children);
    5432     // 2) media from the youngest snapshots followed by those from the parent snapshots until
    5433     //    the root ("first") snapshot of the machine.
    5434     MediaList llMedia;
    5435 
    5436     if (    !mMediaData.isNull()      // can be NULL if machine is inaccessible
    5437          && mMediaData->mAttachments.size()
    5438        )
    5439     {
    5440         // we have media attachments: detach them all and add the Medium objects to our list
    5441         if (cleanupMode != CleanupMode_UnregisterOnly)
    5442             detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
    5443         else
    5444             return setError(VBOX_E_INVALID_OBJECT_STATE,
    5445                             tr("Cannot unregister the machine '%s' because it has %d media attachments"),
    5446                             mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
    5447     }
    5448 
    5449     if (cSnapshots)
    5450     {
    5451         // autoCleanup must be true here, or we would have failed above
    5452 
    5453         // add the media from the medium attachments of the snapshots to llMedia
    5454         // as well, after the "main" machine media; Snapshot::uninitRecursively()
    5455         // calls Machine::detachAllMedia() for the snapshot machine, recursing
    5456         // into the children first
    5457 
    5458         // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
    5459         MachineState_T oldState = mData->mMachineState;
    5460         mData->mMachineState = MachineState_DeletingSnapshot;
    5461 
    5462         // make a copy of the first snapshot so the refcount does not drop to 0
    5463         // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
    5464         // because of the AutoCaller voodoo)
    5465         ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
    5466 
    5467         // GO!
    5468         pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
    5469 
    5470         mData->mMachineState = oldState;
    5471     }
    5472 
    5473     if (FAILED(rc))
    5474     {
    5475         rollbackMedia();
    5476         return rc;
    5477     }
    5478 
    5479     // commit all the media changes made above
    5480     commitMedia();
    5481 
    5482     mData->mRegistered = false;
    5483 
    5484     // machine lock no longer needed
    5485     alock.release();
    5486 
    5487     // return media to caller
    5488     SafeIfaceArray<IMedium> sfaMedia(llMedia);
    5489     sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
    5490 
    5491     mParent->unregisterMachine(this, id);
    5492             // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
    5493 
    5494     return S_OK;
    5495 }
    5496 
    5497 struct Machine::DeleteTask
    5498 {
    5499     ComObjPtr<Machine>          pMachine;
    5500     RTCList<ComPtr<IMedium> >   llMediums;
    5501     StringsList                 llFilesToDelete;
    5502     ComObjPtr<Progress>         pProgress;
    5503 };
    5504 
    5505 STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
    5506 {
    5507     LogFlowFuncEnter();
    5508 
    5509     AutoCaller autoCaller(this);
    5510     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5511 
    5512     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5513 
    5514     HRESULT rc = checkStateDependency(MutableStateDep);
    5515     if (FAILED(rc)) return rc;
    5516 
    5517     if (mData->mRegistered)
    5518         return setError(VBOX_E_INVALID_VM_STATE,
    5519                         tr("Cannot delete settings of a registered machine"));
    5520 
    5521     DeleteTask *pTask = new DeleteTask;
    5522     pTask->pMachine = this;
    5523     com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
    5524 
    5525     // collect files to delete
    5526     pTask->llFilesToDelete = mData->llFilesToDelete;            // saved states pushed here by Unregister()
    5527 
    5528     for (size_t i = 0; i < sfaMedia.size(); ++i)
    5529     {
    5530         IMedium *pIMedium(sfaMedia[i]);
    5531         ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
    5532         if (pMedium.isNull())
    5533             return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
    5534         SafeArray<BSTR> ids;
    5535         rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
    5536         if (FAILED(rc)) return rc;
    5537         /* At this point the medium should not have any back references
    5538          * anymore. If it has it is attached to another VM and *must* not
    5539          * deleted. */
    5540         if (ids.size() < 1)
    5541             pTask->llMediums.append(pMedium);
    5542     }
    5543     if (mData->pMachineConfigFile->fileExists())
    5544         pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
    5545 
    5546     pTask->pProgress.createObject();
    5547     pTask->pProgress->init(getVirtualBox(),
    5548                            static_cast<IMachine*>(this) /* aInitiator */,
    5549                            Bstr(tr("Deleting files")).raw(),
    5550                            true /* fCancellable */,
    5551                            pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1,   // cOperations
    5552                            BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
    5553 
    5554     int vrc = RTThreadCreate(NULL,
    5555                              Machine::deleteThread,
    5556                              (void*)pTask,
    5557                              0,
    5558                              RTTHREADTYPE_MAIN_WORKER,
    5559                              0,
    5560                              "MachineDelete");
    5561 
    5562     pTask->pProgress.queryInterfaceTo(aProgress);
    5563 
    5564     if (RT_FAILURE(vrc))
    5565     {
    5566         delete pTask;
    5567         return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
    5568     }
    5569 
    5570     LogFlowFuncLeave();
    5571 
    5572     return S_OK;
    5573 }
    5574 
    5575 /**
    5576  * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
    5577  * calls Machine::deleteTaskWorker() on the actual machine object.
    5578  * @param Thread
    5579  * @param pvUser
    5580  * @return
    5581  */
    5582 /*static*/
    5583 DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
    5584 {
    5585     LogFlowFuncEnter();
    5586 
    5587     DeleteTask *pTask = (DeleteTask*)pvUser;
    5588     Assert(pTask);
    5589     Assert(pTask->pMachine);
    5590     Assert(pTask->pProgress);
    5591 
    5592     HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
    5593     pTask->pProgress->notifyComplete(rc);
    5594 
    5595     delete pTask;
    5596 
    5597     LogFlowFuncLeave();
    5598 
    5599     NOREF(Thread);
    5600 
    5601     return VINF_SUCCESS;
    5602 }
    5603 
    5604 /**
    5605  * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
    5606  * @param task
    5607  * @return
    5608  */
    5609 HRESULT Machine::deleteTaskWorker(DeleteTask &task)
    5610 {
    5611     AutoCaller autoCaller(this);
    5612     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5613 
    5614     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5615 
    5616     HRESULT rc = S_OK;
    5617 
    5618     try
    5619     {
    5620         ULONG uLogHistoryCount = 3;
    5621         ComPtr<ISystemProperties> systemProperties;
    5622         rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
    5623         if (FAILED(rc)) throw rc;
    5624 
    5625         if (!systemProperties.isNull())
    5626         {
    5627             rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
    5628             if (FAILED(rc)) throw rc;
    5629         }
    5630 
    5631         MachineState_T oldState = mData->mMachineState;
    5632         setMachineState(MachineState_SettingUp);
    5633         alock.release();
    5634         for (size_t i = 0; i < task.llMediums.size(); ++i)
    5635         {
    5636             ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
    5637             {
    5638                 AutoCaller mac(pMedium);
    5639                 if (FAILED(mac.rc())) throw mac.rc();
    5640                 Utf8Str strLocation = pMedium->getLocationFull();
    5641                 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
    5642                 if (FAILED(rc)) throw rc;
    5643                 LogFunc(("Deleting file %s\n", strLocation.c_str()));
    5644             }
    5645             ComPtr<IProgress> pProgress2;
    5646             rc = pMedium->DeleteStorage(pProgress2.asOutParam());
    5647             if (FAILED(rc)) throw rc;
    5648             rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
    5649             if (FAILED(rc)) throw rc;
    5650             /* Check the result of the asynchrony process. */
    5651             LONG iRc;
    5652             rc = pProgress2->COMGETTER(ResultCode)(&iRc);
    5653             if (FAILED(rc)) throw rc;
    5654             /* If the thread of the progress object has an error, then
    5655              * retrieve the error info from there, or it'll be lost. */
    5656             if (FAILED(iRc))
    5657                 throw setError(ProgressErrorInfo(pProgress2));
    5658         }
    5659         setMachineState(oldState);
    5660         alock.acquire();
    5661 
    5662         // delete the files pushed on the task list by Machine::Delete()
    5663         // (this includes saved states of the machine and snapshots and
    5664         // medium storage files from the IMedium list passed in, and the
    5665         // machine XML file)
    5666         StringsList::const_iterator it = task.llFilesToDelete.begin();
    5667         while (it != task.llFilesToDelete.end())
    5668         {
    5669             const Utf8Str &strFile = *it;
    5670             LogFunc(("Deleting file %s\n", strFile.c_str()));
    5671             int vrc = RTFileDelete(strFile.c_str());
    5672             if (RT_FAILURE(vrc))
    5673                 throw setError(VBOX_E_IPRT_ERROR,
    5674                                tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
    5675 
    5676             ++it;
    5677             if (it == task.llFilesToDelete.end())
    5678             {
    5679                 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
    5680                 if (FAILED(rc)) throw rc;
    5681                 break;
    5682             }
    5683 
    5684             rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
    5685             if (FAILED(rc)) throw rc;
    5686         }
    5687 
    5688         /* delete the settings only when the file actually exists */
    5689         if (mData->pMachineConfigFile->fileExists())
    5690         {
    5691             /* Delete any backup or uncommitted XML files. Ignore failures.
    5692                See the fSafe parameter of xml::XmlFileWriter::write for details. */
    5693             /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
    5694             Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
    5695             RTFileDelete(otherXml.c_str());
    5696             otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
    5697             RTFileDelete(otherXml.c_str());
    5698 
    5699             /* delete the Logs folder, nothing important should be left
    5700              * there (we don't check for errors because the user might have
    5701              * some private files there that we don't want to delete) */
    5702             Utf8Str logFolder;
    5703             getLogFolder(logFolder);
    5704             Assert(logFolder.length());
    5705             if (RTDirExists(logFolder.c_str()))
    5706             {
    5707                 /* Delete all VBox.log[.N] files from the Logs folder
    5708                  * (this must be in sync with the rotation logic in
    5709                  * Console::powerUpThread()). Also, delete the VBox.png[.N]
    5710                  * files that may have been created by the GUI. */
    5711                 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
    5712                                          logFolder.c_str(), RTPATH_DELIMITER);
    5713                 RTFileDelete(log.c_str());
    5714                 log = Utf8StrFmt("%s%cVBox.png",
    5715                                  logFolder.c_str(), RTPATH_DELIMITER);
    5716                 RTFileDelete(log.c_str());
    5717                 for (int i = uLogHistoryCount; i > 0; i--)
    5718                 {
    5719                     log = Utf8StrFmt("%s%cVBox.log.%d",
    5720                                      logFolder.c_str(), RTPATH_DELIMITER, i);
    5721                     RTFileDelete(log.c_str());
    5722                     log = Utf8StrFmt("%s%cVBox.png.%d",
    5723                                      logFolder.c_str(), RTPATH_DELIMITER, i);
    5724                     RTFileDelete(log.c_str());
    5725                 }
    5726 
    5727                 RTDirRemove(logFolder.c_str());
    5728             }
    5729 
    5730             /* delete the Snapshots folder, nothing important should be left
    5731              * there (we don't check for errors because the user might have
    5732              * some private files there that we don't want to delete) */
    5733             Utf8Str strFullSnapshotFolder;
    5734             calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    5735             Assert(!strFullSnapshotFolder.isEmpty());
    5736             if (RTDirExists(strFullSnapshotFolder.c_str()))
    5737                 RTDirRemove(strFullSnapshotFolder.c_str());
    5738 
    5739             // delete the directory that contains the settings file, but only
    5740             // if it matches the VM name
    5741             Utf8Str settingsDir;
    5742             if (isInOwnDir(&settingsDir))
    5743                 RTDirRemove(settingsDir.c_str());
    5744         }
    5745 
    5746         alock.release();
    5747 
    5748         mParent->saveModifiedRegistries();
    5749     }
    5750     catch (HRESULT aRC) { rc = aRC; }
    5751 
    5752     return rc;
    5753 }
    5754 
    5755 STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
    5756 {
    5757     CheckComArgOutPointerValid(aSnapshot);
    5758 
    5759     AutoCaller autoCaller(this);
    5760     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5761 
    5762     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5763 
    5764     ComObjPtr<Snapshot> pSnapshot;
    5765     HRESULT rc;
    5766 
    5767     if (!aNameOrId || !*aNameOrId)
    5768         // null case (caller wants root snapshot): findSnapshotById() handles this
    5769         rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
    5770     else
    5771     {
    5772         Guid uuid(aNameOrId);
    5773         if (uuid.isValid())
    5774             rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
    5775         else
    5776             rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
    5777     }
    5778     pSnapshot.queryInterfaceTo(aSnapshot);
    5779 
    5780     return rc;
    5781 }
    5782 
    5783 STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
    5784 {
    5785     CheckComArgStrNotEmptyOrNull(aName);
    5786     CheckComArgStrNotEmptyOrNull(aHostPath);
    5787 
    5788     AutoCaller autoCaller(this);
    5789     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5790 
    5791     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5792 
    5793     HRESULT rc = checkStateDependency(MutableStateDep);
    5794     if (FAILED(rc)) return rc;
    5795 
    5796     Utf8Str strName(aName);
    5797 
    5798     ComObjPtr<SharedFolder> sharedFolder;
    5799     rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
    5800     if (SUCCEEDED(rc))
    5801         return setError(VBOX_E_OBJECT_IN_USE,
    5802                         tr("Shared folder named '%s' already exists"),
    5803                         strName.c_str());
    5804 
    5805     sharedFolder.createObject();
    5806     rc = sharedFolder->init(getMachine(),
    5807                             strName,
    5808                             aHostPath,
    5809                             !!aWritable,
    5810                             !!aAutoMount,
    5811                             true /* fFailOnError */);
    5812     if (FAILED(rc)) return rc;
    5813 
    5814     setModified(IsModified_SharedFolders);
    5815     mHWData.backup();
    5816     mHWData->mSharedFolders.push_back(sharedFolder);
    5817 
    5818     /* inform the direct session if any */
    5819     alock.release();
    5820     onSharedFolderChange();
    5821 
    5822     return S_OK;
    5823 }
    5824 
    5825 STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
    5826 {
    5827     CheckComArgStrNotEmptyOrNull(aName);
    5828 
    5829     AutoCaller autoCaller(this);
    5830     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5831 
    5832     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5833 
    5834     HRESULT rc = checkStateDependency(MutableStateDep);
    5835     if (FAILED(rc)) return rc;
    5836 
    5837     ComObjPtr<SharedFolder> sharedFolder;
    5838     rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
    5839     if (FAILED(rc)) return rc;
    5840 
    5841     setModified(IsModified_SharedFolders);
    5842     mHWData.backup();
    5843     mHWData->mSharedFolders.remove(sharedFolder);
    5844 
    5845     /* inform the direct session if any */
    5846     alock.release();
    5847     onSharedFolderChange();
    5848 
    5849     return S_OK;
    5850 }
    5851 
    5852 STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
    5853 {
    5854     CheckComArgOutPointerValid(aCanShow);
    5855 
    5856     /* start with No */
    5857     *aCanShow = FALSE;
    5858 
    5859     AutoCaller autoCaller(this);
    5860     AssertComRCReturnRC(autoCaller.rc());
    5861 
    5862     ComPtr<IInternalSessionControl> directControl;
    5863     {
    5864         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5865 
    5866         if (mData->mSession.mState != SessionState_Locked)
    5867             return setError(VBOX_E_INVALID_VM_STATE,
    5868                             tr("Machine is not locked for session (session state: %s)"),
    5869                             Global::stringifySessionState(mData->mSession.mState));
    5870 
    5871         directControl = mData->mSession.mDirectControl;
    5872     }
    5873 
    5874     /* ignore calls made after #OnSessionEnd() is called */
    5875     if (!directControl)
    5876         return S_OK;
    5877 
    5878     LONG64 dummy;
    5879     return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
    5880 }
    5881 
    5882 STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
    5883 {
    5884     CheckComArgOutPointerValid(aWinId);
    5885 
    5886     AutoCaller autoCaller(this);
    5887     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    5888 
    5889     ComPtr<IInternalSessionControl> directControl;
    5890     {
    5891         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5892 
    5893         if (mData->mSession.mState != SessionState_Locked)
    5894             return setError(E_FAIL,
    5895                             tr("Machine is not locked for session (session state: %s)"),
    5896                             Global::stringifySessionState(mData->mSession.mState));
    5897 
    5898         directControl = mData->mSession.mDirectControl;
    5899     }
    5900 
    5901     /* ignore calls made after #OnSessionEnd() is called */
    5902     if (!directControl)
    5903         return S_OK;
    5904 
    5905     BOOL dummy;
    5906     return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
    5907 }
    5908 
    5909 #ifdef VBOX_WITH_GUEST_PROPS
    5910 /**
    5911  * Look up a guest property in VBoxSVC's internal structures.
    5912  */
    5913 HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
    5914                                              BSTR *aValue,
    5915                                              LONG64 *aTimestamp,
    5916                                              BSTR *aFlags) const
    5917 {
    5918     using namespace guestProp;
    5919 
    5920     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5921     Utf8Str strName(aName);
    5922     HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
    5923 
    5924     if (it != mHWData->mGuestProperties.end())
    5925     {
    5926         char szFlags[MAX_FLAGS_LEN + 1];
    5927         it->second.strValue.cloneTo(aValue);
    5928         *aTimestamp = it->second.mTimestamp;
    5929         writeFlags(it->second.mFlags, szFlags);
    5930         Bstr(szFlags).cloneTo(aFlags);
    5931     }
    5932 
    5933     return S_OK;
    5934 }
    5935 
    5936 /**
    5937  * Query the VM that a guest property belongs to for the property.
    5938  * @returns E_ACCESSDENIED if the VM process is not available or not
    5939  *          currently handling queries and the lookup should then be done in
    5940  *          VBoxSVC.
    5941  */
    5942 HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
    5943                                         BSTR *aValue,
    5944                                         LONG64 *aTimestamp,
    5945                                         BSTR *aFlags) const
    5946 {
    5947     HRESULT rc;
    5948     ComPtr<IInternalSessionControl> directControl;
    5949     directControl = mData->mSession.mDirectControl;
    5950 
    5951     /* fail if we were called after #OnSessionEnd() is called.  This is a
    5952      * silly race condition. */
    5953 
    5954     if (!directControl)
    5955         rc = E_ACCESSDENIED;
    5956     else
    5957         rc = directControl->AccessGuestProperty(aName, NULL, NULL,
    5958                                                 false /* isSetter */,
    5959                                                 aValue, aTimestamp, aFlags);
    5960     return rc;
    5961 }
    5962 #endif // VBOX_WITH_GUEST_PROPS
    5963 
    5964 STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
    5965                                        BSTR *aValue,
    5966                                        LONG64 *aTimestamp,
    5967                                        BSTR *aFlags)
    5968 {
    5969 #ifndef VBOX_WITH_GUEST_PROPS
    5970     ReturnComNotImplemented();
    5971 #else // VBOX_WITH_GUEST_PROPS
    5972     CheckComArgStrNotEmptyOrNull(aName);
    5973     CheckComArgOutPointerValid(aValue);
    5974     CheckComArgOutPointerValid(aTimestamp);
    5975     CheckComArgOutPointerValid(aFlags);
    5976 
    5977     AutoCaller autoCaller(this);
    5978     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5979 
    5980     HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
    5981     if (rc == E_ACCESSDENIED)
    5982         /* The VM is not running or the service is not (yet) accessible */
    5983         rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
    5984     return rc;
    5985 #endif // VBOX_WITH_GUEST_PROPS
    5986 }
    5987 
    5988 STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
    5989 {
    5990     LONG64 dummyTimestamp;
    5991     Bstr dummyFlags;
    5992     return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
    5993 }
    5994 
    5995 STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
    5996 {
    5997     Bstr dummyValue;
    5998     Bstr dummyFlags;
    5999     return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
    6000 }
    6001 
    6002 #ifdef VBOX_WITH_GUEST_PROPS
    6003 /**
    6004  * Set a guest property in VBoxSVC's internal structures.
    6005  */
    6006 HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
    6007                                            IN_BSTR aFlags)
    6008 {
    6009     using namespace guestProp;
    6010 
    6011     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6012     HRESULT rc = S_OK;
    6013 
    6014     rc = checkStateDependency(MutableStateDep);
    6015     if (FAILED(rc)) return rc;
    6016 
    6017     try
    6018     {
    6019         Utf8Str utf8Name(aName);
    6020         Utf8Str utf8Flags(aFlags);
    6021         uint32_t fFlags = NILFLAG;
    6022         if (   aFlags != NULL
    6023             && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
    6024             return setError(E_INVALIDARG,
    6025                             tr("Invalid guest property flag values: '%ls'"),
    6026                             aFlags);
    6027 
    6028         bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
    6029         HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
    6030         if (it == mHWData->mGuestProperties.end())
    6031         {
    6032             if (!fDelete)
    6033             {
    6034                 setModified(IsModified_MachineData);
    6035                 mHWData.backupEx();
    6036 
    6037                 RTTIMESPEC time;
    6038                 HWData::GuestProperty prop;
    6039                 prop.strValue   = aValue;
    6040                 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
    6041                 prop.mFlags     = fFlags;
    6042                 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
    6043             }
    6044         }
    6045         else
    6046         {
    6047             if (it->second.mFlags & (RDONLYHOST))
    6048             {
    6049                 rc = setError(E_ACCESSDENIED,
    6050                               tr("The property '%ls' cannot be changed by the host"),
    6051                               aName);
    6052             }
    6053             else
    6054             {
    6055                 setModified(IsModified_MachineData);
    6056                 mHWData.backupEx();
    6057 
    6058                 /* The backupEx() operation invalidates our iterator,
    6059                  * so get a new one. */
    6060                 it = mHWData->mGuestProperties.find(utf8Name);
    6061                 Assert(it != mHWData->mGuestProperties.end());
    6062 
    6063                 if (!fDelete)
    6064                 {
    6065                     RTTIMESPEC time;
    6066                     it->second.strValue   = aValue;
    6067                     it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
    6068                     it->second.mFlags     = fFlags;
    6069                 }
    6070                 else
    6071                     mHWData->mGuestProperties.erase(it);
    6072             }
    6073         }
    6074 
    6075         if (   SUCCEEDED(rc)
    6076             && (   mHWData->mGuestPropertyNotificationPatterns.isEmpty()
    6077                 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
    6078                                                 RTSTR_MAX,
    6079                                                 utf8Name.c_str(),
    6080                                                 RTSTR_MAX,
    6081                                                 NULL)
    6082                )
    6083            )
    6084         {
    6085             alock.release();
    6086 
    6087             mParent->onGuestPropertyChange(mData->mUuid, aName,
    6088                                            aValue ? aValue : Bstr("").raw(),
    6089                                            aFlags ? aFlags : Bstr("").raw());
    6090         }
    6091     }
    6092     catch (std::bad_alloc &)
    6093     {
    6094         rc = E_OUTOFMEMORY;
    6095     }
    6096 
    6097     return rc;
    6098 }
    6099 
    6100 /**
    6101  * Set a property on the VM that that property belongs to.
    6102  * @returns E_ACCESSDENIED if the VM process is not available or not
    6103  *          currently handling queries and the setting should then be done in
    6104  *          VBoxSVC.
    6105  */
    6106 HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
    6107                                       IN_BSTR aFlags)
    6108 {
    6109     HRESULT rc;
    6110 
    6111     try
    6112     {
    6113         ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
    6114 
    6115         BSTR dummy = NULL; /* will not be changed (setter) */
    6116         LONG64 dummy64;
    6117         if (!directControl)
    6118             rc = E_ACCESSDENIED;
    6119         else
    6120             /** @todo Fix when adding DeleteGuestProperty(), see defect. */
    6121             rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
    6122                                                     true /* isSetter */,
    6123                                                     &dummy, &dummy64, &dummy);
    6124     }
    6125     catch (std::bad_alloc &)
    6126     {
    6127         rc = E_OUTOFMEMORY;
    6128     }
    6129 
    6130     return rc;
    6131 }
    6132 #endif // VBOX_WITH_GUEST_PROPS
    6133 
    6134 STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
    6135                                        IN_BSTR aFlags)
    6136 {
    6137 #ifndef VBOX_WITH_GUEST_PROPS
    6138     ReturnComNotImplemented();
    6139 #else // VBOX_WITH_GUEST_PROPS
    6140     CheckComArgStrNotEmptyOrNull(aName);
    6141     CheckComArgMaybeNull(aFlags);
    6142     CheckComArgMaybeNull(aValue);
    6143 
    6144     AutoCaller autoCaller(this);
    6145     if (FAILED(autoCaller.rc()))
    6146         return autoCaller.rc();
    6147 
    6148     HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
    6149     if (rc == E_ACCESSDENIED)
    6150         /* The VM is not running or the service is not (yet) accessible */
    6151         rc = setGuestPropertyToService(aName, aValue, aFlags);
    6152     return rc;
    6153 #endif // VBOX_WITH_GUEST_PROPS
    6154 }
    6155 
    6156 STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
    6157 {
    6158     return SetGuestProperty(aName, aValue, NULL);
    6159 }
    6160 
    6161 STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
    6162 {
    6163     return SetGuestProperty(aName, NULL, NULL);
    6164 }
    6165 
    6166 #ifdef VBOX_WITH_GUEST_PROPS
    6167 /**
    6168  * Enumerate the guest properties in VBoxSVC's internal structures.
    6169  */
    6170 HRESULT Machine::enumerateGuestPropertiesInService
    6171                 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
    6172                  ComSafeArrayOut(BSTR, aValues),
    6173                  ComSafeArrayOut(LONG64, aTimestamps),
    6174                  ComSafeArrayOut(BSTR, aFlags))
    6175 {
    6176     using namespace guestProp;
    6177 
    6178     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6179     Utf8Str strPatterns(aPatterns);
    6180 
    6181     HWData::GuestPropertyMap propMap;
    6182 
    6183     /*
    6184      * Look for matching patterns and build up a list.
    6185      */
    6186     HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
    6187     while (it != mHWData->mGuestProperties.end())
    6188     {
    6189         if (   strPatterns.isEmpty()
    6190             || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
    6191                                             RTSTR_MAX,
    6192                                             it->first.c_str(),
    6193                                             RTSTR_MAX,
    6194                                             NULL)
    6195            )
    6196         {
    6197             propMap.insert(*it);
    6198         }
    6199 
    6200         it++;
    6201     }
    6202 
    6203     alock.release();
    6204 
    6205     /*
    6206      * And build up the arrays for returning the property information.
    6207      */
    6208     size_t cEntries = propMap.size();
    6209     SafeArray<BSTR> names(cEntries);
    6210     SafeArray<BSTR> values(cEntries);
    6211     SafeArray<LONG64> timestamps(cEntries);
    6212     SafeArray<BSTR> flags(cEntries);
    6213     size_t iProp = 0;
    6214 
    6215     it = propMap.begin();
    6216     while (it != propMap.end())
    6217     {
    6218          char szFlags[MAX_FLAGS_LEN + 1];
    6219          it->first.cloneTo(&names[iProp]);
    6220          it->second.strValue.cloneTo(&values[iProp]);
    6221          timestamps[iProp] = it->second.mTimestamp;
    6222          writeFlags(it->second.mFlags, szFlags);
    6223          Bstr(szFlags).cloneTo(&flags[iProp++]);
    6224          it++;
    6225     }
    6226     names.detachTo(ComSafeArrayOutArg(aNames));
    6227     values.detachTo(ComSafeArrayOutArg(aValues));
    6228     timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
    6229     flags.detachTo(ComSafeArrayOutArg(aFlags));
    6230     return S_OK;
    6231 }
    6232 
    6233 /**
    6234  * Enumerate the properties managed by a VM.
    6235  * @returns E_ACCESSDENIED if the VM process is not available or not
    6236  *          currently handling queries and the setting should then be done in
    6237  *          VBoxSVC.
    6238  */
    6239 HRESULT Machine::enumerateGuestPropertiesOnVM
    6240                 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
    6241                  ComSafeArrayOut(BSTR, aValues),
    6242                  ComSafeArrayOut(LONG64, aTimestamps),
    6243                  ComSafeArrayOut(BSTR, aFlags))
    6244 {
    6245     HRESULT rc;
    6246     ComPtr<IInternalSessionControl> directControl;
    6247     directControl = mData->mSession.mDirectControl;
    6248 
    6249     if (!directControl)
    6250         rc = E_ACCESSDENIED;
    6251     else
    6252         rc = directControl->EnumerateGuestProperties
    6253                      (aPatterns, ComSafeArrayOutArg(aNames),
    6254                       ComSafeArrayOutArg(aValues),
    6255                       ComSafeArrayOutArg(aTimestamps),
    6256                       ComSafeArrayOutArg(aFlags));
    6257     return rc;
    6258 }
    6259 #endif // VBOX_WITH_GUEST_PROPS
    6260 
    6261 STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
    6262                                                ComSafeArrayOut(BSTR, aNames),
    6263                                                ComSafeArrayOut(BSTR, aValues),
    6264                                                ComSafeArrayOut(LONG64, aTimestamps),
    6265                                                ComSafeArrayOut(BSTR, aFlags))
    6266 {
    6267 #ifndef VBOX_WITH_GUEST_PROPS
    6268     ReturnComNotImplemented();
    6269 #else // VBOX_WITH_GUEST_PROPS
    6270     CheckComArgMaybeNull(aPatterns);
    6271     CheckComArgOutSafeArrayPointerValid(aNames);
    6272     CheckComArgOutSafeArrayPointerValid(aValues);
    6273     CheckComArgOutSafeArrayPointerValid(aTimestamps);
    6274     CheckComArgOutSafeArrayPointerValid(aFlags);
    6275 
    6276     AutoCaller autoCaller(this);
    6277     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6278 
    6279     HRESULT rc = enumerateGuestPropertiesOnVM
    6280                      (aPatterns, ComSafeArrayOutArg(aNames),
    6281                       ComSafeArrayOutArg(aValues),
    6282                       ComSafeArrayOutArg(aTimestamps),
    6283                       ComSafeArrayOutArg(aFlags));
    6284     if (rc == E_ACCESSDENIED)
    6285         /* The VM is not running or the service is not (yet) accessible */
    6286         rc = enumerateGuestPropertiesInService
    6287                      (aPatterns, ComSafeArrayOutArg(aNames),
    6288                       ComSafeArrayOutArg(aValues),
    6289                       ComSafeArrayOutArg(aTimestamps),
    6290                       ComSafeArrayOutArg(aFlags));
    6291     return rc;
    6292 #endif // VBOX_WITH_GUEST_PROPS
    6293 }
    6294 
    6295 STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
    6296                                                        ComSafeArrayOut(IMediumAttachment*, aAttachments))
    6297 {
    6298     MediaData::AttachmentList atts;
    6299 
    6300     HRESULT rc = getMediumAttachmentsOfController(aName, atts);
    6301     if (FAILED(rc)) return rc;
    6302 
    6303     SafeIfaceArray<IMediumAttachment> attachments(atts);
    6304     attachments.detachTo(ComSafeArrayOutArg(aAttachments));
    6305 
    6306     return S_OK;
    6307 }
    6308 
    6309 STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
    6310                                           LONG aControllerPort,
    6311                                           LONG aDevice,
    6312                                           IMediumAttachment **aAttachment)
    6313 {
    6314     LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
    6315                      aControllerName, aControllerPort, aDevice));
    6316 
    6317     CheckComArgStrNotEmptyOrNull(aControllerName);
    6318     CheckComArgOutPointerValid(aAttachment);
    6319 
    6320     AutoCaller autoCaller(this);
    6321     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6322 
    6323     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6324 
    6325     *aAttachment = NULL;
    6326 
    6327     ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
    6328                                                          aControllerName,
    6329                                                          aControllerPort,
    6330                                                          aDevice);
    6331     if (pAttach.isNull())
    6332         return setError(VBOX_E_OBJECT_NOT_FOUND,
    6333                         tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
    6334                         aDevice, aControllerPort, aControllerName);
    6335 
    6336     pAttach.queryInterfaceTo(aAttachment);
    6337 
    6338     return S_OK;
    6339 }
    6340 
    6341 STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
    6342                                            StorageBus_T aConnectionType,
    6343                                            IStorageController **controller)
    6344 {
    6345     CheckComArgStrNotEmptyOrNull(aName);
    6346 
    6347     if (   (aConnectionType <= StorageBus_Null)
    6348         || (aConnectionType >  StorageBus_SAS))
    6349         return setError(E_INVALIDARG,
    6350                         tr("Invalid connection type: %d"),
    6351                         aConnectionType);
    6352 
    6353     AutoCaller autoCaller(this);
    6354     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6355 
    6356     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6357 
    6358     HRESULT rc = checkStateDependency(MutableStateDep);
    6359     if (FAILED(rc)) return rc;
    6360 
    6361     /* try to find one with the name first. */
    6362     ComObjPtr<StorageController> ctrl;
    6363 
    6364     rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
    6365     if (SUCCEEDED(rc))
    6366         return setError(VBOX_E_OBJECT_IN_USE,
    6367                         tr("Storage controller named '%ls' already exists"),
    6368                         aName);
    6369 
    6370     ctrl.createObject();
    6371 
    6372     /* get a new instance number for the storage controller */
    6373     ULONG ulInstance = 0;
    6374     bool fBootable = true;
    6375     for (StorageControllerList::const_iterator it = mStorageControllers->begin();
    6376          it != mStorageControllers->end();
    6377          ++it)
    6378     {
    6379         if ((*it)->getStorageBus() == aConnectionType)
    6380         {
    6381             ULONG ulCurInst = (*it)->getInstance();
    6382 
    6383             if (ulCurInst >= ulInstance)
    6384                 ulInstance = ulCurInst + 1;
    6385 
    6386             /* Only one controller of each type can be marked as bootable. */
    6387             if ((*it)->getBootable())
    6388                 fBootable = false;
    6389         }
    6390     }
    6391 
    6392     rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
    6393     if (FAILED(rc)) return rc;
    6394 
    6395     setModified(IsModified_Storage);
    6396     mStorageControllers.backup();
    6397     mStorageControllers->push_back(ctrl);
    6398 
    6399     ctrl.queryInterfaceTo(controller);
    6400 
    6401     /* inform the direct session if any */
    6402     alock.release();
    6403     onStorageControllerChange();
    6404 
    6405     return S_OK;
    6406 }
    6407 
    6408 STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
    6409                                                  IStorageController **aStorageController)
    6410 {
    6411     CheckComArgStrNotEmptyOrNull(aName);
    6412 
    6413     AutoCaller autoCaller(this);
    6414     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6415 
    6416     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6417 
    6418     ComObjPtr<StorageController> ctrl;
    6419 
    6420     HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
    6421     if (SUCCEEDED(rc))
    6422         ctrl.queryInterfaceTo(aStorageController);
    6423 
    6424     return rc;
    6425 }
    6426 
    6427 STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
    6428                                                      IStorageController **aStorageController)
    6429 {
    6430     AutoCaller autoCaller(this);
    6431     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6432 
    6433     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6434 
    6435     for (StorageControllerList::const_iterator it = mStorageControllers->begin();
    6436          it != mStorageControllers->end();
    6437          ++it)
    6438     {
    6439         if ((*it)->getInstance() == aInstance)
    6440         {
    6441             (*it).queryInterfaceTo(aStorageController);
    6442             return S_OK;
    6443         }
    6444     }
    6445 
    6446     return setError(VBOX_E_OBJECT_NOT_FOUND,
    6447                     tr("Could not find a storage controller with instance number '%lu'"),
    6448                     aInstance);
    6449 }
    6450 
    6451 STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
    6452 {
    6453     AutoCaller autoCaller(this);
    6454     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6455 
    6456     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6457 
    6458     HRESULT rc = checkStateDependency(MutableStateDep);
    6459     if (FAILED(rc)) return rc;
    6460 
    6461     ComObjPtr<StorageController> ctrl;
    6462 
    6463     rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
    6464     if (SUCCEEDED(rc))
    6465     {
    6466         /* Ensure that only one controller of each type is marked as bootable. */
    6467         if (fBootable == TRUE)
    6468         {
    6469             for (StorageControllerList::const_iterator it = mStorageControllers->begin();
    6470                  it != mStorageControllers->end();
    6471                  ++it)
    6472             {
    6473                 ComObjPtr<StorageController> aCtrl = (*it);
    6474 
    6475                 if (   (aCtrl->getName() != Utf8Str(aName))
    6476                     && aCtrl->getBootable() == TRUE
    6477                     && aCtrl->getStorageBus() == ctrl->getStorageBus()
    6478                     && aCtrl->getControllerType() == ctrl->getControllerType())
    6479                 {
    6480                     aCtrl->setBootable(FALSE);
    6481                     break;
    6482                 }
    6483             }
    6484         }
    6485 
    6486         if (SUCCEEDED(rc))
    6487         {
    6488             ctrl->setBootable(fBootable);
    6489             setModified(IsModified_Storage);
    6490         }
    6491     }
    6492 
    6493     if (SUCCEEDED(rc))
    6494     {
    6495         /* inform the direct session if any */
    6496         alock.release();
    6497         onStorageControllerChange();
    6498     }
    6499 
    6500     return rc;
    6501 }
    6502 
    6503 STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
    6504 {
    6505     CheckComArgStrNotEmptyOrNull(aName);
    6506 
    6507     AutoCaller autoCaller(this);
    6508     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6509 
    6510     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6511 
    6512     HRESULT rc = checkStateDependency(MutableStateDep);
    6513     if (FAILED(rc)) return rc;
    6514 
    6515     ComObjPtr<StorageController> ctrl;
    6516     rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
    6517     if (FAILED(rc)) return rc;
    6518 
    6519     {
    6520         /* find all attached devices to the appropriate storage controller and detach them all */
    6521         // make a temporary list because detachDevice invalidates iterators into
    6522         // mMediaData->mAttachments
    6523         MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
    6524 
    6525         for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
    6526              it != llAttachments2.end();
    6527              ++it)
    6528         {
    6529             MediumAttachment *pAttachTemp = *it;
    6530 
    6531             AutoCaller localAutoCaller(pAttachTemp);
    6532             if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
    6533 
    6534             AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
    6535 
    6536             if (pAttachTemp->getControllerName() == aName)
    6537             {
    6538                 rc = detachDevice(pAttachTemp, alock, NULL);
    6539                 if (FAILED(rc)) return rc;
    6540             }
    6541         }
    6542     }
    6543 
    6544     /* We can remove it now. */
    6545     setModified(IsModified_Storage);
    6546     mStorageControllers.backup();
    6547 
    6548     ctrl->unshare();
    6549 
    6550     mStorageControllers->remove(ctrl);
    6551 
    6552     /* inform the direct session if any */
    6553     alock.release();
    6554     onStorageControllerChange();
    6555 
    6556     return S_OK;
    6557 }
    6558 
    6559 STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
    6560                                        IUSBController **controller)
    6561 {
    6562     if (   (aType <= USBControllerType_Null)
    6563         || (aType >= USBControllerType_Last))
    6564         return setError(E_INVALIDARG,
    6565                         tr("Invalid USB controller type: %d"),
    6566                         aType);
    6567 
    6568     AutoCaller autoCaller(this);
    6569     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6570 
    6571     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6572 
    6573     HRESULT rc = checkStateDependency(MutableStateDep);
    6574     if (FAILED(rc)) return rc;
    6575 
    6576     /* try to find one with the same type first. */
    6577     ComObjPtr<USBController> ctrl;
    6578 
    6579     rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
    6580     if (SUCCEEDED(rc))
    6581         return setError(VBOX_E_OBJECT_IN_USE,
    6582                         tr("USB controller named '%ls' already exists"),
    6583                         aName);
    6584 
    6585     /* Check that we don't exceed the maximum number of USB controllers for the given type. */
    6586     ULONG maxInstances;
    6587     rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
    6588     if (FAILED(rc))
    6589         return rc;
    6590 
    6591     ULONG cInstances = getUSBControllerCountByType(aType);
    6592     if (cInstances >= maxInstances)
    6593         return setError(E_INVALIDARG,
    6594                         tr("Too many USB controllers of this type"));
    6595 
    6596     ctrl.createObject();
    6597 
    6598     rc = ctrl->init(this, aName, aType);
    6599     if (FAILED(rc)) return rc;
    6600 
    6601     setModified(IsModified_USB);
    6602     mUSBControllers.backup();
    6603     mUSBControllers->push_back(ctrl);
    6604 
    6605     ctrl.queryInterfaceTo(controller);
    6606 
    6607     /* inform the direct session if any */
    6608     alock.release();
    6609     onUSBControllerChange();
    6610 
    6611     return S_OK;
    6612 }
    6613 
    6614 STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
    6615 {
    6616     CheckComArgStrNotEmptyOrNull(aName);
    6617 
    6618     AutoCaller autoCaller(this);
    6619     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6620 
    6621     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6622 
    6623     ComObjPtr<USBController> ctrl;
    6624 
    6625     HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
    6626     if (SUCCEEDED(rc))
    6627         ctrl.queryInterfaceTo(aUSBController);
    6628 
    6629     return rc;
    6630 }
    6631 
    6632 STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
    6633                                                   ULONG *aControllers)
    6634 {
    6635     CheckComArgOutPointerValid(aControllers);
    6636 
    6637     if (   (aType <= USBControllerType_Null)
    6638         || (aType >= USBControllerType_Last))
    6639         return setError(E_INVALIDARG,
    6640                         tr("Invalid USB controller type: %d"),
    6641                         aType);
    6642 
    6643     AutoCaller autoCaller(this);
    6644     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6645 
    6646     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6647 
    6648     ComObjPtr<USBController> ctrl;
    6649 
    6650     *aControllers = getUSBControllerCountByType(aType);
    6651 
    6652     return S_OK;
    6653 }
    6654 
    6655 STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
    6656 {
    6657     CheckComArgStrNotEmptyOrNull(aName);
    6658 
    6659     AutoCaller autoCaller(this);
    6660     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6661 
    6662     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6663 
    6664     HRESULT rc = checkStateDependency(MutableStateDep);
    6665     if (FAILED(rc)) return rc;
    6666 
    6667     ComObjPtr<USBController> ctrl;
    6668     rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
    6669     if (FAILED(rc)) return rc;
    6670 
    6671     setModified(IsModified_USB);
    6672     mUSBControllers.backup();
    6673 
    6674     ctrl->unshare();
    6675 
    6676     mUSBControllers->remove(ctrl);
    6677 
    6678     /* inform the direct session if any */
    6679     alock.release();
    6680     onUSBControllerChange();
    6681 
    6682     return S_OK;
    6683 }
    6684 
    6685 STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
    6686                                                 ULONG *puOriginX,
    6687                                                 ULONG *puOriginY,
    6688                                                 ULONG *puWidth,
    6689                                                 ULONG *puHeight,
    6690                                                 BOOL *pfEnabled)
    6691 {
    6692     LogFlowThisFunc(("\n"));
    6693 
    6694     CheckComArgNotNull(puOriginX);
    6695     CheckComArgNotNull(puOriginY);
    6696     CheckComArgNotNull(puWidth);
    6697     CheckComArgNotNull(puHeight);
    6698     CheckComArgNotNull(pfEnabled);
    6699 
    6700     uint32_t u32OriginX= 0;
    6701     uint32_t u32OriginY= 0;
    6702     uint32_t u32Width = 0;
    6703     uint32_t u32Height = 0;
    6704     uint16_t u16Flags = 0;
    6705 
    6706     int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
    6707                                        &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
    6708     if (RT_FAILURE(vrc))
    6709     {
    6710 #ifdef RT_OS_WINDOWS
    6711         /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
    6712          * This works with XPCOM. But Windows COM sets all output parameters to zero.
    6713          * So just assign fEnable to TRUE again.
    6714          * The right fix would be to change GUI API wrappers to make sure that parameters
    6715          * are changed only if API succeeds.
    6716          */
    6717         *pfEnabled = TRUE;
    6718 #endif
    6719         return setError(VBOX_E_IPRT_ERROR,
    6720                         tr("Saved guest size is not available (%Rrc)"),
    6721                         vrc);
    6722     }
    6723 
    6724     *puOriginX = u32OriginX;
    6725     *puOriginY = u32OriginY;
    6726     *puWidth = u32Width;
    6727     *puHeight = u32Height;
    6728     *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
    6729 
    6730     return S_OK;
    6731 }
    6732 
    6733 STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
    6734 {
    6735     LogFlowThisFunc(("\n"));
    6736 
    6737     CheckComArgNotNull(aSize);
    6738     CheckComArgNotNull(aWidth);
    6739     CheckComArgNotNull(aHeight);
    6740 
    6741     if (aScreenId != 0)
    6742         return E_NOTIMPL;
    6743 
    6744     AutoCaller autoCaller(this);
    6745     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6746 
    6747     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6748 
    6749     uint8_t *pu8Data = NULL;
    6750     uint32_t cbData = 0;
    6751     uint32_t u32Width = 0;
    6752     uint32_t u32Height = 0;
    6753 
    6754     int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
    6755 
    6756     if (RT_FAILURE(vrc))
    6757         return setError(VBOX_E_IPRT_ERROR,
    6758                         tr("Saved screenshot data is not available (%Rrc)"),
    6759                         vrc);
    6760 
    6761     *aSize = cbData;
    6762     *aWidth = u32Width;
    6763     *aHeight = u32Height;
    6764 
    6765     freeSavedDisplayScreenshot(pu8Data);
    6766 
    6767     return S_OK;
    6768 }
    6769 
    6770 STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
    6771 {
    6772     LogFlowThisFunc(("\n"));
    6773 
    6774     CheckComArgNotNull(aWidth);
    6775     CheckComArgNotNull(aHeight);
    6776     CheckComArgOutSafeArrayPointerValid(aData);
    6777 
    6778     if (aScreenId != 0)
    6779         return E_NOTIMPL;
    6780 
    6781     AutoCaller autoCaller(this);
    6782     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6783 
    6784     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6785 
    6786     uint8_t *pu8Data = NULL;
    6787     uint32_t cbData = 0;
    6788     uint32_t u32Width = 0;
    6789     uint32_t u32Height = 0;
    6790 
    6791     int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
    6792 
    6793     if (RT_FAILURE(vrc))
    6794         return setError(VBOX_E_IPRT_ERROR,
    6795                         tr("Saved screenshot data is not available (%Rrc)"),
    6796                         vrc);
    6797 
    6798     *aWidth = u32Width;
    6799     *aHeight = u32Height;
    6800 
    6801     com::SafeArray<BYTE> bitmap(cbData);
    6802     /* Convert pixels to format expected by the API caller. */
    6803     if (aBGR)
    6804     {
    6805         /* [0] B, [1] G, [2] R, [3] A. */
    6806         for (unsigned i = 0; i < cbData; i += 4)
    6807         {
    6808             bitmap[i]     = pu8Data[i];
    6809             bitmap[i + 1] = pu8Data[i + 1];
    6810             bitmap[i + 2] = pu8Data[i + 2];
    6811             bitmap[i + 3] = 0xff;
    6812         }
    6813     }
    6814     else
    6815     {
    6816         /* [0] R, [1] G, [2] B, [3] A. */
    6817         for (unsigned i = 0; i < cbData; i += 4)
    6818         {
    6819             bitmap[i]     = pu8Data[i + 2];
    6820             bitmap[i + 1] = pu8Data[i + 1];
    6821             bitmap[i + 2] = pu8Data[i];
    6822             bitmap[i + 3] = 0xff;
    6823         }
    6824     }
    6825     bitmap.detachTo(ComSafeArrayOutArg(aData));
    6826 
    6827     freeSavedDisplayScreenshot(pu8Data);
    6828 
    6829     return S_OK;
    6830 }
    6831 
    6832 
    6833 STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
    6834 {
    6835     LogFlowThisFunc(("\n"));
    6836 
    6837     CheckComArgNotNull(aWidth);
    6838     CheckComArgNotNull(aHeight);
    6839     CheckComArgOutSafeArrayPointerValid(aData);
    6840 
    6841     if (aScreenId != 0)
    6842         return E_NOTIMPL;
    6843 
    6844     AutoCaller autoCaller(this);
    6845     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6846 
    6847     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6848 
    6849     uint8_t *pu8Data = NULL;
    6850     uint32_t cbData = 0;
    6851     uint32_t u32Width = 0;
    6852     uint32_t u32Height = 0;
    6853 
    6854     int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
    6855 
    6856     if (RT_FAILURE(vrc))
    6857         return setError(VBOX_E_IPRT_ERROR,
    6858                         tr("Saved screenshot data is not available (%Rrc)"),
    6859                         vrc);
    6860 
    6861     *aWidth = u32Width;
    6862     *aHeight = u32Height;
    6863 
    6864     HRESULT rc = S_OK;
    6865     uint8_t *pu8PNG = NULL;
    6866     uint32_t cbPNG = 0;
    6867     uint32_t cxPNG = 0;
    6868     uint32_t cyPNG = 0;
    6869 
    6870     vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
    6871 
    6872     if (RT_SUCCESS(vrc))
    6873     {
    6874         com::SafeArray<BYTE> screenData(cbPNG);
    6875         screenData.initFrom(pu8PNG, cbPNG);
    6876         if (pu8PNG)
    6877             RTMemFree(pu8PNG);
    6878         screenData.detachTo(ComSafeArrayOutArg(aData));
    6879     }
    6880     else
    6881     {
    6882         if (pu8PNG)
    6883             RTMemFree(pu8PNG);
    6884         return setError(VBOX_E_IPRT_ERROR,
    6885                         tr("Could not convert screenshot to PNG (%Rrc)"),
    6886                         vrc);
    6887     }
    6888 
    6889     freeSavedDisplayScreenshot(pu8Data);
    6890 
    6891     return rc;
    6892 }
    6893 
    6894 STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
    6895 {
    6896     LogFlowThisFunc(("\n"));
    6897 
    6898     CheckComArgNotNull(aSize);
    6899     CheckComArgNotNull(aWidth);
    6900     CheckComArgNotNull(aHeight);
    6901 
    6902     if (aScreenId != 0)
    6903         return E_NOTIMPL;
    6904 
    6905     AutoCaller autoCaller(this);
    6906     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6907 
    6908     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6909 
    6910     uint8_t *pu8Data = NULL;
    6911     uint32_t cbData = 0;
    6912     uint32_t u32Width = 0;
    6913     uint32_t u32Height = 0;
    6914 
    6915     int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
    6916 
    6917     if (RT_FAILURE(vrc))
    6918         return setError(VBOX_E_IPRT_ERROR,
    6919                         tr("Saved screenshot data is not available (%Rrc)"),
    6920                         vrc);
    6921 
    6922     *aSize = cbData;
    6923     *aWidth = u32Width;
    6924     *aHeight = u32Height;
    6925 
    6926     freeSavedDisplayScreenshot(pu8Data);
    6927 
    6928     return S_OK;
    6929 }
    6930 
    6931 STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
    6932 {
    6933     LogFlowThisFunc(("\n"));
    6934 
    6935     CheckComArgNotNull(aWidth);
    6936     CheckComArgNotNull(aHeight);
    6937     CheckComArgOutSafeArrayPointerValid(aData);
    6938 
    6939     if (aScreenId != 0)
    6940         return E_NOTIMPL;
    6941 
    6942     AutoCaller autoCaller(this);
    6943     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6944 
    6945     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    6946 
    6947     uint8_t *pu8Data = NULL;
    6948     uint32_t cbData = 0;
    6949     uint32_t u32Width = 0;
    6950     uint32_t u32Height = 0;
    6951 
    6952     int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
    6953 
    6954     if (RT_FAILURE(vrc))
    6955         return setError(VBOX_E_IPRT_ERROR,
    6956                         tr("Saved screenshot thumbnail data is not available (%Rrc)"),
    6957                         vrc);
    6958 
    6959     *aWidth = u32Width;
    6960     *aHeight = u32Height;
    6961 
    6962     com::SafeArray<BYTE> png(cbData);
    6963     png.initFrom(pu8Data, cbData);
    6964     png.detachTo(ComSafeArrayOutArg(aData));
    6965 
    6966     freeSavedDisplayScreenshot(pu8Data);
    6967 
    6968     return S_OK;
    6969 }
    6970 
    6971 STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
    6972 {
    6973     HRESULT rc = S_OK;
    6974     LogFlowThisFunc(("\n"));
    6975 
    6976     AutoCaller autoCaller(this);
    6977     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6978 
    6979     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6980 
    6981     if (!mHWData->mCPUHotPlugEnabled)
    6982         return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
    6983 
    6984     if (aCpu >= mHWData->mCPUCount)
    6985         return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
    6986 
    6987     if (mHWData->mCPUAttached[aCpu])
    6988         return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
    6989 
    6990     alock.release();
    6991     rc = onCPUChange(aCpu, false);
    6992     alock.acquire();
    6993     if (FAILED(rc)) return rc;
    6994 
    6995     setModified(IsModified_MachineData);
    6996     mHWData.backup();
    6997     mHWData->mCPUAttached[aCpu] = true;
    6998 
    6999     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    7000     if (Global::IsOnline(mData->mMachineState))
    7001         saveSettings(NULL);
    7002 
    7003     return S_OK;
    7004 }
    7005 
    7006 STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
    7007 {
    7008     HRESULT rc = S_OK;
    7009     LogFlowThisFunc(("\n"));
    7010 
    7011     AutoCaller autoCaller(this);
    7012     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7013 
    7014     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7015 
    7016     if (!mHWData->mCPUHotPlugEnabled)
    7017         return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
    7018 
    7019     if (aCpu >= SchemaDefs::MaxCPUCount)
    7020         return setError(E_INVALIDARG,
    7021                         tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
    7022                         SchemaDefs::MaxCPUCount);
    7023 
    7024     if (!mHWData->mCPUAttached[aCpu])
    7025         return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
    7026 
    7027     /* CPU 0 can't be detached */
    7028     if (aCpu == 0)
    7029         return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
    7030 
    7031     alock.release();
    7032     rc = onCPUChange(aCpu, true);
    7033     alock.acquire();
    7034     if (FAILED(rc)) return rc;
    7035 
    7036     setModified(IsModified_MachineData);
    7037     mHWData.backup();
    7038     mHWData->mCPUAttached[aCpu] = false;
    7039 
    7040     /** Save settings if online - @todo why is this required? -- @bugref{6818} */
    7041     if (Global::IsOnline(mData->mMachineState))
    7042         saveSettings(NULL);
    7043 
    7044     return S_OK;
    7045 }
    7046 
    7047 STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
    7048 {
    7049     LogFlowThisFunc(("\n"));
    7050 
    7051     CheckComArgNotNull(aCpuAttached);
    7052 
    7053     *aCpuAttached = false;
    7054 
    7055     AutoCaller autoCaller(this);
    7056     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7057 
    7058     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7059 
    7060     /* If hotplug is enabled the CPU is always enabled. */
    7061     if (!mHWData->mCPUHotPlugEnabled)
    7062     {
    7063         if (aCpu < mHWData->mCPUCount)
    7064             *aCpuAttached = true;
    7065     }
    7066     else
    7067     {
    7068         if (aCpu < SchemaDefs::MaxCPUCount)
    7069             *aCpuAttached = mHWData->mCPUAttached[aCpu];
    7070     }
    7071 
    7072     return S_OK;
    7073 }
    7074 
    7075 STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
    7076 {
    7077     CheckComArgOutPointerValid(aName);
    7078 
    7079     AutoCaller autoCaller(this);
    7080     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7081 
    7082     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7083 
    7084     Utf8Str log = queryLogFilename(aIdx);
    7085     if (!RTFileExists(log.c_str()))
    7086         log.setNull();
    7087     log.cloneTo(aName);
    7088 
    7089     return S_OK;
    7090 }
    7091 
    7092 STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
    7093 {
    7094     LogFlowThisFunc(("\n"));
    7095     CheckComArgOutSafeArrayPointerValid(aData);
    7096     if (aSize < 0)
    7097         return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
    7098 
    7099     AutoCaller autoCaller(this);
    7100     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7101 
    7102     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7103 
    7104     HRESULT rc = S_OK;
    7105     Utf8Str log = queryLogFilename(aIdx);
    7106 
    7107     /* do not unnecessarily hold the lock while doing something which does
    7108      * not need the lock and potentially takes a long time. */
    7109     alock.release();
    7110 
    7111     /* Limit the chunk size to 32K for now, as that gives better performance
    7112      * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
    7113      * One byte expands to approx. 25 bytes of breathtaking XML. */
    7114     size_t cbData = (size_t)RT_MIN(aSize, 32768);
    7115     com::SafeArray<BYTE> logData(cbData);
    7116 
    7117     RTFILE LogFile;
    7118     int vrc = RTFileOpen(&LogFile, log.c_str(),
    7119                          RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
    7120     if (RT_SUCCESS(vrc))
    7121     {
    7122         vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
    7123         if (RT_SUCCESS(vrc))
    7124             logData.resize(cbData);
    7125         else
    7126             rc = setError(VBOX_E_IPRT_ERROR,
    7127                           tr("Could not read log file '%s' (%Rrc)"),
    7128                           log.c_str(), vrc);
    7129         RTFileClose(LogFile);
    7130     }
    7131     else
    7132         rc = setError(VBOX_E_IPRT_ERROR,
    7133                       tr("Could not open log file '%s' (%Rrc)"),
    7134                       log.c_str(), vrc);
    7135 
    7136     if (FAILED(rc))
    7137         logData.resize(0);
    7138     logData.detachTo(ComSafeArrayOutArg(aData));
    7139 
    7140     return rc;
    7141 }
    7142 
    7143 
    7144 /**
    7145  * Currently this method doesn't attach device to the running VM,
    7146  * just makes sure it's plugged on next VM start.
    7147  */
    7148 STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
    7149 {
    7150     AutoCaller autoCaller(this);
    7151     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7152 
    7153     // lock scope
    7154     {
    7155         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7156 
    7157         HRESULT rc = checkStateDependency(MutableStateDep);
    7158         if (FAILED(rc)) return rc;
    7159 
    7160         ChipsetType_T aChipset = ChipsetType_PIIX3;
    7161         COMGETTER(ChipsetType)(&aChipset);
    7162 
    7163         if (aChipset != ChipsetType_ICH9)
    7164         {
    7165             return setError(E_INVALIDARG,
    7166                             tr("Host PCI attachment only supported with ICH9 chipset"));
    7167         }
    7168 
    7169         // check if device with this host PCI address already attached
    7170         for (HWData::PCIDeviceAssignmentList::iterator it =  mHWData->mPCIDeviceAssignments.begin();
    7171              it !=  mHWData->mPCIDeviceAssignments.end();
    7172              ++it)
    7173         {
    7174             LONG iHostAddress = -1;
    7175             ComPtr<PCIDeviceAttachment> pAttach;
    7176             pAttach = *it;
    7177             pAttach->COMGETTER(HostAddress)(&iHostAddress);
    7178             if (iHostAddress == hostAddress)
    7179                 return setError(E_INVALIDARG,
    7180                                 tr("Device with host PCI address already attached to this VM"));
    7181         }
    7182 
    7183         ComObjPtr<PCIDeviceAttachment> pda;
    7184         char name[32];
    7185 
    7186         RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
    7187         Bstr bname(name);
    7188         pda.createObject();
    7189         pda->init(this, bname,  hostAddress, desiredGuestAddress, TRUE);
    7190         setModified(IsModified_MachineData);
    7191         mHWData.backup();
    7192         mHWData->mPCIDeviceAssignments.push_back(pda);
    7193     }
    7194 
    7195     return S_OK;
    7196 }
    7197 
    7198 /**
    7199  * Currently this method doesn't detach device from the running VM,
    7200  * just makes sure it's not plugged on next VM start.
    7201  */
    7202 STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
    7203 {
    7204     AutoCaller autoCaller(this);
    7205     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7206 
    7207     ComObjPtr<PCIDeviceAttachment> pAttach;
    7208     bool fRemoved = false;
    7209     HRESULT rc;
    7210 
    7211     // lock scope
    7212     {
    7213         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7214 
    7215         rc = checkStateDependency(MutableStateDep);
    7216         if (FAILED(rc)) return rc;
    7217 
    7218         for (HWData::PCIDeviceAssignmentList::iterator it =  mHWData->mPCIDeviceAssignments.begin();
    7219              it !=  mHWData->mPCIDeviceAssignments.end();
    7220              ++it)
    7221         {
    7222             LONG iHostAddress = -1;
    7223             pAttach = *it;
    7224             pAttach->COMGETTER(HostAddress)(&iHostAddress);
    7225             if (iHostAddress  != -1  && iHostAddress == hostAddress)
    7226             {
    7227                 setModified(IsModified_MachineData);
    7228                 mHWData.backup();
    7229                 mHWData->mPCIDeviceAssignments.remove(pAttach);
    7230                 fRemoved = true;
    7231                 break;
    7232             }
    7233         }
    7234     }
    7235 
    7236 
    7237     /* Fire event outside of the lock */
    7238     if (fRemoved)
    7239     {
    7240         Assert(!pAttach.isNull());
    7241         ComPtr<IEventSource> es;
    7242         rc = mParent->COMGETTER(EventSource)(es.asOutParam());
    7243         Assert(SUCCEEDED(rc));
    7244         Bstr mid;
    7245         rc = this->COMGETTER(Id)(mid.asOutParam());
    7246         Assert(SUCCEEDED(rc));
    7247         fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
    7248     }
    7249 
    7250     return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
    7251                                       tr("No host PCI device %08x attached"),
    7252                                       hostAddress
    7253                                       );
    7254 }
    7255 
    7256 STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
    7257 {
    7258     CheckComArgOutSafeArrayPointerValid(aAssignments);
    7259 
    7260     AutoCaller autoCaller(this);
    7261     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7262 
    7263     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7264 
    7265     SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
    7266     assignments.detachTo(ComSafeArrayOutArg(aAssignments));
    7267 
    7268     return S_OK;
    7269 }
    7270 
    7271 STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
    7272 {
    7273     CheckComArgOutPointerValid(aBandwidthControl);
    7274 
    7275     AutoCaller autoCaller(this);
    7276     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7277 
    7278     mBandwidthControl.queryInterfaceTo(aBandwidthControl);
    7279 
    7280     return S_OK;
    7281 }
    7282 
    7283 STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
    7284 {
    7285     CheckComArgOutPointerValid(pfEnabled);
    7286     AutoCaller autoCaller(this);
    7287     HRESULT hrc = autoCaller.rc();
    7288     if (SUCCEEDED(hrc))
    7289     {
    7290         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7291         *pfEnabled = mHWData->mDebugging.fTracingEnabled;
    7292     }
    7293     return hrc;
    7294 }
    7295 
    7296 STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
    7297 {
    7298     AutoCaller autoCaller(this);
    7299     HRESULT hrc = autoCaller.rc();
    7300     if (SUCCEEDED(hrc))
    7301     {
    7302         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7303         hrc = checkStateDependency(MutableStateDep);
    7304         if (SUCCEEDED(hrc))
    7305         {
    7306             hrc = mHWData.backupEx();
    7307             if (SUCCEEDED(hrc))
    7308             {
    7309                 setModified(IsModified_MachineData);
    7310                 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
    7311             }
    7312         }
    7313     }
    7314     return hrc;
    7315 }
    7316 
    7317 STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
    7318 {
    7319     CheckComArgOutPointerValid(pbstrConfig);
    7320     AutoCaller autoCaller(this);
    7321     HRESULT hrc = autoCaller.rc();
    7322     if (SUCCEEDED(hrc))
    7323     {
    7324         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7325         hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
    7326     }
    7327     return hrc;
    7328 }
    7329 
    7330 STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
    7331 {
    7332     CheckComArgStr(bstrConfig);
    7333     AutoCaller autoCaller(this);
    7334     HRESULT hrc = autoCaller.rc();
    7335     if (SUCCEEDED(hrc))
    7336     {
    7337         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7338         hrc = checkStateDependency(MutableStateDep);
    7339         if (SUCCEEDED(hrc))
    7340         {
    7341             hrc = mHWData.backupEx();
    7342             if (SUCCEEDED(hrc))
    7343             {
    7344                 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
    7345                 if (SUCCEEDED(hrc))
    7346                     setModified(IsModified_MachineData);
    7347             }
    7348         }
    7349     }
    7350     return hrc;
    7351 
    7352 }
    7353 
    7354 STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
    7355 {
    7356     CheckComArgOutPointerValid(pfAllow);
    7357     AutoCaller autoCaller(this);
    7358     HRESULT hrc = autoCaller.rc();
    7359     if (SUCCEEDED(hrc))
    7360     {
    7361         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7362         *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
    7363     }
    7364     return hrc;
    7365 }
    7366 
    7367 STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
    7368 {
    7369     AutoCaller autoCaller(this);
    7370     HRESULT hrc = autoCaller.rc();
    7371     if (SUCCEEDED(hrc))
    7372     {
    7373         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7374         hrc = checkStateDependency(MutableStateDep);
    7375         if (SUCCEEDED(hrc))
    7376         {
    7377             hrc = mHWData.backupEx();
    7378             if (SUCCEEDED(hrc))
    7379             {
    7380                 setModified(IsModified_MachineData);
    7381                 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
    7382             }
    7383         }
    7384     }
    7385     return hrc;
    7386 }
    7387 
    7388 STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
    7389 {
    7390     CheckComArgOutPointerValid(pfEnabled);
    7391     AutoCaller autoCaller(this);
    7392     HRESULT hrc = autoCaller.rc();
    7393     if (SUCCEEDED(hrc))
    7394     {
    7395         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7396         *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
    7397     }
    7398     return hrc;
    7399 }
    7400 
    7401 STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
    7402 {
    7403     AutoCaller autoCaller(this);
    7404     HRESULT hrc = autoCaller.rc();
    7405     if (SUCCEEDED(hrc))
    7406     {
    7407         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7408         hrc = checkStateDependency(MutableStateDep);
    7409         if (   SUCCEEDED(hrc)
    7410             && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
    7411         {
    7412             AutostartDb *autostartDb = mParent->getAutostartDb();
    7413             int vrc;
    7414 
    7415             if (fEnabled)
    7416                 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
    7417             else
    7418                 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
    7419 
    7420             if (RT_SUCCESS(vrc))
    7421             {
    7422                 hrc = mHWData.backupEx();
    7423                 if (SUCCEEDED(hrc))
    7424                 {
    7425                     setModified(IsModified_MachineData);
    7426                     mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
    7427                 }
    7428             }
    7429             else if (vrc == VERR_NOT_SUPPORTED)
    7430                 hrc = setError(VBOX_E_NOT_SUPPORTED,
    7431                                tr("The VM autostart feature is not supported on this platform"));
    7432             else if (vrc == VERR_PATH_NOT_FOUND)
    7433                 hrc = setError(E_FAIL,
    7434                                tr("The path to the autostart database is not set"));
    7435             else
    7436                 hrc = setError(E_UNEXPECTED,
    7437                                tr("%s machine '%s' to the autostart database failed with %Rrc"),
    7438                                fEnabled ? "Adding" : "Removing",
    7439                                mUserData->s.strName.c_str(), vrc);
    7440         }
    7441     }
    7442     return hrc;
    7443 }
    7444 
    7445 STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
    7446 {
    7447     CheckComArgOutPointerValid(puDelay);
    7448     AutoCaller autoCaller(this);
    7449     HRESULT hrc = autoCaller.rc();
    7450     if (SUCCEEDED(hrc))
    7451     {
    7452         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7453         *puDelay = mHWData->mAutostart.uAutostartDelay;
    7454     }
    7455     return hrc;
    7456 }
    7457 
    7458 STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
    7459 {
    7460     AutoCaller autoCaller(this);
    7461     HRESULT hrc = autoCaller.rc();
    7462     if (SUCCEEDED(hrc))
    7463     {
    7464         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7465         hrc = checkStateDependency(MutableStateDep);
    7466         if (SUCCEEDED(hrc))
    7467         {
    7468             hrc = mHWData.backupEx();
    7469             if (SUCCEEDED(hrc))
    7470             {
    7471                 setModified(IsModified_MachineData);
    7472                 mHWData->mAutostart.uAutostartDelay = uDelay;
    7473             }
    7474         }
    7475     }
    7476     return hrc;
    7477 }
    7478 
    7479 STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
    7480 {
    7481     CheckComArgOutPointerValid(penmAutostopType);
    7482     AutoCaller autoCaller(this);
    7483     HRESULT hrc = autoCaller.rc();
    7484     if (SUCCEEDED(hrc))
    7485     {
    7486         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7487         *penmAutostopType = mHWData->mAutostart.enmAutostopType;
    7488     }
    7489     return hrc;
    7490 }
    7491 
    7492 STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
    7493 {
    7494     AutoCaller autoCaller(this);
    7495     HRESULT hrc = autoCaller.rc();
    7496     if (SUCCEEDED(hrc))
    7497     {
    7498         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7499         hrc = checkStateDependency(MutableStateDep);
    7500         if (   SUCCEEDED(hrc)
    7501             && mHWData->mAutostart.enmAutostopType != enmAutostopType)
    7502         {
    7503             AutostartDb *autostartDb = mParent->getAutostartDb();
    7504             int vrc;
    7505 
    7506             if (enmAutostopType != AutostopType_Disabled)
    7507                 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
    7508             else
    7509                 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
    7510 
    7511             if (RT_SUCCESS(vrc))
    7512             {
    7513                 hrc = mHWData.backupEx();
    7514                 if (SUCCEEDED(hrc))
    7515                 {
    7516                     setModified(IsModified_MachineData);
    7517                     mHWData->mAutostart.enmAutostopType = enmAutostopType;
    7518                 }
    7519             }
    7520             else if (vrc == VERR_NOT_SUPPORTED)
    7521                 hrc = setError(VBOX_E_NOT_SUPPORTED,
    7522                                tr("The VM autostop feature is not supported on this platform"));
    7523             else if (vrc == VERR_PATH_NOT_FOUND)
    7524                 hrc = setError(E_FAIL,
    7525                                tr("The path to the autostart database is not set"));
    7526             else
    7527                 hrc = setError(E_UNEXPECTED,
    7528                                tr("%s machine '%s' to the autostop database failed with %Rrc"),
    7529                                enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
    7530                                mUserData->s.strName.c_str(), vrc);
    7531         }
    7532     }
    7533     return hrc;
    7534 }
    7535 
    7536 STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
    7537 {
    7538     CheckComArgOutPointerValid(aDefaultFrontend);
    7539     AutoCaller autoCaller(this);
    7540     HRESULT hrc = autoCaller.rc();
    7541     if (SUCCEEDED(hrc))
    7542     {
    7543         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7544         mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
    7545     }
    7546     return hrc;
    7547 }
    7548 
    7549 STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
    7550 {
    7551     CheckComArgStr(aDefaultFrontend);
    7552     AutoCaller autoCaller(this);
    7553     HRESULT hrc = autoCaller.rc();
    7554     if (SUCCEEDED(hrc))
    7555     {
    7556         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7557         hrc = checkStateDependency(MutableOrSavedStateDep);
    7558         if (SUCCEEDED(hrc))
    7559         {
    7560             hrc = mHWData.backupEx();
    7561             if (SUCCEEDED(hrc))
    7562             {
    7563                 setModified(IsModified_MachineData);
    7564                 mHWData->mDefaultFrontend = aDefaultFrontend;
    7565             }
    7566         }
    7567     }
    7568     return hrc;
    7569 }
    7570 
    7571 STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
    7572 {
    7573     CheckComArgSafeArrayNotNull(aIcon);
    7574     CheckComArgOutSafeArrayPointerValid(aIcon);
    7575     AutoCaller autoCaller(this);
    7576     HRESULT hrc = autoCaller.rc();
    7577     if (SUCCEEDED(hrc))
    7578     {
    7579         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7580         com::SafeArray<BYTE> icon(mUserData->mIcon.size());
    7581         memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
    7582         icon.detachTo(ComSafeArrayOutArg(aIcon));
    7583     }
    7584     return hrc;
    7585 }
    7586 
    7587 STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
    7588 {
    7589     CheckComArgSafeArrayNotNull(aIcon);
    7590     AutoCaller autoCaller(this);
    7591     HRESULT hrc = autoCaller.rc();
    7592     if (SUCCEEDED(hrc))
    7593     {
    7594         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7595         hrc = checkStateDependency(MutableOrSavedStateDep);
    7596         if (SUCCEEDED(hrc))
    7597         {
    7598             setModified(IsModified_MachineData);
    7599             mUserData.backup();
    7600             com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
    7601             mUserData->mIcon.clear();
    7602             memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
    7603          }
    7604     }
    7605     return hrc;
    7606 }
    7607 
    7608 STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
    7609 {
    7610     CheckComArgOutPointerValid(aAvailable);
    7611 
    7612     AutoCaller autoCaller(this);
    7613     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7614 
    7615 #ifdef VBOX_WITH_USB
    7616     *aAvailable = true;
    7617 #else
    7618     *aAvailable = false;
    7619 #endif
    7620     return S_OK;
    7621 }
    7622 
    7623 STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
    7624 {
    7625     LogFlowFuncEnter();
    7626 
    7627     CheckComArgNotNull(pTarget);
    7628     CheckComArgOutPointerValid(pProgress);
    7629 
    7630     /* Convert the options. */
    7631     RTCList<CloneOptions_T> optList;
    7632     if (options != NULL)
    7633         optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
    7634 
    7635     if (optList.contains(CloneOptions_Link))
    7636     {
    7637         if (!isSnapshotMachine())
    7638             return setError(E_INVALIDARG,
    7639                             tr("Linked clone can only be created from a snapshot"));
    7640         if (mode != CloneMode_MachineState)
    7641             return setError(E_INVALIDARG,
    7642                             tr("Linked clone can only be created for a single machine state"));
    7643     }
    7644     AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
    7645 
    7646     AutoCaller autoCaller(this);
    7647     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7648 
    7649 
    7650     MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
    7651 
    7652     HRESULT rc = pWorker->start(pProgress);
    7653 
    7654     LogFlowFuncLeave();
    7655 
    7656     return rc;
    7657 }
    7658 
    7659 // public methods for internal purposes
    7660 /////////////////////////////////////////////////////////////////////////////
    7661 
    7662 /**
    7663  * Adds the given IsModified_* flag to the dirty flags of the machine.
    7664  * This must be called either during loadSettings or under the machine write lock.
    7665  * @param fl
    7666  */
    7667 void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
    7668 {
    7669     mData->flModifications |= fl;
    7670     if (fAllowStateModification && isStateModificationAllowed())
    7671         mData->mCurrentStateModified = true;
    7672 }
    7673 
    7674 /**
    7675  * Adds the given IsModified_* flag to the dirty flags of the machine, taking
    7676  * care of the write locking.
    7677  *
    7678  * @param   fModifications      The flag to add.
    7679  */
    7680 void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
    7681 {
    7682     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7683     setModified(fModification, fAllowStateModification);
    7684 }
    7685 
    7686 /**
    7687  *  Saves the registry entry of this machine to the given configuration node.
    7688  *
    7689  *  @param aEntryNode Node to save the registry entry to.
    7690  *
    7691  *  @note locks this object for reading.
    7692  */
    7693 HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
    7694 {
    7695     AutoLimitedCaller autoCaller(this);
    7696     AssertComRCReturnRC(autoCaller.rc());
    7697 
    7698     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7699 
    7700     data.uuid = mData->mUuid;
    7701     data.strSettingsFile = mData->m_strConfigFile;
    7702 
    7703     return S_OK;
    7704 }
    7705 
    7706 /**
    7707  * Calculates the absolute path of the given path taking the directory of the
    7708  * machine settings file as the current directory.
    7709  *
    7710  * @param  aPath    Path to calculate the absolute path for.
    7711  * @param  aResult  Where to put the result (used only on success, can be the
    7712  *                  same Utf8Str instance as passed in @a aPath).
    7713  * @return IPRT result.
    7714  *
    7715  * @note Locks this object for reading.
    7716  */
    7717 int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
    7718 {
    7719     AutoCaller autoCaller(this);
    7720     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    7721 
    7722     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7723 
    7724     AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
    7725 
    7726     Utf8Str strSettingsDir = mData->m_strConfigFileFull;
    7727 
    7728     strSettingsDir.stripFilename();
    7729     char folder[RTPATH_MAX];
    7730     int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
    7731     if (RT_SUCCESS(vrc))
    7732         aResult = folder;
    7733 
    7734     return vrc;
    7735 }
    7736 
    7737 /**
    7738  * Copies strSource to strTarget, making it relative to the machine folder
    7739  * if it is a subdirectory thereof, or simply copying it otherwise.
    7740  *
    7741  * @param strSource Path to evaluate and copy.
    7742  * @param strTarget Buffer to receive target path.
    7743  *
    7744  * @note Locks this object for reading.
    7745  */
    7746 void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
    7747                                         Utf8Str &strTarget)
    7748 {
    7749     AutoCaller autoCaller(this);
    7750     AssertComRCReturn(autoCaller.rc(), (void)0);
    7751 
    7752     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7753 
    7754     AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
    7755     // use strTarget as a temporary buffer to hold the machine settings dir
    7756     strTarget = mData->m_strConfigFileFull;
    7757     strTarget.stripFilename();
    7758     if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
    7759     {
    7760         // is relative: then append what's left
    7761         strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
    7762         // for empty paths (only possible for subdirs) use "." to avoid
    7763         // triggering default settings for not present config attributes.
    7764         if (strTarget.isEmpty())
    7765             strTarget = ".";
    7766     }
    7767     else
    7768         // is not relative: then overwrite
    7769         strTarget = strSource;
    7770 }
    7771 
    7772 /**
    7773  *  Returns the full path to the machine's log folder in the
    7774  *  \a aLogFolder argument.
    7775  */
    7776 void Machine::getLogFolder(Utf8Str &aLogFolder)
    7777 {
    7778     AutoCaller autoCaller(this);
    7779     AssertComRCReturnVoid(autoCaller.rc());
    7780 
    7781     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7782 
    7783     char szTmp[RTPATH_MAX];
    7784     int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
    7785     if (RT_SUCCESS(vrc))
    7786     {
    7787         if (szTmp[0] && !mUserData.isNull())
    7788         {
    7789             char szTmp2[RTPATH_MAX];
    7790             vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
    7791             if (RT_SUCCESS(vrc))
    7792                 aLogFolder = BstrFmt("%s%c%s",
    7793                                      szTmp2,
    7794                                      RTPATH_DELIMITER,
    7795                                      mUserData->s.strName.c_str()); // path/to/logfolder/vmname
    7796         }
    7797         else
    7798             vrc = VERR_PATH_IS_RELATIVE;
    7799     }
    7800 
    7801     if (RT_FAILURE(vrc))
    7802     {
    7803         // fallback if VBOX_USER_LOGHOME is not set or invalid
    7804         aLogFolder = mData->m_strConfigFileFull;    // path/to/machinesfolder/vmname/vmname.vbox
    7805         aLogFolder.stripFilename();                 // path/to/machinesfolder/vmname
    7806         aLogFolder.append(RTPATH_DELIMITER);
    7807         aLogFolder.append("Logs");                  // path/to/machinesfolder/vmname/Logs
    7808     }
    7809 }
    7810 
    7811 /**
    7812  *  Returns the full path to the machine's log file for an given index.
    7813  */
    7814 Utf8Str Machine::queryLogFilename(ULONG idx)
    7815 {
    7816     Utf8Str logFolder;
    7817     getLogFolder(logFolder);
    7818     Assert(logFolder.length());
    7819     Utf8Str log;
    7820     if (idx == 0)
    7821         log = Utf8StrFmt("%s%cVBox.log",
    7822                          logFolder.c_str(), RTPATH_DELIMITER);
    7823     else
    7824         log = Utf8StrFmt("%s%cVBox.log.%d",
    7825                          logFolder.c_str(), RTPATH_DELIMITER, idx);
    7826     return log;
    7827 }
    7828 
    7829 /**
    7830  * Composes a unique saved state filename based on the current system time. The filename is
    7831  * granular to the second so this will work so long as no more than one snapshot is taken on
    7832  * a machine per second.
    7833  *
    7834  * Before version 4.1, we used this formula for saved state files:
    7835  *      Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
    7836  * which no longer works because saved state files can now be shared between the saved state of the
    7837  * "saved" machine and an online snapshot, and the following would cause problems:
    7838  * 1) save machine
    7839  * 2) create online snapshot from that machine state --> reusing saved state file
    7840  * 3) save machine again --> filename would be reused, breaking the online snapshot
    7841  *
    7842  * So instead we now use a timestamp.
    7843  *
    7844  * @param str
    7845  */
    7846 void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
    7847 {
    7848     AutoCaller autoCaller(this);
    7849     AssertComRCReturnVoid(autoCaller.rc());
    7850 
    7851     {
    7852         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7853         calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
    7854     }
    7855 
    7856     RTTIMESPEC ts;
    7857     RTTimeNow(&ts);
    7858     RTTIME time;
    7859     RTTimeExplode(&time, &ts);
    7860 
    7861     strStateFilePath += RTPATH_DELIMITER;
    7862     strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
    7863                                    time.i32Year, time.u8Month, time.u8MonthDay,
    7864                                    time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
    7865 }
    7866 
    7867 /**
    7868  *  Returns the full path to the default video capture file.
    7869  */
    7870 void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
    7871 {
    7872     AutoCaller autoCaller(this);
    7873     AssertComRCReturnVoid(autoCaller.rc());
    7874 
    7875     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7876 
    7877     strFile = mData->m_strConfigFileFull;       // path/to/machinesfolder/vmname/vmname.vbox
    7878     strFile.stripExt();                         // path/to/machinesfolder/vmname/vmname
    7879     strFile.append(".webm");                    // path/to/machinesfolder/vmname/vmname.webm
    7880 }
    7881 
    7882 /**
    7883  * Returns whether at least one USB controller is present for the VM.
    7884  */
    7885 bool Machine::isUSBControllerPresent()
    7886 {
    7887     AutoCaller autoCaller(this);
    7888     AssertComRCReturn(autoCaller.rc(), false);
    7889 
    7890     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    7891 
    7892     return (mUSBControllers->size() > 0);
    7893 }
    7894 
    7895 /**
    7896  *  @note Locks this object for writing, calls the client process
    7897  *        (inside the lock).
    7898  */
    7899 HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
    7900                                  const Utf8Str &strFrontend,
    7901                                  const Utf8Str &strEnvironment,
    7902                                  ProgressProxy *aProgress)
    7903 {
    7904     LogFlowThisFuncEnter();
    7905 
    7906     AssertReturn(aControl, E_FAIL);
    7907     AssertReturn(aProgress, E_FAIL);
    7908     AssertReturn(!strFrontend.isEmpty(), E_FAIL);
    7909 
    7910     AutoCaller autoCaller(this);
    7911     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    7912 
    7913     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    7914 
    7915     if (!mData->mRegistered)
    7916         return setError(E_UNEXPECTED,
    7917                         tr("The machine '%s' is not registered"),
    7918                         mUserData->s.strName.c_str());
    7919 
    7920     LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
    7921 
    7922     if (    mData->mSession.mState == SessionState_Locked
    7923          || mData->mSession.mState == SessionState_Spawning
    7924          || mData->mSession.mState == SessionState_Unlocking)
    7925         return setError(VBOX_E_INVALID_OBJECT_STATE,
    7926                         tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
    7927                         mUserData->s.strName.c_str());
    7928 
    7929     /* may not be busy */
    7930     AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
    7931 
    7932     /* get the path to the executable */
    7933     char szPath[RTPATH_MAX];
    7934     RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
    7935     size_t sz = strlen(szPath);
    7936     szPath[sz++] = RTPATH_DELIMITER;
    7937     szPath[sz] = 0;
    7938     char *cmd = szPath + sz;
    7939     sz = RTPATH_MAX - sz;
    7940 
    7941     int vrc = VINF_SUCCESS;
    7942     RTPROCESS pid = NIL_RTPROCESS;
    7943 
    7944     RTENV env = RTENV_DEFAULT;
    7945 
    7946     if (!strEnvironment.isEmpty())
    7947     {
    7948         char *newEnvStr = NULL;
    7949 
    7950         do
    7951         {
    7952             /* clone the current environment */
    7953             int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
    7954             AssertRCBreakStmt(vrc2, vrc = vrc2);
    7955 
    7956             newEnvStr = RTStrDup(strEnvironment.c_str());
    7957             AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
    7958 
    7959             /* put new variables to the environment
    7960              * (ignore empty variable names here since RTEnv API
    7961              * intentionally doesn't do that) */
    7962             char *var = newEnvStr;
    7963             for (char *p = newEnvStr; *p; ++p)
    7964             {
    7965                 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
    7966                 {
    7967                     *p = '\0';
    7968                     if (*var)
    7969                     {
    7970                         char *val = strchr(var, '=');
    7971                         if (val)
    7972                         {
    7973                             *val++ = '\0';
    7974                             vrc2 = RTEnvSetEx(env, var, val);
    7975                         }
    7976                         else
    7977                             vrc2 = RTEnvUnsetEx(env, var);
    7978                         if (RT_FAILURE(vrc2))
    7979                             break;
    7980                     }
    7981                     var = p + 1;
    7982                 }
    7983             }
    7984             if (RT_SUCCESS(vrc2) && *var)
    7985                 vrc2 = RTEnvPutEx(env, var);
    7986 
    7987             AssertRCBreakStmt(vrc2, vrc = vrc2);
    7988         }
    7989         while (0);
    7990 
    7991         if (newEnvStr != NULL)
    7992             RTStrFree(newEnvStr);
    7993     }
    7994 
    7995 #ifdef VBOX_WITH_QTGUI
    7996     if (strFrontend == "gui" || strFrontend == "GUI/Qt")
    7997     {
    7998 # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
    7999         const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
    8000 # else
    8001         const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
    8002 # endif
    8003         Assert(sz >= sizeof(VirtualBox_exe));
    8004         strcpy(cmd, VirtualBox_exe);
    8005 
    8006         Utf8Str idStr = mData->mUuid.toString();
    8007         const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
    8008         vrc = RTProcCreate(szPath, args, env, 0, &pid);
    8009     }
    8010 #else /* !VBOX_WITH_QTGUI */
    8011     if (0)
    8012         ;
    8013 #endif /* VBOX_WITH_QTGUI */
    8014 
    8015     else
    8016 
    8017 #ifdef VBOX_WITH_VBOXSDL
    8018     if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
    8019     {
    8020         const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
    8021         Assert(sz >= sizeof(VBoxSDL_exe));
    8022         strcpy(cmd, VBoxSDL_exe);
    8023 
    8024         Utf8Str idStr = mData->mUuid.toString();
    8025         const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
    8026         vrc = RTProcCreate(szPath, args, env, 0, &pid);
    8027     }
    8028 #else /* !VBOX_WITH_VBOXSDL */
    8029     if (0)
    8030         ;
    8031 #endif /* !VBOX_WITH_VBOXSDL */
    8032 
    8033     else
    8034 
    8035 #ifdef VBOX_WITH_HEADLESS
    8036     if (   strFrontend == "headless"
    8037         || strFrontend == "capture"
    8038         || strFrontend == "vrdp" /* Deprecated. Same as headless. */
    8039        )
    8040     {
    8041         /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
    8042          * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
    8043          * and a VM works even if the server has not been installed.
    8044          * So in 4.0 the "headless" behavior remains the same for default VBox installations.
    8045          * Only if a VRDE has been installed and the VM enables it, the "headless" will work
    8046          * differently in 4.0 and 3.x.
    8047          */
    8048         const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
    8049         Assert(sz >= sizeof(VBoxHeadless_exe));
    8050         strcpy(cmd, VBoxHeadless_exe);
    8051 
    8052         Utf8Str idStr = mData->mUuid.toString();
    8053         /* Leave space for "--capture" arg. */
    8054         const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
    8055                                        "--startvm", idStr.c_str(),
    8056                                        "--vrde", "config",
    8057                                        0, /* For "--capture". */
    8058                                        0 };
    8059         if (strFrontend == "capture")
    8060         {
    8061             unsigned pos = RT_ELEMENTS(args) - 2;
    8062             args[pos] = "--capture";
    8063         }
    8064         vrc = RTProcCreate(szPath, args, env,
    8065 #ifdef RT_OS_WINDOWS
    8066                 RTPROC_FLAGS_NO_WINDOW
    8067 #else
    8068                 0
    8069 #endif
    8070                 , &pid);
    8071     }
    8072 #else /* !VBOX_WITH_HEADLESS */
    8073     if (0)
    8074         ;
    8075 #endif /* !VBOX_WITH_HEADLESS */
    8076     else
    8077     {
    8078         RTEnvDestroy(env);
    8079         return setError(E_INVALIDARG,
    8080                         tr("Invalid frontend name: '%s'"),
    8081                         strFrontend.c_str());
    8082     }
    8083 
    8084     RTEnvDestroy(env);
    8085 
    8086     if (RT_FAILURE(vrc))
    8087         return setError(VBOX_E_IPRT_ERROR,
    8088                         tr("Could not launch a process for the machine '%s' (%Rrc)"),
    8089                         mUserData->s.strName.c_str(), vrc);
    8090 
    8091     LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
    8092 
    8093     /*
    8094      *  Note that we don't release the lock here before calling the client,
    8095      *  because it doesn't need to call us back if called with a NULL argument.
    8096      *  Releasing the lock here is dangerous because we didn't prepare the
    8097      *  launch data yet, but the client we've just started may happen to be
    8098      *  too fast and call openSession() that will fail (because of PID, etc.),
    8099      *  so that the Machine will never get out of the Spawning session state.
    8100      */
    8101 
    8102     /* inform the session that it will be a remote one */
    8103     LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
    8104     HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
    8105     LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
    8106 
    8107     if (FAILED(rc))
    8108     {
    8109         /* restore the session state */
    8110         mData->mSession.mState = SessionState_Unlocked;
    8111         /* The failure may occur w/o any error info (from RPC), so provide one */
    8112         return setError(VBOX_E_VM_ERROR,
    8113                         tr("Failed to assign the machine to the session (%Rrc)"), rc);
    8114     }
    8115 
    8116     /* attach launch data to the machine */
    8117     Assert(mData->mSession.mPID == NIL_RTPROCESS);
    8118     mData->mSession.mRemoteControls.push_back(aControl);
    8119     mData->mSession.mProgress = aProgress;
    8120     mData->mSession.mPID = pid;
    8121     mData->mSession.mState = SessionState_Spawning;
    8122     mData->mSession.mType = strFrontend;
    8123 
    8124     LogFlowThisFuncLeave();
    8125     return S_OK;
    8126 }
    8127 
    8128 /**
    8129  * Returns @c true if the given machine has an open direct session and returns
    8130  * the session machine instance and additional session data (on some platforms)
    8131  * if so.
    8132  *
    8133  * Note that when the method returns @c false, the arguments remain unchanged.
    8134  *
    8135  * @param aMachine  Session machine object.
    8136  * @param aControl  Direct session control object (optional).
    8137  * @param aIPCSem   Mutex IPC semaphore handle for this machine (optional).
    8138  *
    8139  * @note locks this object for reading.
    8140  */
     40
     41Machine::ClientToken::ClientToken()
     42{
     43    AssertReleaseFailed();
     44}
     45
     46Machine::ClientToken::~ClientToken()
     47{
    814148#if defined(RT_OS_WINDOWS)
    8142 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    8143                             ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    8144                             HANDLE *aIPCSem /*= NULL*/,
    8145                             bool aAllowClosing /*= false*/)
     49    if (mClientToken)
     50        ::CloseHandle(mClientToken);
    814651#elif defined(RT_OS_OS2)
    8147 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    8148                             ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    8149                             HMTX *aIPCSem /*= NULL*/,
    8150                             bool aAllowClosing /*= false*/)
    8151 #else
    8152 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    8153                             ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    8154                             bool aAllowClosing /*= false*/)
    8155 #endif
    8156 {
    8157     AutoLimitedCaller autoCaller(this);
    8158     AssertComRCReturn(autoCaller.rc(), false);
    8159 
    8160     /* just return false for inaccessible machines */
    8161     if (autoCaller.state() != Ready)
    8162         return false;
    8163 
    8164     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    8165 
    8166     if (    mData->mSession.mState == SessionState_Locked
    8167          || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
    8168        )
    8169     {
    8170         AssertReturn(!mData->mSession.mMachine.isNull(), false);
    8171 
    8172         aMachine = mData->mSession.mMachine;
    8173 
    8174         if (aControl != NULL)
    8175             *aControl = mData->mSession.mDirectControl;
    8176 
    8177 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8178         /* Additional session data */
    8179         if (aIPCSem != NULL)
    8180             *aIPCSem = aMachine->mIPCSem;
    8181 #endif
    8182         return true;
    8183     }
    8184 
    8185     return false;
    8186 }
    8187 
    8188 /**
    8189  * Returns @c true if the given machine has an spawning direct session and
    8190  * returns and additional session data (on some platforms) if so.
    8191  *
    8192  * Note that when the method returns @c false, the arguments remain unchanged.
    8193  *
    8194  * @param aPID  PID of the spawned direct session process.
    8195  *
    8196  * @note locks this object for reading.
    8197  */
    8198 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8199 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
    8200 #else
    8201 bool Machine::isSessionSpawning()
    8202 #endif
    8203 {
    8204     AutoLimitedCaller autoCaller(this);
    8205     AssertComRCReturn(autoCaller.rc(), false);
    8206 
    8207     /* just return false for inaccessible machines */
    8208     if (autoCaller.state() != Ready)
    8209         return false;
    8210 
    8211     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    8212 
    8213     if (mData->mSession.mState == SessionState_Spawning)
    8214     {
    8215 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8216         /* Additional session data */
    8217         if (aPID != NULL)
    8218         {
    8219             AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
    8220             *aPID = mData->mSession.mPID;
    8221         }
    8222 #endif
    8223         return true;
    8224     }
    8225 
    8226     return false;
    8227 }
    8228 
    8229 /**
    8230  * Called from the client watcher thread to check for unexpected client process
    8231  * death during Session_Spawning state (e.g. before it successfully opened a
    8232  * direct session).
    8233  *
    8234  * On Win32 and on OS/2, this method is called only when we've got the
    8235  * direct client's process termination notification, so it always returns @c
    8236  * true.
    8237  *
    8238  * On other platforms, this method returns @c true if the client process is
    8239  * terminated and @c false if it's still alive.
    8240  *
    8241  * @note Locks this object for writing.
    8242  */
    8243 bool Machine::checkForSpawnFailure()
    8244 {
    8245     AutoCaller autoCaller(this);
    8246     if (!autoCaller.isOk())
    8247     {
    8248         /* nothing to do */
    8249         LogFlowThisFunc(("Already uninitialized!\n"));
    8250         return true;
    8251     }
    8252 
    8253     /* VirtualBox::addProcessToReap() needs a write lock */
    8254     AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
    8255 
    8256     if (mData->mSession.mState != SessionState_Spawning)
    8257     {
    8258         /* nothing to do */
    8259         LogFlowThisFunc(("Not spawning any more!\n"));
    8260         return true;
    8261     }
    8262 
    8263     HRESULT rc = S_OK;
    8264 
    8265 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8266 
    8267     /* the process was already unexpectedly terminated, we just need to set an
    8268      * error and finalize session spawning */
    8269     rc = setError(E_FAIL,
    8270                   tr("The virtual machine '%s' has terminated unexpectedly during startup"),
    8271                   getName().c_str());
    8272 #else
    8273 
    8274     /* PID not yet initialized, skip check. */
    8275     if (mData->mSession.mPID == NIL_RTPROCESS)
    8276         return false;
    8277 
    8278     RTPROCSTATUS status;
    8279     int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
    8280                            &status);
    8281 
    8282     if (vrc != VERR_PROCESS_RUNNING)
    8283     {
    8284         if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
    8285             rc = setError(E_FAIL,
    8286                           tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
    8287                           getName().c_str(), status.iStatus);
    8288         else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
    8289             rc = setError(E_FAIL,
    8290                           tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
    8291                           getName().c_str(), status.iStatus);
    8292         else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
    8293             rc = setError(E_FAIL,
    8294                           tr("The virtual machine '%s' has terminated abnormally"),
    8295                           getName().c_str(), status.iStatus);
    8296         else
    8297             rc = setError(E_FAIL,
    8298                           tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
    8299                           getName().c_str(), rc);
    8300     }
    8301 
    8302 #endif
    8303 
    8304     if (FAILED(rc))
    8305     {
    8306         /* Close the remote session, remove the remote control from the list
    8307          * and reset session state to Closed (@note keep the code in sync with
    8308          * the relevant part in checkForSpawnFailure()). */
    8309 
    8310         Assert(mData->mSession.mRemoteControls.size() == 1);
    8311         if (mData->mSession.mRemoteControls.size() == 1)
    8312         {
    8313             ErrorInfoKeeper eik;
    8314             mData->mSession.mRemoteControls.front()->Uninitialize();
    8315         }
    8316 
    8317         mData->mSession.mRemoteControls.clear();
    8318         mData->mSession.mState = SessionState_Unlocked;
    8319 
    8320         /* finalize the progress after setting the state */
    8321         if (!mData->mSession.mProgress.isNull())
    8322         {
    8323             mData->mSession.mProgress->notifyComplete(rc);
    8324             mData->mSession.mProgress.setNull();
    8325         }
    8326 
    8327         mParent->addProcessToReap(mData->mSession.mPID);
    8328         mData->mSession.mPID = NIL_RTPROCESS;
    8329 
    8330         mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
    8331         return true;
    8332     }
    8333 
    8334     return false;
    8335 }
    8336 
    8337 /**
    8338  *  Checks whether the machine can be registered. If so, commits and saves
    8339  *  all settings.
    8340  *
    8341  *  @note Must be called from mParent's write lock. Locks this object and
    8342  *  children for writing.
    8343  */
    8344 HRESULT Machine::prepareRegister()
    8345 {
    8346     AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
    8347 
    8348     AutoLimitedCaller autoCaller(this);
    8349     AssertComRCReturnRC(autoCaller.rc());
    8350 
    8351     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8352 
    8353     /* wait for state dependents to drop to zero */
    8354     ensureNoStateDependencies();
    8355 
    8356     if (!mData->mAccessible)
    8357         return setError(VBOX_E_INVALID_OBJECT_STATE,
    8358                         tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
    8359                         mUserData->s.strName.c_str(),
    8360                         mData->mUuid.toString().c_str());
    8361 
    8362     AssertReturn(autoCaller.state() == Ready, E_FAIL);
    8363 
    8364     if (mData->mRegistered)
    8365         return setError(VBOX_E_INVALID_OBJECT_STATE,
    8366                         tr("The machine '%s' with UUID {%s} is already registered"),
    8367                         mUserData->s.strName.c_str(),
    8368                         mData->mUuid.toString().c_str());
    8369 
    8370     HRESULT rc = S_OK;
    8371 
    8372     // Ensure the settings are saved. If we are going to be registered and
    8373     // no config file exists yet, create it by calling saveSettings() too.
    8374     if (    (mData->flModifications)
    8375          || (!mData->pMachineConfigFile->fileExists())
    8376        )
    8377     {
    8378         rc = saveSettings(NULL);
    8379                 // no need to check whether VirtualBox.xml needs saving too since
    8380                 // we can't have a machine XML file rename pending
    8381         if (FAILED(rc)) return rc;
    8382     }
    8383 
    8384     /* more config checking goes here */
    8385 
    8386     if (SUCCEEDED(rc))
    8387     {
    8388         /* we may have had implicit modifications we want to fix on success */
    8389         commit();
    8390 
    8391         mData->mRegistered = true;
    8392     }
    8393     else
    8394     {
    8395         /* we may have had implicit modifications we want to cancel on failure*/
    8396         rollback(false /* aNotify */);
    8397     }
    8398 
    8399     return rc;
    8400 }
    8401 
    8402 /**
    8403  * Increases the number of objects dependent on the machine state or on the
    8404  * registered state. Guarantees that these two states will not change at least
    8405  * until #releaseStateDependency() is called.
    8406  *
    8407  * Depending on the @a aDepType value, additional state checks may be made.
    8408  * These checks will set extended error info on failure. See
    8409  * #checkStateDependency() for more info.
    8410  *
    8411  * If this method returns a failure, the dependency is not added and the caller
    8412  * is not allowed to rely on any particular machine state or registration state
    8413  * value and may return the failed result code to the upper level.
    8414  *
    8415  * @param aDepType      Dependency type to add.
    8416  * @param aState        Current machine state (NULL if not interested).
    8417  * @param aRegistered   Current registered state (NULL if not interested).
    8418  *
    8419  * @note Locks this object for writing.
    8420  */
    8421 HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
    8422                                     MachineState_T *aState /* = NULL */,
    8423                                     BOOL *aRegistered /* = NULL */)
    8424 {
    8425     AutoCaller autoCaller(this);
    8426     AssertComRCReturnRC(autoCaller.rc());
    8427 
    8428     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8429 
    8430     HRESULT rc = checkStateDependency(aDepType);
    8431     if (FAILED(rc)) return rc;
    8432 
    8433     {
    8434         if (mData->mMachineStateChangePending != 0)
    8435         {
    8436             /* ensureNoStateDependencies() is waiting for state dependencies to
    8437              * drop to zero so don't add more. It may make sense to wait a bit
    8438              * and retry before reporting an error (since the pending state
    8439              * transition should be really quick) but let's just assert for
    8440              * now to see if it ever happens on practice. */
    8441 
    8442             AssertFailed();
    8443 
    8444             return setError(E_ACCESSDENIED,
    8445                             tr("Machine state change is in progress. Please retry the operation later."));
    8446         }
    8447 
    8448         ++mData->mMachineStateDeps;
    8449         Assert(mData->mMachineStateDeps != 0 /* overflow */);
    8450     }
    8451 
    8452     if (aState)
    8453         *aState = mData->mMachineState;
    8454     if (aRegistered)
    8455         *aRegistered = mData->mRegistered;
    8456 
    8457     return S_OK;
    8458 }
    8459 
    8460 /**
    8461  * Decreases the number of objects dependent on the machine state.
    8462  * Must always complete the #addStateDependency() call after the state
    8463  * dependency is no more necessary.
    8464  */
    8465 void Machine::releaseStateDependency()
    8466 {
    8467     AutoCaller autoCaller(this);
    8468     AssertComRCReturnVoid(autoCaller.rc());
    8469 
    8470     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8471 
    8472     /* releaseStateDependency() w/o addStateDependency()? */
    8473     AssertReturnVoid(mData->mMachineStateDeps != 0);
    8474     -- mData->mMachineStateDeps;
    8475 
    8476     if (mData->mMachineStateDeps == 0)
    8477     {
    8478         /* inform ensureNoStateDependencies() that there are no more deps */
    8479         if (mData->mMachineStateChangePending != 0)
    8480         {
    8481             Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
    8482             RTSemEventMultiSignal (mData->mMachineStateDepsSem);
    8483         }
    8484     }
    8485 }
    8486 
    8487 Utf8Str Machine::getExtraData(const Utf8Str &strKey)
    8488 {
    8489     /* start with nothing found */
    8490     Utf8Str strResult("");
    8491 
    8492     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    8493 
    8494     settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
    8495     if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
    8496         // found:
    8497         strResult = it->second; // source is a Utf8Str
    8498 
    8499     return strResult;
    8500 }
    8501 
    8502 // protected methods
    8503 /////////////////////////////////////////////////////////////////////////////
    8504 
    8505 /**
    8506  *  Performs machine state checks based on the @a aDepType value. If a check
    8507  *  fails, this method will set extended error info, otherwise it will return
    8508  *  S_OK. It is supposed, that on failure, the caller will immediately return
    8509  *  the return value of this method to the upper level.
    8510  *
    8511  *  When @a aDepType is AnyStateDep, this method always returns S_OK.
    8512  *
    8513  *  When @a aDepType is MutableStateDep, this method returns S_OK only if the
    8514  *  current state of this machine object allows to change settings of the
    8515  *  machine (i.e. the machine is not registered, or registered but not running
    8516  *  and not saved). It is useful to call this method from Machine setters
    8517  *  before performing any change.
    8518  *
    8519  *  When @a aDepType is MutableOrSavedStateDep, this method behaves the same
    8520  *  as for MutableStateDep except that if the machine is saved, S_OK is also
    8521  *  returned. This is useful in setters which allow changing machine
    8522  *  properties when it is in the saved state.
    8523  *
    8524  *  When @a aDepType is OfflineStateDep, this method returns S_OK if the
    8525  *  state is one of the 4 offline states (PoweredOff, Saved, Teleported,
    8526  *  Aborted).
    8527  *
    8528  *  @param aDepType     Dependency type to check.
    8529  *
    8530  *  @note Non Machine based classes should use #addStateDependency() and
    8531  *  #releaseStateDependency() methods or the smart AutoStateDependency
    8532  *  template.
    8533  *
    8534  *  @note This method must be called from under this object's read or write
    8535  *        lock.
    8536  */
    8537 HRESULT Machine::checkStateDependency(StateDependency aDepType)
    8538 {
    8539     switch (aDepType)
    8540     {
    8541         case AnyStateDep:
    8542         {
    8543             break;
    8544         }
    8545         case MutableStateDep:
    8546         {
    8547             if (   mData->mRegistered
    8548                 && (   !isSessionMachine()  /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
    8549                     || (   mData->mMachineState != MachineState_Paused
    8550                         && mData->mMachineState != MachineState_Running
    8551                         && mData->mMachineState != MachineState_Aborted
    8552                         && mData->mMachineState != MachineState_Teleported
    8553                         && mData->mMachineState != MachineState_PoweredOff
    8554                        )
    8555                    )
    8556                )
    8557                 return setError(VBOX_E_INVALID_VM_STATE,
    8558                                 tr("The machine is not mutable (state is %s)"),
    8559                                 Global::stringifyMachineState(mData->mMachineState));
    8560             break;
    8561         }
    8562         case MutableOrSavedStateDep:
    8563         {
    8564             if (   mData->mRegistered
    8565                 && (   !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
    8566                     || (   mData->mMachineState != MachineState_Paused
    8567                         && mData->mMachineState != MachineState_Running
    8568                         && mData->mMachineState != MachineState_Aborted
    8569                         && mData->mMachineState != MachineState_Teleported
    8570                         && mData->mMachineState != MachineState_Saved
    8571                         && mData->mMachineState != MachineState_PoweredOff
    8572                        )
    8573                    )
    8574                )
    8575                 return setError(VBOX_E_INVALID_VM_STATE,
    8576                                 tr("The machine is not mutable (state is %s)"),
    8577                                 Global::stringifyMachineState(mData->mMachineState));
    8578             break;
    8579         }
    8580         case OfflineStateDep:
    8581         {
    8582             if (   mData->mRegistered
    8583                 && (   !isSessionMachine()
    8584                     || (   mData->mMachineState != MachineState_PoweredOff
    8585                         && mData->mMachineState != MachineState_Saved
    8586                         && mData->mMachineState != MachineState_Aborted
    8587                         && mData->mMachineState != MachineState_Teleported
    8588                        )
    8589                    )
    8590                )
    8591                 return setError(VBOX_E_INVALID_VM_STATE,
    8592                                 tr("The machine is not offline (state is %s)"),
    8593                                 Global::stringifyMachineState(mData->mMachineState));
    8594             break;
    8595         }
    8596     }
    8597 
    8598     return S_OK;
    8599 }
    8600 
    8601 /**
    8602  * Helper to initialize all associated child objects and allocate data
    8603  * structures.
    8604  *
    8605  * This method must be called as a part of the object's initialization procedure
    8606  * (usually done in the #init() method).
    8607  *
    8608  * @note Must be called only from #init() or from #registeredInit().
    8609  */
    8610 HRESULT Machine::initDataAndChildObjects()
    8611 {
    8612     AutoCaller autoCaller(this);
    8613     AssertComRCReturnRC(autoCaller.rc());
    8614     AssertComRCReturn(autoCaller.state() == InInit ||
    8615                       autoCaller.state() == Limited, E_FAIL);
    8616 
    8617     AssertReturn(!mData->mAccessible, E_FAIL);
    8618 
    8619     /* allocate data structures */
    8620     mSSData.allocate();
    8621     mUserData.allocate();
    8622     mHWData.allocate();
    8623     mMediaData.allocate();
    8624     mStorageControllers.allocate();
    8625     mUSBControllers.allocate();
    8626 
    8627     /* initialize mOSTypeId */
    8628     mUserData->s.strOsType = mParent->getUnknownOSType()->id();
    8629 
    8630     /* create associated BIOS settings object */
    8631     unconst(mBIOSSettings).createObject();
    8632     mBIOSSettings->init(this);
    8633 
    8634     /* create an associated VRDE object (default is disabled) */
    8635     unconst(mVRDEServer).createObject();
    8636     mVRDEServer->init(this);
    8637 
    8638     /* create associated serial port objects */
    8639     for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    8640     {
    8641         unconst(mSerialPorts[slot]).createObject();
    8642         mSerialPorts[slot]->init(this, slot);
    8643     }
    8644 
    8645     /* create associated parallel port objects */
    8646     for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    8647     {
    8648         unconst(mParallelPorts[slot]).createObject();
    8649         mParallelPorts[slot]->init(this, slot);
    8650     }
    8651 
    8652     /* create the audio adapter object (always present, default is disabled) */
    8653     unconst(mAudioAdapter).createObject();
    8654     mAudioAdapter->init(this);
    8655 
    8656     /* create the USB device filters object (always present) */
    8657     unconst(mUSBDeviceFilters).createObject();
    8658     mUSBDeviceFilters->init(this);
    8659 
    8660     /* create associated network adapter objects */
    8661     mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
    8662     for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
    8663     {
    8664         unconst(mNetworkAdapters[slot]).createObject();
    8665         mNetworkAdapters[slot]->init(this, slot);
    8666     }
    8667 
    8668     /* create the bandwidth control */
    8669     unconst(mBandwidthControl).createObject();
    8670     mBandwidthControl->init(this);
    8671 
    8672     return S_OK;
    8673 }
    8674 
    8675 /**
    8676  * Helper to uninitialize all associated child objects and to free all data
    8677  * structures.
    8678  *
    8679  * This method must be called as a part of the object's uninitialization
    8680  * procedure (usually done in the #uninit() method).
    8681  *
    8682  * @note Must be called only from #uninit() or from #registeredInit().
    8683  */
    8684 void Machine::uninitDataAndChildObjects()
    8685 {
    8686     AutoCaller autoCaller(this);
    8687     AssertComRCReturnVoid(autoCaller.rc());
    8688     AssertComRCReturnVoid(    autoCaller.state() == InUninit
    8689                            || autoCaller.state() == Limited);
    8690 
    8691     /* tell all our other child objects we've been uninitialized */
    8692     if (mBandwidthControl)
    8693     {
    8694         mBandwidthControl->uninit();
    8695         unconst(mBandwidthControl).setNull();
    8696     }
    8697 
    8698     for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
    8699     {
    8700         if (mNetworkAdapters[slot])
    8701         {
    8702             mNetworkAdapters[slot]->uninit();
    8703             unconst(mNetworkAdapters[slot]).setNull();
    8704         }
    8705     }
    8706 
    8707     if (mUSBDeviceFilters)
    8708     {
    8709         mUSBDeviceFilters->uninit();
    8710         unconst(mUSBDeviceFilters).setNull();
    8711     }
    8712 
    8713     if (mAudioAdapter)
    8714     {
    8715         mAudioAdapter->uninit();
    8716         unconst(mAudioAdapter).setNull();
    8717     }
    8718 
    8719     for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    8720     {
    8721         if (mParallelPorts[slot])
    8722         {
    8723             mParallelPorts[slot]->uninit();
    8724             unconst(mParallelPorts[slot]).setNull();
    8725         }
    8726     }
    8727 
    8728     for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    8729     {
    8730         if (mSerialPorts[slot])
    8731         {
    8732             mSerialPorts[slot]->uninit();
    8733             unconst(mSerialPorts[slot]).setNull();
    8734         }
    8735     }
    8736 
    8737     if (mVRDEServer)
    8738     {
    8739         mVRDEServer->uninit();
    8740         unconst(mVRDEServer).setNull();
    8741     }
    8742 
    8743     if (mBIOSSettings)
    8744     {
    8745         mBIOSSettings->uninit();
    8746         unconst(mBIOSSettings).setNull();
    8747     }
    8748 
    8749     /* Deassociate media (only when a real Machine or a SnapshotMachine
    8750      * instance is uninitialized; SessionMachine instances refer to real
    8751      * Machine media). This is necessary for a clean re-initialization of
    8752      * the VM after successfully re-checking the accessibility state. Note
    8753      * that in case of normal Machine or SnapshotMachine uninitialization (as
    8754      * a result of unregistering or deleting the snapshot), outdated media
    8755      * attachments will already be uninitialized and deleted, so this
    8756      * code will not affect them. */
    8757     if (    !!mMediaData
    8758          && (!isSessionMachine())
    8759        )
    8760     {
    8761         for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    8762              it != mMediaData->mAttachments.end();
    8763              ++it)
    8764         {
    8765             ComObjPtr<Medium> pMedium = (*it)->getMedium();
    8766             if (pMedium.isNull())
    8767                 continue;
    8768             HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
    8769             AssertComRC(rc);
    8770         }
    8771     }
    8772 
    8773     if (!isSessionMachine() && !isSnapshotMachine())
    8774     {
    8775         // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
    8776         if (mData->mFirstSnapshot)
    8777         {
    8778             // snapshots tree is protected by machine write lock; strictly
    8779             // this isn't necessary here since we're deleting the entire
    8780             // machine, but otherwise we assert in Snapshot::uninit()
    8781             AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8782             mData->mFirstSnapshot->uninit();
    8783             mData->mFirstSnapshot.setNull();
    8784         }
    8785 
    8786         mData->mCurrentSnapshot.setNull();
    8787     }
    8788 
    8789     /* free data structures (the essential mData structure is not freed here
    8790      * since it may be still in use) */
    8791     mMediaData.free();
    8792     mStorageControllers.free();
    8793     mUSBControllers.free();
    8794     mHWData.free();
    8795     mUserData.free();
    8796     mSSData.free();
    8797 }
    8798 
    8799 /**
    8800  *  Returns a pointer to the Machine object for this machine that acts like a
    8801  *  parent for complex machine data objects such as shared folders, etc.
    8802  *
    8803  *  For primary Machine objects and for SnapshotMachine objects, returns this
    8804  *  object's pointer itself. For SessionMachine objects, returns the peer
    8805  *  (primary) machine pointer.
    8806  */
    8807 Machine* Machine::getMachine()
    8808 {
    8809     if (isSessionMachine())
    8810         return (Machine*)mPeer;
    8811     return this;
    8812 }
    8813 
    8814 /**
    8815  * Makes sure that there are no machine state dependents. If necessary, waits
    8816  * for the number of dependents to drop to zero.
    8817  *
    8818  * Make sure this method is called from under this object's write lock to
    8819  * guarantee that no new dependents may be added when this method returns
    8820  * control to the caller.
    8821  *
    8822  * @note Locks this object for writing. The lock will be released while waiting
    8823  *       (if necessary).
    8824  *
    8825  * @warning To be used only in methods that change the machine state!
    8826  */
    8827 void Machine::ensureNoStateDependencies()
    8828 {
    8829     AssertReturnVoid(isWriteLockOnCurrentThread());
    8830 
    8831     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8832 
    8833     /* Wait for all state dependents if necessary */
    8834     if (mData->mMachineStateDeps != 0)
    8835     {
    8836         /* lazy semaphore creation */
    8837         if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
    8838             RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
    8839 
    8840         LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
    8841                           mData->mMachineStateDeps));
    8842 
    8843         ++mData->mMachineStateChangePending;
    8844 
    8845         /* reset the semaphore before waiting, the last dependent will signal
    8846          * it */
    8847         RTSemEventMultiReset(mData->mMachineStateDepsSem);
    8848 
    8849         alock.release();
    8850 
    8851         RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
    8852 
    8853         alock.acquire();
    8854 
    8855         -- mData->mMachineStateChangePending;
    8856     }
    8857 }
    8858 
    8859 /**
    8860  * Changes the machine state and informs callbacks.
    8861  *
    8862  * This method is not intended to fail so it either returns S_OK or asserts (and
    8863  * returns a failure).
    8864  *
    8865  * @note Locks this object for writing.
    8866  */
    8867 HRESULT Machine::setMachineState(MachineState_T aMachineState)
    8868 {
    8869     LogFlowThisFuncEnter();
    8870     LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
    8871 
    8872     AutoCaller autoCaller(this);
    8873     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    8874 
    8875     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    8876 
    8877     /* wait for state dependents to drop to zero */
    8878     ensureNoStateDependencies();
    8879 
    8880     if (mData->mMachineState != aMachineState)
    8881     {
    8882         mData->mMachineState = aMachineState;
    8883 
    8884         RTTimeNow(&mData->mLastStateChange);
    8885 
    8886         mParent->onMachineStateChange(mData->mUuid, aMachineState);
    8887     }
    8888 
    8889     LogFlowThisFuncLeave();
    8890     return S_OK;
    8891 }
    8892 
    8893 /**
    8894  *  Searches for a shared folder with the given logical name
    8895  *  in the collection of shared folders.
    8896  *
    8897  *  @param aName            logical name of the shared folder
    8898  *  @param aSharedFolder    where to return the found object
    8899  *  @param aSetError        whether to set the error info if the folder is
    8900  *                          not found
    8901  *  @return
    8902  *      S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
    8903  *
    8904  *  @note
    8905  *      must be called from under the object's lock!
    8906  */
    8907 HRESULT Machine::findSharedFolder(const Utf8Str &aName,
    8908                                   ComObjPtr<SharedFolder> &aSharedFolder,
    8909                                   bool aSetError /* = false */)
    8910 {
    8911     HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
    8912     for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
    8913         it != mHWData->mSharedFolders.end();
    8914         ++it)
    8915     {
    8916         SharedFolder *pSF = *it;
    8917         AutoCaller autoCaller(pSF);
    8918         if (pSF->getName() == aName)
    8919         {
    8920             aSharedFolder = pSF;
    8921             rc = S_OK;
    8922             break;
    8923         }
    8924     }
    8925 
    8926     if (aSetError && FAILED(rc))
    8927         setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
    8928 
    8929     return rc;
    8930 }
    8931 
    8932 /**
    8933  * Initializes all machine instance data from the given settings structures
    8934  * from XML. The exception is the machine UUID which needs special handling
    8935  * depending on the caller's use case, so the caller needs to set that herself.
    8936  *
    8937  * This gets called in several contexts during machine initialization:
    8938  *
    8939  * -- When machine XML exists on disk already and needs to be loaded into memory,
    8940  *    for example, from registeredInit() to load all registered machines on
    8941  *    VirtualBox startup. In this case, puuidRegistry is NULL because the media
    8942  *    attached to the machine should be part of some media registry already.
    8943  *
    8944  * -- During OVF import, when a machine config has been constructed from an
    8945  *    OVF file. In this case, puuidRegistry is set to the machine UUID to
    8946  *    ensure that the media listed as attachments in the config (which have
    8947  *    been imported from the OVF) receive the correct registry ID.
    8948  *
    8949  * -- During VM cloning.
    8950  *
    8951  * @param config Machine settings from XML.
    8952  * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
    8953  * @return
    8954  */
    8955 HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
    8956                                              const Guid *puuidRegistry)
    8957 {
    8958     // copy name, description, OS type, teleporter, UTC etc.
    8959     #define DECODE_STR_MAX _1M
    8960     mUserData->s = config.machineUserData;
    8961 
    8962     // Decode the Icon overide data from config userdata and set onto Machine.
    8963     const char* pszStr = config.machineUserData.ovIcon.c_str();
    8964     ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
    8965     if (cbOut > DECODE_STR_MAX)
    8966         return setError(E_FAIL,
    8967                         tr("Icon Data too long.'%d' > '%d'"),
    8968                         cbOut,
    8969                         DECODE_STR_MAX);
    8970     com::SafeArray<BYTE> iconByte(cbOut);
    8971     HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
    8972     if (FAILED(rc))
    8973         return setError(E_FAIL,
    8974                         tr("Failure to Decode Icon Data. '%s' (%d)"),
    8975                         pszStr,
    8976                         rc);
    8977     COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
    8978 
    8979     // look up the object by Id to check it is valid
    8980     ComPtr<IGuestOSType> guestOSType;
    8981     rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
    8982                                          guestOSType.asOutParam());
    8983     if (FAILED(rc)) return rc;
    8984 
    8985     // stateFile (optional)
    8986     if (config.strStateFile.isEmpty())
    8987         mSSData->strStateFilePath.setNull();
    8988     else
    8989     {
    8990         Utf8Str stateFilePathFull(config.strStateFile);
    8991         int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
    8992         if (RT_FAILURE(vrc))
    8993             return setError(E_FAIL,
    8994                             tr("Invalid saved state file path '%s' (%Rrc)"),
    8995                             config.strStateFile.c_str(),
    8996                             vrc);
    8997         mSSData->strStateFilePath = stateFilePathFull;
    8998     }
    8999 
    9000     // snapshot folder needs special processing so set it again
    9001     rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
    9002     if (FAILED(rc)) return rc;
    9003 
    9004     /* Copy the extra data items (Not in any case config is already the same as
    9005      * mData->pMachineConfigFile, like when the xml files are read from disk. So
    9006      * make sure the extra data map is copied). */
    9007     mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
    9008 
    9009     /* currentStateModified (optional, default is true) */
    9010     mData->mCurrentStateModified = config.fCurrentStateModified;
    9011 
    9012     mData->mLastStateChange = config.timeLastStateChange;
    9013 
    9014     /*
    9015      *  note: all mUserData members must be assigned prior this point because
    9016      *  we need to commit changes in order to let mUserData be shared by all
    9017      *  snapshot machine instances.
    9018      */
    9019     mUserData.commitCopy();
    9020 
    9021     // machine registry, if present (must be loaded before snapshots)
    9022     if (config.canHaveOwnMediaRegistry())
    9023     {
    9024         // determine machine folder
    9025         Utf8Str strMachineFolder = getSettingsFileFull();
    9026         strMachineFolder.stripFilename();
    9027         rc = mParent->initMedia(getId(),         // media registry ID == machine UUID
    9028                                 config.mediaRegistry,
    9029                                 strMachineFolder);
    9030         if (FAILED(rc)) return rc;
    9031     }
    9032 
    9033     /* Snapshot node (optional) */
    9034     size_t cRootSnapshots;
    9035     if ((cRootSnapshots = config.llFirstSnapshot.size()))
    9036     {
    9037         // there must be only one root snapshot
    9038         Assert(cRootSnapshots == 1);
    9039 
    9040         const settings::Snapshot &snap = config.llFirstSnapshot.front();
    9041 
    9042         rc = loadSnapshot(snap,
    9043                           config.uuidCurrentSnapshot,
    9044                           NULL);        // no parent == first snapshot
    9045         if (FAILED(rc)) return rc;
    9046     }
    9047 
    9048     // hardware data
    9049     rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
    9050     if (FAILED(rc)) return rc;
    9051 
    9052     // load storage controllers
    9053     rc = loadStorageControllers(config.storageMachine,
    9054                                 puuidRegistry,
    9055                                 NULL /* puuidSnapshot */);
    9056     if (FAILED(rc)) return rc;
    9057 
    9058     /*
    9059      *  NOTE: the assignment below must be the last thing to do,
    9060      *  otherwise it will be not possible to change the settings
    9061      *  somewhere in the code above because all setters will be
    9062      *  blocked by checkStateDependency(MutableStateDep).
    9063      */
    9064 
    9065     /* set the machine state to Aborted or Saved when appropriate */
    9066     if (config.fAborted)
    9067     {
    9068         mSSData->strStateFilePath.setNull();
    9069 
    9070         /* no need to use setMachineState() during init() */
    9071         mData->mMachineState = MachineState_Aborted;
    9072     }
    9073     else if (!mSSData->strStateFilePath.isEmpty())
    9074     {
    9075         /* no need to use setMachineState() during init() */
    9076         mData->mMachineState = MachineState_Saved;
    9077     }
    9078 
    9079     // after loading settings, we are no longer different from the XML on disk
    9080     mData->flModifications = 0;
    9081 
    9082     return S_OK;
    9083 }
    9084 
    9085 /**
    9086  *  Recursively loads all snapshots starting from the given.
    9087  *
    9088  *  @param aNode            <Snapshot> node.
    9089  *  @param aCurSnapshotId   Current snapshot ID from the settings file.
    9090  *  @param aParentSnapshot  Parent snapshot.
    9091  */
    9092 HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
    9093                               const Guid &aCurSnapshotId,
    9094                               Snapshot *aParentSnapshot)
    9095 {
    9096     AssertReturn(!isSnapshotMachine(), E_FAIL);
    9097     AssertReturn(!isSessionMachine(), E_FAIL);
    9098 
    9099     HRESULT rc = S_OK;
    9100 
    9101     Utf8Str strStateFile;
    9102     if (!data.strStateFile.isEmpty())
    9103     {
    9104         /* optional */
    9105         strStateFile = data.strStateFile;
    9106         int vrc = calculateFullPath(strStateFile, strStateFile);
    9107         if (RT_FAILURE(vrc))
    9108             return setError(E_FAIL,
    9109                             tr("Invalid saved state file path '%s' (%Rrc)"),
    9110                             strStateFile.c_str(),
    9111                             vrc);
    9112     }
    9113 
    9114     /* create a snapshot machine object */
    9115     ComObjPtr<SnapshotMachine> pSnapshotMachine;
    9116     pSnapshotMachine.createObject();
    9117     rc = pSnapshotMachine->initFromSettings(this,
    9118                                             data.hardware,
    9119                                             &data.debugging,
    9120                                             &data.autostart,
    9121                                             data.storage,
    9122                                             data.uuid.ref(),
    9123                                             strStateFile);
    9124     if (FAILED(rc)) return rc;
    9125 
    9126     /* create a snapshot object */
    9127     ComObjPtr<Snapshot> pSnapshot;
    9128     pSnapshot.createObject();
    9129     /* initialize the snapshot */
    9130     rc = pSnapshot->init(mParent, // VirtualBox object
    9131                          data.uuid,
    9132                          data.strName,
    9133                          data.strDescription,
    9134                          data.timestamp,
    9135                          pSnapshotMachine,
    9136                          aParentSnapshot);
    9137     if (FAILED(rc)) return rc;
    9138 
    9139     /* memorize the first snapshot if necessary */
    9140     if (!mData->mFirstSnapshot)
    9141         mData->mFirstSnapshot = pSnapshot;
    9142 
    9143     /* memorize the current snapshot when appropriate */
    9144     if (    !mData->mCurrentSnapshot
    9145          && pSnapshot->getId() == aCurSnapshotId
    9146        )
    9147         mData->mCurrentSnapshot = pSnapshot;
    9148 
    9149     // now create the children
    9150     for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
    9151          it != data.llChildSnapshots.end();
    9152          ++it)
    9153     {
    9154         const settings::Snapshot &childData = *it;
    9155         // recurse
    9156         rc = loadSnapshot(childData,
    9157                           aCurSnapshotId,
    9158                           pSnapshot);       // parent = the one we created above
    9159         if (FAILED(rc)) return rc;
    9160     }
    9161 
    9162     return rc;
    9163 }
    9164 
    9165 /**
    9166  *  Loads settings into mHWData.
    9167  *
    9168  *  @param data           Reference to the hardware settings.
    9169  *  @param pDbg           Pointer to the debugging settings.
    9170  *  @param pAutostart     Pointer to the autostart settings.
    9171  */
    9172 HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
    9173                               const settings::Autostart *pAutostart)
    9174 {
    9175     AssertReturn(!isSessionMachine(), E_FAIL);
    9176 
    9177     HRESULT rc = S_OK;
    9178 
    9179     try
    9180     {
    9181         /* The hardware version attribute (optional). */
    9182         mHWData->mHWVersion = data.strVersion;
    9183         mHWData->mHardwareUUID = data.uuid;
    9184 
    9185         mHWData->mHWVirtExEnabled             = data.fHardwareVirt;
    9186         mHWData->mHWVirtExExclusive           = data.fHardwareVirtExclusive;
    9187         mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
    9188         mHWData->mHWVirtExLargePagesEnabled   = data.fLargePages;
    9189         mHWData->mHWVirtExVPIDEnabled         = data.fVPID;
    9190         mHWData->mHWVirtExUXEnabled           = data.fUnrestrictedExecution;
    9191         mHWData->mHWVirtExForceEnabled        = data.fHardwareVirtForce;
    9192         mHWData->mPAEEnabled                  = data.fPAE;
    9193         mHWData->mSyntheticCpu                = data.fSyntheticCpu;
    9194         mHWData->mLongMode                    = data.enmLongMode;
    9195         mHWData->mCPUCount                    = data.cCPUs;
    9196         mHWData->mCPUHotPlugEnabled           = data.fCpuHotPlug;
    9197         mHWData->mCpuExecutionCap             = data.ulCpuExecutionCap;
    9198 
    9199         // cpu
    9200         if (mHWData->mCPUHotPlugEnabled)
    9201         {
    9202             for (settings::CpuList::const_iterator it = data.llCpus.begin();
    9203                 it != data.llCpus.end();
    9204                 ++it)
    9205             {
    9206                 const settings::Cpu &cpu = *it;
    9207 
    9208                 mHWData->mCPUAttached[cpu.ulId] = true;
    9209             }
    9210         }
    9211 
    9212         // cpuid leafs
    9213         for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
    9214             it != data.llCpuIdLeafs.end();
    9215             ++it)
    9216         {
    9217             const settings::CpuIdLeaf &leaf = *it;
    9218 
    9219             switch (leaf.ulId)
    9220             {
    9221             case 0x0:
    9222             case 0x1:
    9223             case 0x2:
    9224             case 0x3:
    9225             case 0x4:
    9226             case 0x5:
    9227             case 0x6:
    9228             case 0x7:
    9229             case 0x8:
    9230             case 0x9:
    9231             case 0xA:
    9232                 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
    9233                 break;
    9234 
    9235             case 0x80000000:
    9236             case 0x80000001:
    9237             case 0x80000002:
    9238             case 0x80000003:
    9239             case 0x80000004:
    9240             case 0x80000005:
    9241             case 0x80000006:
    9242             case 0x80000007:
    9243             case 0x80000008:
    9244             case 0x80000009:
    9245             case 0x8000000A:
    9246                 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
    9247                 break;
    9248 
    9249             default:
    9250                 /* just ignore */
    9251                 break;
    9252             }
    9253         }
    9254 
    9255         mHWData->mMemorySize = data.ulMemorySizeMB;
    9256         mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
    9257 
    9258         // boot order
    9259         for (size_t i = 0;
    9260              i < RT_ELEMENTS(mHWData->mBootOrder);
    9261              i++)
    9262         {
    9263             settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
    9264             if (it == data.mapBootOrder.end())
    9265                 mHWData->mBootOrder[i] = DeviceType_Null;
    9266             else
    9267                 mHWData->mBootOrder[i] = it->second;
    9268         }
    9269 
    9270         mHWData->mGraphicsControllerType = data.graphicsControllerType;
    9271         mHWData->mVRAMSize      = data.ulVRAMSizeMB;
    9272         mHWData->mMonitorCount  = data.cMonitors;
    9273         mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
    9274         mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
    9275         mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
    9276         mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
    9277         mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
    9278         for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
    9279             mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
    9280         AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
    9281         mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
    9282         mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
    9283         if (!data.strVideoCaptureFile.isEmpty())
    9284             calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
    9285         else
    9286             mHWData->mVideoCaptureFile.setNull();
    9287         mHWData->mFirmwareType = data.firmwareType;
    9288         mHWData->mPointingHIDType = data.pointingHIDType;
    9289         mHWData->mKeyboardHIDType = data.keyboardHIDType;
    9290         mHWData->mChipsetType = data.chipsetType;
    9291         mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
    9292         mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
    9293         mHWData->mHPETEnabled = data.fHPETEnabled;
    9294 
    9295         /* VRDEServer */
    9296         rc = mVRDEServer->loadSettings(data.vrdeSettings);
    9297         if (FAILED(rc)) return rc;
    9298 
    9299         /* BIOS */
    9300         rc = mBIOSSettings->loadSettings(data.biosSettings);
    9301         if (FAILED(rc)) return rc;
    9302 
    9303         // Bandwidth control (must come before network adapters)
    9304         rc = mBandwidthControl->loadSettings(data.ioSettings);
    9305         if (FAILED(rc)) return rc;
    9306 
    9307         /* Shared folders */
    9308         for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
    9309              it != data.usbSettings.llUSBControllers.end();
    9310              ++it)
    9311         {
    9312             const settings::USBController &settingsCtrl = *it;
    9313             ComObjPtr<USBController> newCtrl;
    9314 
    9315             newCtrl.createObject();
    9316             newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
    9317             mUSBControllers->push_back(newCtrl);
    9318         }
    9319 
    9320         /* USB device filters */
    9321         rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
    9322         if (FAILED(rc)) return rc;
    9323 
    9324         // network adapters
    9325         uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
    9326         uint32_t oldCount = mNetworkAdapters.size();
    9327         if (newCount > oldCount)
    9328         {
    9329             mNetworkAdapters.resize(newCount);
    9330             for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
    9331             {
    9332                 unconst(mNetworkAdapters[slot]).createObject();
    9333                 mNetworkAdapters[slot]->init(this, slot);
    9334             }
    9335         }
    9336         else if (newCount < oldCount)
    9337             mNetworkAdapters.resize(newCount);
    9338         for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
    9339             it != data.llNetworkAdapters.end();
    9340             ++it)
    9341         {
    9342             const settings::NetworkAdapter &nic = *it;
    9343 
    9344             /* slot unicity is guaranteed by XML Schema */
    9345             AssertBreak(nic.ulSlot < mNetworkAdapters.size());
    9346             rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
    9347             if (FAILED(rc)) return rc;
    9348         }
    9349 
    9350         // serial ports
    9351         for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
    9352             it != data.llSerialPorts.end();
    9353             ++it)
    9354         {
    9355             const settings::SerialPort &s = *it;
    9356 
    9357             AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
    9358             rc = mSerialPorts[s.ulSlot]->loadSettings(s);
    9359             if (FAILED(rc)) return rc;
    9360         }
    9361 
    9362         // parallel ports (optional)
    9363         for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
    9364             it != data.llParallelPorts.end();
    9365             ++it)
    9366         {
    9367             const settings::ParallelPort &p = *it;
    9368 
    9369             AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
    9370             rc = mParallelPorts[p.ulSlot]->loadSettings(p);
    9371             if (FAILED(rc)) return rc;
    9372         }
    9373 
    9374         /* AudioAdapter */
    9375         rc = mAudioAdapter->loadSettings(data.audioAdapter);
    9376         if (FAILED(rc)) return rc;
    9377 
    9378         /* Shared folders */
    9379         for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
    9380              it != data.llSharedFolders.end();
    9381              ++it)
    9382         {
    9383             const settings::SharedFolder &sf = *it;
    9384 
    9385             ComObjPtr<SharedFolder> sharedFolder;
    9386             /* Check for double entries. Not allowed! */
    9387             rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
    9388             if (SUCCEEDED(rc))
    9389                 return setError(VBOX_E_OBJECT_IN_USE,
    9390                                 tr("Shared folder named '%s' already exists"),
    9391                                 sf.strName.c_str());
    9392 
    9393             /* Create the new shared folder. Don't break on error. This will be
    9394              * reported when the machine starts. */
    9395             sharedFolder.createObject();
    9396             rc = sharedFolder->init(getMachine(),
    9397                                     sf.strName,
    9398                                     sf.strHostPath,
    9399                                     RT_BOOL(sf.fWritable),
    9400                                     RT_BOOL(sf.fAutoMount),
    9401                                     false /* fFailOnError */);
    9402             if (FAILED(rc)) return rc;
    9403             mHWData->mSharedFolders.push_back(sharedFolder);
    9404         }
    9405 
    9406         // Clipboard
    9407         mHWData->mClipboardMode = data.clipboardMode;
    9408 
    9409         // drag'n'drop
    9410         mHWData->mDragAndDropMode = data.dragAndDropMode;
    9411 
    9412         // guest settings
    9413         mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
    9414 
    9415         // IO settings
    9416         mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
    9417         mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
    9418 
    9419         // Host PCI devices
    9420         for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
    9421              it != data.pciAttachments.end();
    9422              ++it)
    9423         {
    9424             const settings::HostPCIDeviceAttachment &hpda = *it;
    9425             ComObjPtr<PCIDeviceAttachment> pda;
    9426 
    9427             pda.createObject();
    9428             pda->loadSettings(this, hpda);
    9429             mHWData->mPCIDeviceAssignments.push_back(pda);
    9430         }
    9431 
    9432         /*
    9433          * (The following isn't really real hardware, but it lives in HWData
    9434          * for reasons of convenience.)
    9435          */
    9436 
    9437 #ifdef VBOX_WITH_GUEST_PROPS
    9438         /* Guest properties (optional) */
    9439         for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
    9440             it != data.llGuestProperties.end();
    9441             ++it)
    9442         {
    9443             const settings::GuestProperty &prop = *it;
    9444             uint32_t fFlags = guestProp::NILFLAG;
    9445             guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
    9446             HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
    9447             mHWData->mGuestProperties[prop.strName] = property;
    9448         }
    9449 
    9450         mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
    9451 #endif /* VBOX_WITH_GUEST_PROPS defined */
    9452 
    9453         rc = loadDebugging(pDbg);
    9454         if (FAILED(rc))
    9455             return rc;
    9456 
    9457         mHWData->mAutostart = *pAutostart;
    9458 
    9459         /* default frontend */
    9460         mHWData->mDefaultFrontend = data.strDefaultFrontend;
    9461     }
    9462     catch(std::bad_alloc &)
    9463     {
    9464         return E_OUTOFMEMORY;
    9465     }
    9466 
    9467     AssertComRC(rc);
    9468     return rc;
    9469 }
    9470 
    9471 /**
    9472  * Called from Machine::loadHardware() to load the debugging settings of the
    9473  * machine.
    9474  *
    9475  * @param   pDbg        Pointer to the settings.
    9476  */
    9477 HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
    9478 {
    9479     mHWData->mDebugging = *pDbg;
    9480     /* no more processing currently required, this will probably change. */
    9481     return S_OK;
    9482 }
    9483 
    9484 /**
    9485  *  Called from loadMachineDataFromSettings() for the storage controller data, including media.
    9486  *
    9487  * @param data
    9488  * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
    9489  * @param puuidSnapshot
    9490  * @return
    9491  */
    9492 HRESULT Machine::loadStorageControllers(const settings::Storage &data,
    9493                                         const Guid *puuidRegistry,
    9494                                         const Guid *puuidSnapshot)
    9495 {
    9496     AssertReturn(!isSessionMachine(), E_FAIL);
    9497 
    9498     HRESULT rc = S_OK;
    9499 
    9500     for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
    9501          it != data.llStorageControllers.end();
    9502          ++it)
    9503     {
    9504         const settings::StorageController &ctlData = *it;
    9505 
    9506         ComObjPtr<StorageController> pCtl;
    9507         /* Try to find one with the name first. */
    9508         rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
    9509         if (SUCCEEDED(rc))
    9510             return setError(VBOX_E_OBJECT_IN_USE,
    9511                             tr("Storage controller named '%s' already exists"),
    9512                             ctlData.strName.c_str());
    9513 
    9514         pCtl.createObject();
    9515         rc = pCtl->init(this,
    9516                         ctlData.strName,
    9517                         ctlData.storageBus,
    9518                         ctlData.ulInstance,
    9519                         ctlData.fBootable);
    9520         if (FAILED(rc)) return rc;
    9521 
    9522         mStorageControllers->push_back(pCtl);
    9523 
    9524         rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
    9525         if (FAILED(rc)) return rc;
    9526 
    9527         rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
    9528         if (FAILED(rc)) return rc;
    9529 
    9530         rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
    9531         if (FAILED(rc)) return rc;
    9532 
    9533         /* Set IDE emulation settings (only for AHCI controller). */
    9534         if (ctlData.controllerType == StorageControllerType_IntelAhci)
    9535         {
    9536             if (    (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
    9537                  || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
    9538                  || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
    9539                  || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
    9540                )
    9541                 return rc;
    9542         }
    9543 
    9544         /* Load the attached devices now. */
    9545         rc = loadStorageDevices(pCtl,
    9546                                 ctlData,
    9547                                 puuidRegistry,
    9548                                 puuidSnapshot);
    9549         if (FAILED(rc)) return rc;
    9550     }
    9551 
    9552     return S_OK;
    9553 }
    9554 
    9555 /**
    9556  * Called from loadStorageControllers for a controller's devices.
    9557  *
    9558  * @param aStorageController
    9559  * @param data
    9560  * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
    9561  * @param aSnapshotId  pointer to the snapshot ID if this is a snapshot machine
    9562  * @return
    9563  */
    9564 HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
    9565                                     const settings::StorageController &data,
    9566                                     const Guid *puuidRegistry,
    9567                                     const Guid *puuidSnapshot)
    9568 {
    9569     HRESULT rc = S_OK;
    9570 
    9571     /* paranoia: detect duplicate attachments */
    9572     for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
    9573          it != data.llAttachedDevices.end();
    9574          ++it)
    9575     {
    9576         const settings::AttachedDevice &ad = *it;
    9577 
    9578         for (settings::AttachedDevicesList::const_iterator it2 = it;
    9579              it2 != data.llAttachedDevices.end();
    9580              ++it2)
    9581         {
    9582             if (it == it2)
    9583                 continue;
    9584 
    9585             const settings::AttachedDevice &ad2 = *it2;
    9586 
    9587             if (   ad.lPort == ad2.lPort
    9588                 && ad.lDevice == ad2.lDevice)
    9589             {
    9590                 return setError(E_FAIL,
    9591                                 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
    9592                                 aStorageController->getName().c_str(),
    9593                                 ad.lPort,
    9594                                 ad.lDevice,
    9595                                 mUserData->s.strName.c_str());
    9596             }
    9597         }
    9598     }
    9599 
    9600     for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
    9601          it != data.llAttachedDevices.end();
    9602          ++it)
    9603     {
    9604         const settings::AttachedDevice &dev = *it;
    9605         ComObjPtr<Medium> medium;
    9606 
    9607         switch (dev.deviceType)
    9608         {
    9609             case DeviceType_Floppy:
    9610             case DeviceType_DVD:
    9611                 if (dev.strHostDriveSrc.isNotEmpty())
    9612                     rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
    9613                 else
    9614                     rc = mParent->findRemoveableMedium(dev.deviceType,
    9615                                                        dev.uuid,
    9616                                                        false /* fRefresh */,
    9617                                                        false /* aSetError */,
    9618                                                        medium);
    9619                 if (rc == VBOX_E_OBJECT_NOT_FOUND)
    9620                     // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
    9621                     rc = S_OK;
    9622             break;
    9623 
    9624             case DeviceType_HardDisk:
    9625             {
    9626                 /* find a hard disk by UUID */
    9627                 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
    9628                 if (FAILED(rc))
    9629                 {
    9630                     if (isSnapshotMachine())
    9631                     {
    9632                         // wrap another error message around the "cannot find hard disk" set by findHardDisk
    9633                         // so the user knows that the bad disk is in a snapshot somewhere
    9634                         com::ErrorInfo info;
    9635                         return setError(E_FAIL,
    9636                                         tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
    9637                                         puuidSnapshot->raw(),
    9638                                         info.getText().raw());
    9639                     }
    9640                     else
    9641                         return rc;
    9642                 }
    9643 
    9644                 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
    9645 
    9646                 if (medium->getType() == MediumType_Immutable)
    9647                 {
    9648                     if (isSnapshotMachine())
    9649                         return setError(E_FAIL,
    9650                                         tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
    9651                                            "of the virtual machine '%s' ('%s')"),
    9652                                         medium->getLocationFull().c_str(),
    9653                                         dev.uuid.raw(),
    9654                                         puuidSnapshot->raw(),
    9655                                         mUserData->s.strName.c_str(),
    9656                                         mData->m_strConfigFileFull.c_str());
    9657 
    9658                     return setError(E_FAIL,
    9659                                     tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
    9660                                     medium->getLocationFull().c_str(),
    9661                                     dev.uuid.raw(),
    9662                                     mUserData->s.strName.c_str(),
    9663                                     mData->m_strConfigFileFull.c_str());
    9664                 }
    9665 
    9666                 if (medium->getType() == MediumType_MultiAttach)
    9667                 {
    9668                     if (isSnapshotMachine())
    9669                         return setError(E_FAIL,
    9670                                         tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
    9671                                            "of the virtual machine '%s' ('%s')"),
    9672                                         medium->getLocationFull().c_str(),
    9673                                         dev.uuid.raw(),
    9674                                         puuidSnapshot->raw(),
    9675                                         mUserData->s.strName.c_str(),
    9676                                         mData->m_strConfigFileFull.c_str());
    9677 
    9678                     return setError(E_FAIL,
    9679                                     tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
    9680                                     medium->getLocationFull().c_str(),
    9681                                     dev.uuid.raw(),
    9682                                     mUserData->s.strName.c_str(),
    9683                                     mData->m_strConfigFileFull.c_str());
    9684                 }
    9685 
    9686                 if (    !isSnapshotMachine()
    9687                      && medium->getChildren().size() != 0
    9688                    )
    9689                     return setError(E_FAIL,
    9690                                     tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
    9691                                        "because it has %d differencing child hard disks"),
    9692                                     medium->getLocationFull().c_str(),
    9693                                     dev.uuid.raw(),
    9694                                     mUserData->s.strName.c_str(),
    9695                                     mData->m_strConfigFileFull.c_str(),
    9696                                     medium->getChildren().size());
    9697 
    9698                 if (findAttachment(mMediaData->mAttachments,
    9699                                    medium))
    9700                     return setError(E_FAIL,
    9701                                     tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
    9702                                     medium->getLocationFull().c_str(),
    9703                                     dev.uuid.raw(),
    9704                                     mUserData->s.strName.c_str(),
    9705                                     mData->m_strConfigFileFull.c_str());
    9706 
    9707                 break;
    9708             }
    9709 
    9710             default:
    9711                 return setError(E_FAIL,
    9712                                 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
    9713                                 medium->getLocationFull().c_str(),
    9714                                 mUserData->s.strName.c_str(),
    9715                                 mData->m_strConfigFileFull.c_str());
    9716         }
    9717 
    9718         if (FAILED(rc))
    9719             break;
    9720 
    9721         /* Bandwidth groups are loaded at this point. */
    9722         ComObjPtr<BandwidthGroup> pBwGroup;
    9723 
    9724         if (!dev.strBwGroup.isEmpty())
    9725         {
    9726             rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
    9727             if (FAILED(rc))
    9728                 return setError(E_FAIL,
    9729                                 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
    9730                                 medium->getLocationFull().c_str(),
    9731                                 dev.strBwGroup.c_str(),
    9732                                 mUserData->s.strName.c_str(),
    9733                                 mData->m_strConfigFileFull.c_str());
    9734             pBwGroup->reference();
    9735         }
    9736 
    9737         const Bstr controllerName = aStorageController->getName();
    9738         ComObjPtr<MediumAttachment> pAttachment;
    9739         pAttachment.createObject();
    9740         rc = pAttachment->init(this,
    9741                                medium,
    9742                                controllerName,
    9743                                dev.lPort,
    9744                                dev.lDevice,
    9745                                dev.deviceType,
    9746                                false,
    9747                                dev.fPassThrough,
    9748                                dev.fTempEject,
    9749                                dev.fNonRotational,
    9750                                dev.fDiscard,
    9751                                pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
    9752         if (FAILED(rc)) break;
    9753 
    9754         /* associate the medium with this machine and snapshot */
    9755         if (!medium.isNull())
    9756         {
    9757             AutoCaller medCaller(medium);
    9758             if (FAILED(medCaller.rc())) return medCaller.rc();
    9759             AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
    9760 
    9761             if (isSnapshotMachine())
    9762                 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
    9763             else
    9764                 rc = medium->addBackReference(mData->mUuid);
    9765             /* If the medium->addBackReference fails it sets an appropriate
    9766              * error message, so no need to do any guesswork here. */
    9767 
    9768             if (puuidRegistry)
    9769                 // caller wants registry ID to be set on all attached media (OVF import case)
    9770                 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
    9771         }
    9772 
    9773         if (FAILED(rc))
    9774             break;
    9775 
    9776         /* back up mMediaData to let registeredInit() properly rollback on failure
    9777          * (= limited accessibility) */
    9778         setModified(IsModified_Storage);
    9779         mMediaData.backup();
    9780         mMediaData->mAttachments.push_back(pAttachment);
    9781     }
    9782 
    9783     return rc;
    9784 }
    9785 
    9786 /**
    9787  *  Returns the snapshot with the given UUID or fails of no such snapshot exists.
    9788  *
    9789  *  @param aId          snapshot UUID to find (empty UUID refers the first snapshot)
    9790  *  @param aSnapshot    where to return the found snapshot
    9791  *  @param aSetError    true to set extended error info on failure
    9792  */
    9793 HRESULT Machine::findSnapshotById(const Guid &aId,
    9794                                   ComObjPtr<Snapshot> &aSnapshot,
    9795                                   bool aSetError /* = false */)
    9796 {
    9797     AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
    9798 
    9799     if (!mData->mFirstSnapshot)
    9800     {
    9801         if (aSetError)
    9802             return setError(E_FAIL, tr("This machine does not have any snapshots"));
    9803         return E_FAIL;
    9804     }
    9805 
    9806     if (aId.isZero())
    9807         aSnapshot = mData->mFirstSnapshot;
    9808     else
    9809         aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
    9810 
    9811     if (!aSnapshot)
    9812     {
    9813         if (aSetError)
    9814             return setError(E_FAIL,
    9815                             tr("Could not find a snapshot with UUID {%s}"),
    9816                             aId.toString().c_str());
    9817         return E_FAIL;
    9818     }
    9819 
    9820     return S_OK;
    9821 }
    9822 
    9823 /**
    9824  *  Returns the snapshot with the given name or fails of no such snapshot.
    9825  *
    9826  *  @param aName        snapshot name to find
    9827  *  @param aSnapshot    where to return the found snapshot
    9828  *  @param aSetError    true to set extended error info on failure
    9829  */
    9830 HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
    9831                                     ComObjPtr<Snapshot> &aSnapshot,
    9832                                     bool aSetError /* = false */)
    9833 {
    9834     AssertReturn(!strName.isEmpty(), E_INVALIDARG);
    9835 
    9836     AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
    9837 
    9838     if (!mData->mFirstSnapshot)
    9839     {
    9840         if (aSetError)
    9841             return setError(VBOX_E_OBJECT_NOT_FOUND,
    9842                             tr("This machine does not have any snapshots"));
    9843         return VBOX_E_OBJECT_NOT_FOUND;
    9844     }
    9845 
    9846     aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
    9847 
    9848     if (!aSnapshot)
    9849     {
    9850         if (aSetError)
    9851             return setError(VBOX_E_OBJECT_NOT_FOUND,
    9852                             tr("Could not find a snapshot named '%s'"), strName.c_str());
    9853         return VBOX_E_OBJECT_NOT_FOUND;
    9854     }
    9855 
    9856     return S_OK;
    9857 }
    9858 
    9859 /**
    9860  * Returns a storage controller object with the given name.
    9861  *
    9862  *  @param aName                 storage controller name to find
    9863  *  @param aStorageController    where to return the found storage controller
    9864  *  @param aSetError             true to set extended error info on failure
    9865  */
    9866 HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
    9867                                             ComObjPtr<StorageController> &aStorageController,
    9868                                             bool aSetError /* = false */)
    9869 {
    9870     AssertReturn(!aName.isEmpty(), E_INVALIDARG);
    9871 
    9872     for (StorageControllerList::const_iterator it = mStorageControllers->begin();
    9873          it != mStorageControllers->end();
    9874          ++it)
    9875     {
    9876         if ((*it)->getName() == aName)
    9877         {
    9878             aStorageController = (*it);
    9879             return S_OK;
    9880         }
    9881     }
    9882 
    9883     if (aSetError)
    9884         return setError(VBOX_E_OBJECT_NOT_FOUND,
    9885                         tr("Could not find a storage controller named '%s'"),
    9886                         aName.c_str());
    9887     return VBOX_E_OBJECT_NOT_FOUND;
    9888 }
    9889 
    9890 /**
    9891  * Returns a USB controller object with the given name.
    9892  *
    9893  *  @param aName                 USB controller name to find
    9894  *  @param aUSBController        where to return the found USB controller
    9895  *  @param aSetError             true to set extended error info on failure
    9896  */
    9897 HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
    9898                                         ComObjPtr<USBController> &aUSBController,
    9899                                         bool aSetError /* = false */)
    9900 {
    9901     AssertReturn(!aName.isEmpty(), E_INVALIDARG);
    9902 
    9903     for (USBControllerList::const_iterator it = mUSBControllers->begin();
    9904          it != mUSBControllers->end();
    9905          ++it)
    9906     {
    9907         if ((*it)->getName() == aName)
    9908         {
    9909             aUSBController = (*it);
    9910             return S_OK;
    9911         }
    9912     }
    9913 
    9914     if (aSetError)
    9915         return setError(VBOX_E_OBJECT_NOT_FOUND,
    9916                         tr("Could not find a storage controller named '%s'"),
    9917                         aName.c_str());
    9918     return VBOX_E_OBJECT_NOT_FOUND;
    9919 }
    9920 
    9921 /**
    9922  * Returns the number of USB controller instance of the given type.
    9923  *
    9924  * @param enmType                USB controller type.
    9925  */
    9926 ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
    9927 {
    9928     ULONG cCtrls = 0;
    9929 
    9930     for (USBControllerList::const_iterator it = mUSBControllers->begin();
    9931          it != mUSBControllers->end();
    9932          ++it)
    9933     {
    9934         if ((*it)->getControllerType() == enmType)
    9935             cCtrls++;
    9936     }
    9937 
    9938     return cCtrls;
    9939 }
    9940 
    9941 HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
    9942                                                   MediaData::AttachmentList &atts)
    9943 {
    9944     AutoCaller autoCaller(this);
    9945     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    9946 
    9947     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    9948 
    9949     for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
    9950          it != mMediaData->mAttachments.end();
    9951          ++it)
    9952     {
    9953         const ComObjPtr<MediumAttachment> &pAtt = *it;
    9954 
    9955         // should never happen, but deal with NULL pointers in the list.
    9956         AssertStmt(!pAtt.isNull(), continue);
    9957 
    9958         // getControllerName() needs caller+read lock
    9959         AutoCaller autoAttCaller(pAtt);
    9960         if (FAILED(autoAttCaller.rc()))
    9961         {
    9962             atts.clear();
    9963             return autoAttCaller.rc();
    9964         }
    9965         AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
    9966 
    9967         if (pAtt->getControllerName() == aName)
    9968             atts.push_back(pAtt);
    9969     }
    9970 
    9971     return S_OK;
    9972 }
    9973 
    9974 /**
    9975  *  Helper for #saveSettings. Cares about renaming the settings directory and
    9976  *  file if the machine name was changed and about creating a new settings file
    9977  *  if this is a new machine.
    9978  *
    9979  *  @note Must be never called directly but only from #saveSettings().
    9980  */
    9981 HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
    9982 {
    9983     AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
    9984 
    9985     HRESULT rc = S_OK;
    9986 
    9987     bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
    9988 
    9989     /// @todo need to handle primary group change, too
    9990 
    9991     /* attempt to rename the settings file if machine name is changed */
    9992     if (    mUserData->s.fNameSync
    9993          && mUserData.isBackedUp()
    9994          && (   mUserData.backedUpData()->s.strName != mUserData->s.strName
    9995              || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
    9996        )
    9997     {
    9998         bool dirRenamed = false;
    9999         bool fileRenamed = false;
    10000 
    10001         Utf8Str configFile, newConfigFile;
    10002         Utf8Str configFilePrev, newConfigFilePrev;
    10003         Utf8Str configDir, newConfigDir;
    10004 
    10005         do
    10006         {
    10007             int vrc = VINF_SUCCESS;
    10008 
    10009             Utf8Str name = mUserData.backedUpData()->s.strName;
    10010             Utf8Str newName = mUserData->s.strName;
    10011             Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
    10012             if (group == "/")
    10013                 group.setNull();
    10014             Utf8Str newGroup = mUserData->s.llGroups.front();
    10015             if (newGroup == "/")
    10016                 newGroup.setNull();
    10017 
    10018             configFile = mData->m_strConfigFileFull;
    10019 
    10020             /* first, rename the directory if it matches the group and machine name */
    10021             Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
    10022                 group.c_str(), RTPATH_DELIMITER, name.c_str());
    10023             /** @todo hack, make somehow use of ComposeMachineFilename */
    10024             if (mUserData->s.fDirectoryIncludesUUID)
    10025                 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
    10026             Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
    10027                 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
    10028             /** @todo hack, make somehow use of ComposeMachineFilename */
    10029             if (mUserData->s.fDirectoryIncludesUUID)
    10030                 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
    10031             configDir = configFile;
    10032             configDir.stripFilename();
    10033             newConfigDir = configDir;
    10034             if (   configDir.length() >= groupPlusName.length()
    10035                 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
    10036             {
    10037                 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
    10038                 Utf8Str newConfigBaseDir(newConfigDir);
    10039                 newConfigDir.append(newGroupPlusName);
    10040                 /* consistency: use \ if appropriate on the platform */
    10041                 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
    10042                 /* new dir and old dir cannot be equal here because of 'if'
    10043                  * above and because name != newName */
    10044                 Assert(configDir != newConfigDir);
    10045                 if (!fSettingsFileIsNew)
    10046                 {
    10047                     /* perform real rename only if the machine is not new */
    10048                     vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
    10049                     if (   vrc == VERR_FILE_NOT_FOUND
    10050                         || vrc == VERR_PATH_NOT_FOUND)
    10051                     {
    10052                         /* create the parent directory, then retry renaming */
    10053                         Utf8Str parent(newConfigDir);
    10054                         parent.stripFilename();
    10055                         (void)RTDirCreateFullPath(parent.c_str(), 0700);
    10056                         vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
    10057                     }
    10058                     if (RT_FAILURE(vrc))
    10059                     {
    10060                         rc = setError(E_FAIL,
    10061                                       tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
    10062                                       configDir.c_str(),
    10063                                       newConfigDir.c_str(),
    10064                                       vrc);
    10065                         break;
    10066                     }
    10067                     /* delete subdirectories which are no longer needed */
    10068                     Utf8Str dir(configDir);
    10069                     dir.stripFilename();
    10070                     while (dir != newConfigBaseDir && dir != ".")
    10071                     {
    10072                         vrc = RTDirRemove(dir.c_str());
    10073                         if (RT_FAILURE(vrc))
    10074                             break;
    10075                         dir.stripFilename();
    10076                     }
    10077                     dirRenamed = true;
    10078                 }
    10079             }
    10080 
    10081             newConfigFile = Utf8StrFmt("%s%c%s.vbox",
    10082                 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
    10083 
    10084             /* then try to rename the settings file itself */
    10085             if (newConfigFile != configFile)
    10086             {
    10087                 /* get the path to old settings file in renamed directory */
    10088                 configFile = Utf8StrFmt("%s%c%s",
    10089                                         newConfigDir.c_str(),
    10090                                         RTPATH_DELIMITER,
    10091                                         RTPathFilename(configFile.c_str()));
    10092                 if (!fSettingsFileIsNew)
    10093                 {
    10094                     /* perform real rename only if the machine is not new */
    10095                     vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
    10096                     if (RT_FAILURE(vrc))
    10097                     {
    10098                         rc = setError(E_FAIL,
    10099                                       tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
    10100                                       configFile.c_str(),
    10101                                       newConfigFile.c_str(),
    10102                                       vrc);
    10103                         break;
    10104                     }
    10105                     fileRenamed = true;
    10106                     configFilePrev = configFile;
    10107                     configFilePrev += "-prev";
    10108                     newConfigFilePrev = newConfigFile;
    10109                     newConfigFilePrev += "-prev";
    10110                     RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
    10111                 }
    10112             }
    10113 
    10114             // update m_strConfigFileFull amd mConfigFile
    10115             mData->m_strConfigFileFull = newConfigFile;
    10116             // compute the relative path too
    10117             mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
    10118 
    10119             // store the old and new so that VirtualBox::saveSettings() can update
    10120             // the media registry
    10121             if (    mData->mRegistered
    10122                  && configDir != newConfigDir)
    10123             {
    10124                 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
    10125 
    10126                 if (pfNeedsGlobalSaveSettings)
    10127                     *pfNeedsGlobalSaveSettings = true;
    10128             }
    10129 
    10130             // in the saved state file path, replace the old directory with the new directory
    10131             if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
    10132                 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
    10133 
    10134             // and do the same thing for the saved state file paths of all the online snapshots
    10135             if (mData->mFirstSnapshot)
    10136                 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
    10137                                                              newConfigDir.c_str());
    10138         }
    10139         while (0);
    10140 
    10141         if (FAILED(rc))
    10142         {
    10143             /* silently try to rename everything back */
    10144             if (fileRenamed)
    10145             {
    10146                 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
    10147                 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
    10148             }
    10149             if (dirRenamed)
    10150                 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
    10151         }
    10152 
    10153         if (FAILED(rc)) return rc;
    10154     }
    10155 
    10156     if (fSettingsFileIsNew)
    10157     {
    10158         /* create a virgin config file */
    10159         int vrc = VINF_SUCCESS;
    10160 
    10161         /* ensure the settings directory exists */
    10162         Utf8Str path(mData->m_strConfigFileFull);
    10163         path.stripFilename();
    10164         if (!RTDirExists(path.c_str()))
    10165         {
    10166             vrc = RTDirCreateFullPath(path.c_str(), 0700);
    10167             if (RT_FAILURE(vrc))
    10168             {
    10169                 return setError(E_FAIL,
    10170                                 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
    10171                                 path.c_str(),
    10172                                 vrc);
    10173             }
    10174         }
    10175 
    10176         /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
    10177         path = Utf8Str(mData->m_strConfigFileFull);
    10178         RTFILE f = NIL_RTFILE;
    10179         vrc = RTFileOpen(&f, path.c_str(),
    10180                          RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
    10181         if (RT_FAILURE(vrc))
    10182             return setError(E_FAIL,
    10183                             tr("Could not create the settings file '%s' (%Rrc)"),
    10184                             path.c_str(),
    10185                             vrc);
    10186         RTFileClose(f);
    10187     }
    10188 
    10189     return rc;
    10190 }
    10191 
    10192 /**
    10193  * Saves and commits machine data, user data and hardware data.
    10194  *
    10195  * Note that on failure, the data remains uncommitted.
    10196  *
    10197  * @a aFlags may combine the following flags:
    10198  *
    10199  *  - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
    10200  *    Used when saving settings after an operation that makes them 100%
    10201  *    correspond to the settings from the current snapshot.
    10202  *  - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
    10203  *    #isReallyModified() returns false. This is necessary for cases when we
    10204  *    change machine data directly, not through the backup()/commit() mechanism.
    10205  *  - SaveS_Force: settings will be saved without doing a deep compare of the
    10206  *    settings structures. This is used when this is called because snapshots
    10207  *    have changed to avoid the overhead of the deep compare.
    10208  *
    10209  * @note Must be called from under this object's write lock. Locks children for
    10210  * writing.
    10211  *
    10212  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
    10213  *          initialized to false and that will be set to true by this function if
    10214  *          the caller must invoke VirtualBox::saveSettings() because the global
    10215  *          settings have changed. This will happen if a machine rename has been
    10216  *          saved and the global machine and media registries will therefore need
    10217  *          updating.
    10218  */
    10219 HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
    10220                               int aFlags /*= 0*/)
    10221 {
    10222     LogFlowThisFuncEnter();
    10223 
    10224     AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
    10225 
    10226     /* make sure child objects are unable to modify the settings while we are
    10227      * saving them */
    10228     ensureNoStateDependencies();
    10229 
    10230     AssertReturn(!isSnapshotMachine(),
    10231                  E_FAIL);
    10232 
    10233     HRESULT rc = S_OK;
    10234     bool fNeedsWrite = false;
    10235 
    10236     /* First, prepare to save settings. It will care about renaming the
    10237      * settings directory and file if the machine name was changed and about
    10238      * creating a new settings file if this is a new machine. */
    10239     rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
    10240     if (FAILED(rc)) return rc;
    10241 
    10242     // keep a pointer to the current settings structures
    10243     settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
    10244     settings::MachineConfigFile *pNewConfig = NULL;
    10245 
    10246     try
    10247     {
    10248         // make a fresh one to have everyone write stuff into
    10249         pNewConfig = new settings::MachineConfigFile(NULL);
    10250         pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
    10251 
    10252         // now go and copy all the settings data from COM to the settings structures
    10253         // (this calles saveSettings() on all the COM objects in the machine)
    10254         copyMachineDataToSettings(*pNewConfig);
    10255 
    10256         if (aFlags & SaveS_ResetCurStateModified)
    10257         {
    10258             // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
    10259             mData->mCurrentStateModified = FALSE;
    10260             fNeedsWrite = true;     // always, no need to compare
    10261         }
    10262         else if (aFlags & SaveS_Force)
    10263         {
    10264             fNeedsWrite = true;     // always, no need to compare
    10265         }
    10266         else
    10267         {
    10268             if (!mData->mCurrentStateModified)
    10269             {
    10270                 // do a deep compare of the settings that we just saved with the settings
    10271                 // previously stored in the config file; this invokes MachineConfigFile::operator==
    10272                 // which does a deep compare of all the settings, which is expensive but less expensive
    10273                 // than writing out XML in vain
    10274                 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
    10275 
    10276                 // could still be modified if any settings changed
    10277                 mData->mCurrentStateModified = fAnySettingsChanged;
    10278 
    10279                 fNeedsWrite = fAnySettingsChanged;
    10280             }
    10281             else
    10282                 fNeedsWrite = true;
    10283         }
    10284 
    10285         pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
    10286 
    10287         if (fNeedsWrite)
    10288             // now spit it all out!
    10289             pNewConfig->write(mData->m_strConfigFileFull);
    10290 
    10291         mData->pMachineConfigFile = pNewConfig;
    10292         delete pOldConfig;
    10293         commit();
    10294 
    10295         // after saving settings, we are no longer different from the XML on disk
    10296         mData->flModifications = 0;
    10297     }
    10298     catch (HRESULT err)
    10299     {
    10300         // we assume that error info is set by the thrower
    10301         rc = err;
    10302 
    10303         // restore old config
    10304         delete pNewConfig;
    10305         mData->pMachineConfigFile = pOldConfig;
    10306     }
    10307     catch (...)
    10308     {
    10309         rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    10310     }
    10311 
    10312     if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
    10313     {
    10314         /* Fire the data change event, even on failure (since we've already
    10315          * committed all data). This is done only for SessionMachines because
    10316          * mutable Machine instances are always not registered (i.e. private
    10317          * to the client process that creates them) and thus don't need to
    10318          * inform callbacks. */
    10319         if (isSessionMachine())
    10320             mParent->onMachineDataChange(mData->mUuid);
    10321     }
    10322 
    10323     LogFlowThisFunc(("rc=%08X\n", rc));
    10324     LogFlowThisFuncLeave();
    10325     return rc;
    10326 }
    10327 
    10328 /**
    10329  * Implementation for saving the machine settings into the given
    10330  * settings::MachineConfigFile instance. This copies machine extradata
    10331  * from the previous machine config file in the instance data, if any.
    10332  *
    10333  * This gets called from two locations:
    10334  *
    10335  *  --  Machine::saveSettings(), during the regular XML writing;
    10336  *
    10337  *  --  Appliance::buildXMLForOneVirtualSystem(), when a machine gets
    10338  *      exported to OVF and we write the VirtualBox proprietary XML
    10339  *      into a <vbox:Machine> tag.
    10340  *
    10341  * This routine fills all the fields in there, including snapshots, *except*
    10342  * for the following:
    10343  *
    10344  * -- fCurrentStateModified. There is some special logic associated with that.
    10345  *
    10346  * The caller can then call MachineConfigFile::write() or do something else
    10347  * with it.
    10348  *
    10349  * Caller must hold the machine lock!
    10350  *
    10351  * This throws XML errors and HRESULT, so the caller must have a catch block!
    10352  */
    10353 void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
    10354 {
    10355     // deep copy extradata
    10356     config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
    10357 
    10358     config.uuid = mData->mUuid;
    10359 
    10360     // copy name, description, OS type, teleport, UTC etc.
    10361     config.machineUserData = mUserData->s;
    10362 
    10363     // Encode the Icon Override data from Machine and store on config userdata.
    10364     com::SafeArray<BYTE> iconByte;
    10365     COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
    10366     ssize_t cbData = iconByte.size();
    10367     if (cbData > 0)
    10368     {
    10369         ssize_t cchOut = RTBase64EncodedLength(cbData);
    10370         Utf8Str strIconData;
    10371         strIconData.reserve(cchOut+1);
    10372         int vrc = RTBase64Encode(iconByte.raw(), cbData,
    10373                                  strIconData.mutableRaw(), strIconData.capacity(),
    10374                                  NULL);
    10375         if (RT_FAILURE(vrc))
    10376             throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
    10377         strIconData.jolt();
    10378         config.machineUserData.ovIcon = strIconData;
    10379     }
    10380     else
    10381         config.machineUserData.ovIcon.setNull();
    10382 
    10383     if (    mData->mMachineState == MachineState_Saved
    10384          || mData->mMachineState == MachineState_Restoring
    10385             // when deleting a snapshot we may or may not have a saved state in the current state,
    10386             // so let's not assert here please
    10387          || (    (   mData->mMachineState == MachineState_DeletingSnapshot
    10388                   || mData->mMachineState == MachineState_DeletingSnapshotOnline
    10389                   || mData->mMachineState == MachineState_DeletingSnapshotPaused)
    10390               && (!mSSData->strStateFilePath.isEmpty())
    10391             )
    10392         )
    10393     {
    10394         Assert(!mSSData->strStateFilePath.isEmpty());
    10395         /* try to make the file name relative to the settings file dir */
    10396         copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
    10397     }
    10398     else
    10399     {
    10400         Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
    10401         config.strStateFile.setNull();
    10402     }
    10403 
    10404     if (mData->mCurrentSnapshot)
    10405         config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
    10406     else
    10407         config.uuidCurrentSnapshot.clear();
    10408 
    10409     config.timeLastStateChange = mData->mLastStateChange;
    10410     config.fAborted = (mData->mMachineState == MachineState_Aborted);
    10411     /// @todo Live Migration:        config.fTeleported = (mData->mMachineState == MachineState_Teleported);
    10412 
    10413     HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
    10414     if (FAILED(rc)) throw rc;
    10415 
    10416     rc = saveStorageControllers(config.storageMachine);
    10417     if (FAILED(rc)) throw rc;
    10418 
    10419     // save machine's media registry if this is VirtualBox 4.0 or later
    10420     if (config.canHaveOwnMediaRegistry())
    10421     {
    10422         // determine machine folder
    10423         Utf8Str strMachineFolder = getSettingsFileFull();
    10424         strMachineFolder.stripFilename();
    10425         mParent->saveMediaRegistry(config.mediaRegistry,
    10426                                    getId(),             // only media with registry ID == machine UUID
    10427                                    strMachineFolder);
    10428             // this throws HRESULT
    10429     }
    10430 
    10431     // save snapshots
    10432     rc = saveAllSnapshots(config);
    10433     if (FAILED(rc)) throw rc;
    10434 }
    10435 
    10436 /**
    10437  * Saves all snapshots of the machine into the given machine config file. Called
    10438  * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
    10439  * @param config
    10440  * @return
    10441  */
    10442 HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
    10443 {
    10444     AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
    10445 
    10446     HRESULT rc = S_OK;
    10447 
    10448     try
    10449     {
    10450         config.llFirstSnapshot.clear();
    10451 
    10452         if (mData->mFirstSnapshot)
    10453         {
    10454             settings::Snapshot snapNew;
    10455             config.llFirstSnapshot.push_back(snapNew);
    10456 
    10457             // get reference to the fresh copy of the snapshot on the list and
    10458             // work on that copy directly to avoid excessive copying later
    10459             settings::Snapshot &snap = config.llFirstSnapshot.front();
    10460 
    10461             rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
    10462             if (FAILED(rc)) throw rc;
    10463         }
    10464 
    10465 //         if (mType == IsSessionMachine)
    10466 //             mParent->onMachineDataChange(mData->mUuid);          @todo is this necessary?
    10467 
    10468     }
    10469     catch (HRESULT err)
    10470     {
    10471         /* we assume that error info is set by the thrower */
    10472         rc = err;
    10473     }
    10474     catch (...)
    10475     {
    10476         rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    10477     }
    10478 
    10479     return rc;
    10480 }
    10481 
    10482 /**
    10483  *  Saves the VM hardware configuration. It is assumed that the
    10484  *  given node is empty.
    10485  *
    10486  *  @param data           Reference to the settings object for the hardware config.
    10487  *  @param pDbg           Pointer to the settings object for the debugging config
    10488  *                        which happens to live in mHWData.
    10489  *  @param pAutostart     Pointer to the settings object for the autostart config
    10490  *                        which happens to live in mHWData.
    10491  */
    10492 HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
    10493                               settings::Autostart *pAutostart)
    10494 {
    10495     HRESULT rc = S_OK;
    10496 
    10497     try
    10498     {
    10499         /* The hardware version attribute (optional).
    10500             Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
    10501         if (    mHWData->mHWVersion == "1"
    10502              && mSSData->strStateFilePath.isEmpty()
    10503            )
    10504             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. */
    10505 
    10506         data.strVersion = mHWData->mHWVersion;
    10507         data.uuid = mHWData->mHardwareUUID;
    10508 
    10509         // CPU
    10510         data.fHardwareVirt          = !!mHWData->mHWVirtExEnabled;
    10511         data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
    10512         data.fNestedPaging          = !!mHWData->mHWVirtExNestedPagingEnabled;
    10513         data.fLargePages            = !!mHWData->mHWVirtExLargePagesEnabled;
    10514         data.fVPID                  = !!mHWData->mHWVirtExVPIDEnabled;
    10515         data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
    10516         data.fHardwareVirtForce     = !!mHWData->mHWVirtExForceEnabled;
    10517         data.fPAE                   = !!mHWData->mPAEEnabled;
    10518         data.enmLongMode            = mHWData->mLongMode;
    10519         data.fSyntheticCpu          = !!mHWData->mSyntheticCpu;
    10520 
    10521         /* Standard and Extended CPUID leafs. */
    10522         data.llCpuIdLeafs.clear();
    10523         for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
    10524         {
    10525             if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
    10526                 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
    10527         }
    10528         for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
    10529         {
    10530             if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
    10531                 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
    10532         }
    10533 
    10534         data.cCPUs             = mHWData->mCPUCount;
    10535         data.fCpuHotPlug       = !!mHWData->mCPUHotPlugEnabled;
    10536         data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
    10537 
    10538         data.llCpus.clear();
    10539         if (data.fCpuHotPlug)
    10540         {
    10541             for (unsigned idx = 0; idx < data.cCPUs; idx++)
    10542             {
    10543                 if (mHWData->mCPUAttached[idx])
    10544                 {
    10545                     settings::Cpu cpu;
    10546                     cpu.ulId = idx;
    10547                     data.llCpus.push_back(cpu);
    10548                 }
    10549             }
    10550         }
    10551 
    10552         // memory
    10553         data.ulMemorySizeMB = mHWData->mMemorySize;
    10554         data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
    10555 
    10556         // firmware
    10557         data.firmwareType = mHWData->mFirmwareType;
    10558 
    10559         // HID
    10560         data.pointingHIDType = mHWData->mPointingHIDType;
    10561         data.keyboardHIDType = mHWData->mKeyboardHIDType;
    10562 
    10563         // chipset
    10564         data.chipsetType = mHWData->mChipsetType;
    10565 
    10566         data.fEmulatedUSBWebcam     = !!mHWData->mEmulatedUSBWebcamEnabled;
    10567         data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
    10568 
    10569         // HPET
    10570         data.fHPETEnabled = !!mHWData->mHPETEnabled;
    10571 
    10572         // boot order
    10573         data.mapBootOrder.clear();
    10574         for (size_t i = 0;
    10575              i < RT_ELEMENTS(mHWData->mBootOrder);
    10576              ++i)
    10577             data.mapBootOrder[i] = mHWData->mBootOrder[i];
    10578 
    10579         // display
    10580         data.graphicsControllerType = mHWData->mGraphicsControllerType;
    10581         data.ulVRAMSizeMB = mHWData->mVRAMSize;
    10582         data.cMonitors = mHWData->mMonitorCount;
    10583         data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
    10584         data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
    10585         data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
    10586         data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
    10587         data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
    10588         data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
    10589         data.fVideoCaptureEnabled  = !!mHWData->mVideoCaptureEnabled;
    10590         for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
    10591         {
    10592             if (mHWData->maVideoCaptureScreens[i])
    10593                 ASMBitSet(&data.u64VideoCaptureScreens, i);
    10594             else
    10595                 ASMBitClear(&data.u64VideoCaptureScreens, i);
    10596         }
    10597         /* store relative video capture file if possible */
    10598         copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
    10599 
    10600         /* VRDEServer settings (optional) */
    10601         rc = mVRDEServer->saveSettings(data.vrdeSettings);
    10602         if (FAILED(rc)) throw rc;
    10603 
    10604         /* BIOS (required) */
    10605         rc = mBIOSSettings->saveSettings(data.biosSettings);
    10606         if (FAILED(rc)) throw rc;
    10607 
    10608         /* USB Controller (required) */
    10609         for (USBControllerList::const_iterator it = mUSBControllers->begin();
    10610              it != mUSBControllers->end();
    10611              ++it)
    10612         {
    10613             ComObjPtr<USBController> ctrl = *it;
    10614             settings::USBController settingsCtrl;
    10615 
    10616             settingsCtrl.strName = ctrl->getName();
    10617             settingsCtrl.enmType = ctrl->getControllerType();
    10618 
    10619             data.usbSettings.llUSBControllers.push_back(settingsCtrl);
    10620         }
    10621 
    10622         /* USB device filters (required) */
    10623         rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
    10624         if (FAILED(rc)) throw rc;
    10625 
    10626         /* Network adapters (required) */
    10627         uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
    10628         data.llNetworkAdapters.clear();
    10629         /* Write out only the nominal number of network adapters for this
    10630          * chipset type. Since Machine::commit() hasn't been called there
    10631          * may be extra NIC settings in the vector. */
    10632         for (ULONG slot = 0; slot < uMaxNICs; ++slot)
    10633         {
    10634             settings::NetworkAdapter nic;
    10635             nic.ulSlot = slot;
    10636             /* paranoia check... must not be NULL, but must not crash either. */
    10637             if (mNetworkAdapters[slot])
    10638             {
    10639                 rc = mNetworkAdapters[slot]->saveSettings(nic);
    10640                 if (FAILED(rc)) throw rc;
    10641 
    10642                 data.llNetworkAdapters.push_back(nic);
    10643             }
    10644         }
    10645 
    10646         /* Serial ports */
    10647         data.llSerialPorts.clear();
    10648         for (ULONG slot = 0;
    10649              slot < RT_ELEMENTS(mSerialPorts);
    10650              ++slot)
    10651         {
    10652             settings::SerialPort s;
    10653             s.ulSlot = slot;
    10654             rc = mSerialPorts[slot]->saveSettings(s);
    10655             if (FAILED(rc)) return rc;
    10656 
    10657             data.llSerialPorts.push_back(s);
    10658         }
    10659 
    10660         /* Parallel ports */
    10661         data.llParallelPorts.clear();
    10662         for (ULONG slot = 0;
    10663              slot < RT_ELEMENTS(mParallelPorts);
    10664              ++slot)
    10665         {
    10666             settings::ParallelPort p;
    10667             p.ulSlot = slot;
    10668             rc = mParallelPorts[slot]->saveSettings(p);
    10669             if (FAILED(rc)) return rc;
    10670 
    10671             data.llParallelPorts.push_back(p);
    10672         }
    10673 
    10674         /* Audio adapter */
    10675         rc = mAudioAdapter->saveSettings(data.audioAdapter);
    10676         if (FAILED(rc)) return rc;
    10677 
    10678         /* Shared folders */
    10679         data.llSharedFolders.clear();
    10680         for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
    10681             it != mHWData->mSharedFolders.end();
    10682             ++it)
    10683         {
    10684             SharedFolder *pSF = *it;
    10685             AutoCaller sfCaller(pSF);
    10686             AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
    10687             settings::SharedFolder sf;
    10688             sf.strName = pSF->getName();
    10689             sf.strHostPath = pSF->getHostPath();
    10690             sf.fWritable = !!pSF->isWritable();
    10691             sf.fAutoMount = !!pSF->isAutoMounted();
    10692 
    10693             data.llSharedFolders.push_back(sf);
    10694         }
    10695 
    10696         // clipboard
    10697         data.clipboardMode = mHWData->mClipboardMode;
    10698 
    10699         // drag'n'drop
    10700         data.dragAndDropMode = mHWData->mDragAndDropMode;
    10701 
    10702         /* Guest */
    10703         data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
    10704 
    10705         // IO settings
    10706         data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
    10707         data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
    10708 
    10709         /* BandwidthControl (required) */
    10710         rc = mBandwidthControl->saveSettings(data.ioSettings);
    10711         if (FAILED(rc)) throw rc;
    10712 
    10713         /* Host PCI devices */
    10714         for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
    10715              it != mHWData->mPCIDeviceAssignments.end();
    10716              ++it)
    10717         {
    10718             ComObjPtr<PCIDeviceAttachment> pda = *it;
    10719             settings::HostPCIDeviceAttachment hpda;
    10720 
    10721             rc = pda->saveSettings(hpda);
    10722             if (FAILED(rc)) throw rc;
    10723 
    10724             data.pciAttachments.push_back(hpda);
    10725         }
    10726 
    10727 
    10728         // guest properties
    10729         data.llGuestProperties.clear();
    10730 #ifdef VBOX_WITH_GUEST_PROPS
    10731         for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
    10732              it != mHWData->mGuestProperties.end();
    10733              ++it)
    10734         {
    10735             HWData::GuestProperty property = it->second;
    10736 
    10737             /* Remove transient guest properties at shutdown unless we
    10738              * are saving state */
    10739             if (   (   mData->mMachineState == MachineState_PoweredOff
    10740                     || mData->mMachineState == MachineState_Aborted
    10741                     || mData->mMachineState == MachineState_Teleported)
    10742                 && (   property.mFlags & guestProp::TRANSIENT
    10743                     || property.mFlags & guestProp::TRANSRESET))
    10744                 continue;
    10745             settings::GuestProperty prop;
    10746             prop.strName = it->first;
    10747             prop.strValue = property.strValue;
    10748             prop.timestamp = property.mTimestamp;
    10749             char szFlags[guestProp::MAX_FLAGS_LEN + 1];
    10750             guestProp::writeFlags(property.mFlags, szFlags);
    10751             prop.strFlags = szFlags;
    10752 
    10753             data.llGuestProperties.push_back(prop);
    10754         }
    10755 
    10756         data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
    10757         /* I presume this doesn't require a backup(). */
    10758         mData->mGuestPropertiesModified = FALSE;
    10759 #endif /* VBOX_WITH_GUEST_PROPS defined */
    10760 
    10761         *pDbg = mHWData->mDebugging;
    10762         *pAutostart = mHWData->mAutostart;
    10763 
    10764         data.strDefaultFrontend = mHWData->mDefaultFrontend;
    10765     }
    10766     catch(std::bad_alloc &)
    10767     {
    10768         return E_OUTOFMEMORY;
    10769     }
    10770 
    10771     AssertComRC(rc);
    10772     return rc;
    10773 }
    10774 
    10775 /**
    10776  *  Saves the storage controller configuration.
    10777  *
    10778  *  @param aNode    <StorageControllers> node to save the VM hardware configuration to.
    10779  */
    10780 HRESULT Machine::saveStorageControllers(settings::Storage &data)
    10781 {
    10782     data.llStorageControllers.clear();
    10783 
    10784     for (StorageControllerList::const_iterator it = mStorageControllers->begin();
    10785          it != mStorageControllers->end();
    10786          ++it)
    10787     {
    10788         HRESULT rc;
    10789         ComObjPtr<StorageController> pCtl = *it;
    10790 
    10791         settings::StorageController ctl;
    10792         ctl.strName = pCtl->getName();
    10793         ctl.controllerType = pCtl->getControllerType();
    10794         ctl.storageBus = pCtl->getStorageBus();
    10795         ctl.ulInstance = pCtl->getInstance();
    10796         ctl.fBootable = pCtl->getBootable();
    10797 
    10798         /* Save the port count. */
    10799         ULONG portCount;
    10800         rc = pCtl->COMGETTER(PortCount)(&portCount);
    10801         ComAssertComRCRet(rc, rc);
    10802         ctl.ulPortCount = portCount;
    10803 
    10804         /* Save fUseHostIOCache */
    10805         BOOL fUseHostIOCache;
    10806         rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
    10807         ComAssertComRCRet(rc, rc);
    10808         ctl.fUseHostIOCache = !!fUseHostIOCache;
    10809 
    10810         /* Save IDE emulation settings. */
    10811         if (ctl.controllerType == StorageControllerType_IntelAhci)
    10812         {
    10813             if (    (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
    10814                  || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
    10815                  || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
    10816                  || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
    10817                )
    10818                 ComAssertComRCRet(rc, rc);
    10819         }
    10820 
    10821         /* save the devices now. */
    10822         rc = saveStorageDevices(pCtl, ctl);
    10823         ComAssertComRCRet(rc, rc);
    10824 
    10825         data.llStorageControllers.push_back(ctl);
    10826     }
    10827 
    10828     return S_OK;
    10829 }
    10830 
    10831 /**
    10832  *  Saves the hard disk configuration.
    10833  */
    10834 HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
    10835                                     settings::StorageController &data)
    10836 {
    10837     MediaData::AttachmentList atts;
    10838 
    10839     HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
    10840     if (FAILED(rc)) return rc;
    10841 
    10842     data.llAttachedDevices.clear();
    10843     for (MediaData::AttachmentList::const_iterator it = atts.begin();
    10844          it != atts.end();
    10845          ++it)
    10846     {
    10847         settings::AttachedDevice dev;
    10848 
    10849         MediumAttachment *pAttach = *it;
    10850         Medium *pMedium = pAttach->getMedium();
    10851 
    10852         dev.deviceType = pAttach->getType();
    10853         dev.lPort = pAttach->getPort();
    10854         dev.lDevice = pAttach->getDevice();
    10855         if (pMedium)
    10856         {
    10857             if (pMedium->isHostDrive())
    10858                 dev.strHostDriveSrc = pMedium->getLocationFull();
    10859             else
    10860                 dev.uuid = pMedium->getId();
    10861             dev.fPassThrough = pAttach->getPassthrough();
    10862             dev.fTempEject = pAttach->getTempEject();
    10863             dev.fNonRotational = pAttach->getNonRotational();
    10864             dev.fDiscard = pAttach->getDiscard();
    10865         }
    10866 
    10867         dev.strBwGroup = pAttach->getBandwidthGroup();
    10868 
    10869         data.llAttachedDevices.push_back(dev);
    10870     }
    10871 
    10872     return S_OK;
    10873 }
    10874 
    10875 /**
    10876  *  Saves machine state settings as defined by aFlags
    10877  *  (SaveSTS_* values).
    10878  *
    10879  *  @param aFlags   Combination of SaveSTS_* flags.
    10880  *
    10881  *  @note Locks objects for writing.
    10882  */
    10883 HRESULT Machine::saveStateSettings(int aFlags)
    10884 {
    10885     if (aFlags == 0)
    10886         return S_OK;
    10887 
    10888     AutoCaller autoCaller(this);
    10889     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    10890 
    10891     /* This object's write lock is also necessary to serialize file access
    10892      * (prevent concurrent reads and writes) */
    10893     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    10894 
    10895     HRESULT rc = S_OK;
    10896 
    10897     Assert(mData->pMachineConfigFile);
    10898 
    10899     try
    10900     {
    10901         if (aFlags & SaveSTS_CurStateModified)
    10902             mData->pMachineConfigFile->fCurrentStateModified = true;
    10903 
    10904         if (aFlags & SaveSTS_StateFilePath)
    10905         {
    10906             if (!mSSData->strStateFilePath.isEmpty())
    10907                 /* try to make the file name relative to the settings file dir */
    10908                 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
    10909             else
    10910                 mData->pMachineConfigFile->strStateFile.setNull();
    10911         }
    10912 
    10913         if (aFlags & SaveSTS_StateTimeStamp)
    10914         {
    10915             Assert(    mData->mMachineState != MachineState_Aborted
    10916                     || mSSData->strStateFilePath.isEmpty());
    10917 
    10918             mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
    10919 
    10920             mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
    10921 //@todo live migration             mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
    10922         }
    10923 
    10924         mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
    10925     }
    10926     catch (...)
    10927     {
    10928         rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    10929     }
    10930 
    10931     return rc;
    10932 }
    10933 
    10934 /**
    10935  * Ensures that the given medium is added to a media registry. If this machine
    10936  * was created with 4.0 or later, then the machine registry is used. Otherwise
    10937  * the global VirtualBox media registry is used.
    10938  *
    10939  * Caller must NOT hold machine lock, media tree or any medium locks!
    10940  *
    10941  * @param pMedium
    10942  */
    10943 void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
    10944 {
    10945     /* Paranoia checks: do not hold machine or media tree locks. */
    10946     AssertReturnVoid(!isWriteLockOnCurrentThread());
    10947     AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    10948 
    10949     ComObjPtr<Medium> pBase;
    10950     {
    10951         AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    10952         pBase = pMedium->getBase();
    10953     }
    10954 
    10955     /* Paranoia checks: do not hold medium locks. */
    10956     AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
    10957     AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
    10958 
    10959     // decide which medium registry to use now that the medium is attached:
    10960     Guid uuid;
    10961     if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
    10962         // machine XML is VirtualBox 4.0 or higher:
    10963         uuid = getId();     // machine UUID
    10964     else
    10965         uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
    10966 
    10967     if (pMedium->addRegistry(uuid, false /* fRecurse */))
    10968         mParent->markRegistryModified(uuid);
    10969 
    10970     /* For more complex hard disk structures it can happen that the base
    10971      * medium isn't yet associated with any medium registry. Do that now. */
    10972     if (pMedium != pBase)
    10973     {
    10974         if (pBase->addRegistry(uuid, true /* fRecurse */))
    10975             mParent->markRegistryModified(uuid);
    10976     }
    10977 }
    10978 
    10979 /**
    10980  * Creates differencing hard disks for all normal hard disks attached to this
    10981  * machine and a new set of attachments to refer to created disks.
    10982  *
    10983  * Used when taking a snapshot or when deleting the current state. Gets called
    10984  * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
    10985  *
    10986  * This method assumes that mMediaData contains the original hard disk attachments
    10987  * it needs to create diffs for. On success, these attachments will be replaced
    10988  * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
    10989  * called to delete created diffs which will also rollback mMediaData and restore
    10990  * whatever was backed up before calling this method.
    10991  *
    10992  * Attachments with non-normal hard disks are left as is.
    10993  *
    10994  * If @a aOnline is @c false then the original hard disks that require implicit
    10995  * diffs will be locked for reading. Otherwise it is assumed that they are
    10996  * already locked for writing (when the VM was started). Note that in the latter
    10997  * case it is responsibility of the caller to lock the newly created diffs for
    10998  * writing if this method succeeds.
    10999  *
    11000  * @param aProgress         Progress object to run (must contain at least as
    11001  *                          many operations left as the number of hard disks
    11002  *                          attached).
    11003  * @param aOnline           Whether the VM was online prior to this operation.
    11004  *
    11005  * @note The progress object is not marked as completed, neither on success nor
    11006  *       on failure. This is a responsibility of the caller.
    11007  *
    11008  * @note Locks this object and the media tree for writing.
    11009  */
    11010 HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
    11011                                      ULONG aWeight,
    11012                                      bool aOnline)
    11013 {
    11014     LogFlowThisFunc(("aOnline=%d\n", aOnline));
    11015 
    11016     AutoCaller autoCaller(this);
    11017     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    11018 
    11019     AutoMultiWriteLock2 alock(this->lockHandle(),
    11020                               &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    11021 
    11022     /* must be in a protective state because we release the lock below */
    11023     AssertReturn(   mData->mMachineState == MachineState_Saving
    11024                  || mData->mMachineState == MachineState_LiveSnapshotting
    11025                  || mData->mMachineState == MachineState_RestoringSnapshot
    11026                  || mData->mMachineState == MachineState_DeletingSnapshot
    11027                  , E_FAIL);
    11028 
    11029     HRESULT rc = S_OK;
    11030 
    11031     // use appropriate locked media map (online or offline)
    11032     MediumLockListMap lockedMediaOffline;
    11033     MediumLockListMap *lockedMediaMap;
    11034     if (aOnline)
    11035         lockedMediaMap = &mData->mSession.mLockedMedia;
    11036     else
    11037         lockedMediaMap = &lockedMediaOffline;
    11038 
    11039     try
    11040     {
    11041         if (!aOnline)
    11042         {
    11043             /* lock all attached hard disks early to detect "in use"
    11044              * situations before creating actual diffs */
    11045             for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11046                  it != mMediaData->mAttachments.end();
    11047                  ++it)
    11048             {
    11049                 MediumAttachment* pAtt = *it;
    11050                 if (pAtt->getType() == DeviceType_HardDisk)
    11051                 {
    11052                     Medium* pMedium = pAtt->getMedium();
    11053                     Assert(pMedium);
    11054 
    11055                     MediumLockList *pMediumLockList(new MediumLockList());
    11056                     alock.release();
    11057                     rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
    11058                                                        false /* fMediumLockWrite */,
    11059                                                        NULL,
    11060                                                        *pMediumLockList);
    11061                     alock.acquire();
    11062                     if (FAILED(rc))
    11063                     {
    11064                         delete pMediumLockList;
    11065                         throw rc;
    11066                     }
    11067                     rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
    11068                     if (FAILED(rc))
    11069                     {
    11070                         throw setError(rc,
    11071                                        tr("Collecting locking information for all attached media failed"));
    11072                     }
    11073                 }
    11074             }
    11075 
    11076             /* Now lock all media. If this fails, nothing is locked. */
    11077             alock.release();
    11078             rc = lockedMediaMap->Lock();
    11079             alock.acquire();
    11080             if (FAILED(rc))
    11081             {
    11082                 throw setError(rc,
    11083                                tr("Locking of attached media failed"));
    11084             }
    11085         }
    11086 
    11087         /* remember the current list (note that we don't use backup() since
    11088          * mMediaData may be already backed up) */
    11089         MediaData::AttachmentList atts = mMediaData->mAttachments;
    11090 
    11091         /* start from scratch */
    11092         mMediaData->mAttachments.clear();
    11093 
    11094         /* go through remembered attachments and create diffs for normal hard
    11095          * disks and attach them */
    11096         for (MediaData::AttachmentList::const_iterator it = atts.begin();
    11097              it != atts.end();
    11098              ++it)
    11099         {
    11100             MediumAttachment* pAtt = *it;
    11101 
    11102             DeviceType_T devType = pAtt->getType();
    11103             Medium* pMedium = pAtt->getMedium();
    11104 
    11105             if (   devType != DeviceType_HardDisk
    11106                 || pMedium == NULL
    11107                 || pMedium->getType() != MediumType_Normal)
    11108             {
    11109                 /* copy the attachment as is */
    11110 
    11111                 /** @todo the progress object created in Console::TakeSnaphot
    11112                  * only expects operations for hard disks. Later other
    11113                  * device types need to show up in the progress as well. */
    11114                 if (devType == DeviceType_HardDisk)
    11115                 {
    11116                     if (pMedium == NULL)
    11117                         aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
    11118                                                     aWeight);        // weight
    11119                     else
    11120                         aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
    11121                                                             pMedium->getBase()->getName().c_str()).raw(),
    11122                                                     aWeight);        // weight
    11123                 }
    11124 
    11125                 mMediaData->mAttachments.push_back(pAtt);
    11126                 continue;
    11127             }
    11128 
    11129             /* need a diff */
    11130             aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
    11131                                                 pMedium->getBase()->getName().c_str()).raw(),
    11132                                         aWeight);        // weight
    11133 
    11134             Utf8Str strFullSnapshotFolder;
    11135             calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
    11136 
    11137             ComObjPtr<Medium> diff;
    11138             diff.createObject();
    11139             // store the diff in the same registry as the parent
    11140             // (this cannot fail here because we can't create implicit diffs for
    11141             // unregistered images)
    11142             Guid uuidRegistryParent;
    11143             bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
    11144             Assert(fInRegistry); NOREF(fInRegistry);
    11145             rc = diff->init(mParent,
    11146                             pMedium->getPreferredDiffFormat(),
    11147                             strFullSnapshotFolder.append(RTPATH_SLASH_STR),
    11148                             uuidRegistryParent);
    11149             if (FAILED(rc)) throw rc;
    11150 
    11151             /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
    11152              *        the push_back?  Looks like we're going to release medium with the
    11153              *        wrong kind of lock (general issue with if we fail anywhere at all)
    11154              *        and an orphaned VDI in the snapshots folder. */
    11155 
    11156             /* update the appropriate lock list */
    11157             MediumLockList *pMediumLockList;
    11158             rc = lockedMediaMap->Get(pAtt, pMediumLockList);
    11159             AssertComRCThrowRC(rc);
    11160             if (aOnline)
    11161             {
    11162                 alock.release();
    11163                 /* The currently attached medium will be read-only, change
    11164                  * the lock type to read. */
    11165                 rc = pMediumLockList->Update(pMedium, false);
    11166                 alock.acquire();
    11167                 AssertComRCThrowRC(rc);
    11168             }
    11169 
    11170             /* release the locks before the potentially lengthy operation */
    11171             alock.release();
    11172             rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
    11173                                             pMediumLockList,
    11174                                             NULL /* aProgress */,
    11175                                             true /* aWait */);
    11176             alock.acquire();
    11177             if (FAILED(rc)) throw rc;
    11178 
    11179             /* actual lock list update is done in Medium::commitMedia */
    11180 
    11181             rc = diff->addBackReference(mData->mUuid);
    11182             AssertComRCThrowRC(rc);
    11183 
    11184             /* add a new attachment */
    11185             ComObjPtr<MediumAttachment> attachment;
    11186             attachment.createObject();
    11187             rc = attachment->init(this,
    11188                                   diff,
    11189                                   pAtt->getControllerName(),
    11190                                   pAtt->getPort(),
    11191                                   pAtt->getDevice(),
    11192                                   DeviceType_HardDisk,
    11193                                   true /* aImplicit */,
    11194                                   false /* aPassthrough */,
    11195                                   false /* aTempEject */,
    11196                                   pAtt->getNonRotational(),
    11197                                   pAtt->getDiscard(),
    11198                                   pAtt->getBandwidthGroup());
    11199             if (FAILED(rc)) throw rc;
    11200 
    11201             rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
    11202             AssertComRCThrowRC(rc);
    11203             mMediaData->mAttachments.push_back(attachment);
    11204         }
    11205     }
    11206     catch (HRESULT aRC) { rc = aRC; }
    11207 
    11208     /* unlock all hard disks we locked when there is no VM */
    11209     if (!aOnline)
    11210     {
    11211         ErrorInfoKeeper eik;
    11212 
    11213         HRESULT rc1 = lockedMediaMap->Clear();
    11214         AssertComRC(rc1);
    11215     }
    11216 
    11217     return rc;
    11218 }
    11219 
    11220 /**
    11221  * Deletes implicit differencing hard disks created either by
    11222  * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
    11223  *
    11224  * Note that to delete hard disks created by #AttachDevice() this method is
    11225  * called from #fixupMedia() when the changes are rolled back.
    11226  *
    11227  * @note Locks this object and the media tree for writing.
    11228  */
    11229 HRESULT Machine::deleteImplicitDiffs(bool aOnline)
    11230 {
    11231     LogFlowThisFunc(("aOnline=%d\n", aOnline));
    11232 
    11233     AutoCaller autoCaller(this);
    11234     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    11235 
    11236     AutoMultiWriteLock2 alock(this->lockHandle(),
    11237                               &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    11238 
    11239     /* We absolutely must have backed up state. */
    11240     AssertReturn(mMediaData.isBackedUp(), E_FAIL);
    11241 
    11242     /* Check if there are any implicitly created diff images. */
    11243     bool fImplicitDiffs = false;
    11244     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11245          it != mMediaData->mAttachments.end();
    11246          ++it)
    11247     {
    11248         const ComObjPtr<MediumAttachment> &pAtt = *it;
    11249         if (pAtt->isImplicit())
    11250         {
    11251             fImplicitDiffs = true;
    11252             break;
    11253         }
    11254     }
    11255     /* If there is nothing to do, leave early. This saves lots of image locking
    11256      * effort. It also avoids a MachineStateChanged event without real reason.
    11257      * This is important e.g. when loading a VM config, because there should be
    11258      * no events. Otherwise API clients can become thoroughly confused for
    11259      * inaccessible VMs (the code for loading VM configs uses this method for
    11260      * cleanup if the config makes no sense), as they take such events as an
    11261      * indication that the VM is alive, and they would force the VM config to
    11262      * be reread, leading to an endless loop. */
    11263     if (!fImplicitDiffs)
    11264         return S_OK;
    11265 
    11266     HRESULT rc = S_OK;
    11267     MachineState_T oldState = mData->mMachineState;
    11268 
    11269     /* will release the lock before the potentially lengthy operation,
    11270      * so protect with the special state (unless already protected) */
    11271     if (   oldState != MachineState_Saving
    11272         && oldState != MachineState_LiveSnapshotting
    11273         && oldState != MachineState_RestoringSnapshot
    11274         && oldState != MachineState_DeletingSnapshot
    11275         && oldState != MachineState_DeletingSnapshotOnline
    11276         && oldState != MachineState_DeletingSnapshotPaused
    11277        )
    11278         setMachineState(MachineState_SettingUp);
    11279 
    11280     // use appropriate locked media map (online or offline)
    11281     MediumLockListMap lockedMediaOffline;
    11282     MediumLockListMap *lockedMediaMap;
    11283     if (aOnline)
    11284         lockedMediaMap = &mData->mSession.mLockedMedia;
    11285     else
    11286         lockedMediaMap = &lockedMediaOffline;
    11287 
    11288     try
    11289     {
    11290         if (!aOnline)
    11291         {
    11292             /* lock all attached hard disks early to detect "in use"
    11293              * situations before deleting actual diffs */
    11294             for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11295                it != mMediaData->mAttachments.end();
    11296                ++it)
    11297             {
    11298                 MediumAttachment* pAtt = *it;
    11299                 if (pAtt->getType() == DeviceType_HardDisk)
    11300                 {
    11301                     Medium* pMedium = pAtt->getMedium();
    11302                     Assert(pMedium);
    11303 
    11304                     MediumLockList *pMediumLockList(new MediumLockList());
    11305                     alock.release();
    11306                     rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
    11307                                                        false /* fMediumLockWrite */,
    11308                                                        NULL,
    11309                                                        *pMediumLockList);
    11310                     alock.acquire();
    11311 
    11312                     if (FAILED(rc))
    11313                     {
    11314                         delete pMediumLockList;
    11315                         throw rc;
    11316                     }
    11317 
    11318                     rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
    11319                     if (FAILED(rc))
    11320                         throw rc;
    11321                 }
    11322             }
    11323 
    11324             if (FAILED(rc))
    11325                 throw rc;
    11326         } // end of offline
    11327 
    11328         /* Lock lists are now up to date and include implicitly created media */
    11329 
    11330         /* Go through remembered attachments and delete all implicitly created
    11331          * diffs and fix up the attachment information */
    11332         const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
    11333         MediaData::AttachmentList implicitAtts;
    11334         for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11335              it != mMediaData->mAttachments.end();
    11336              ++it)
    11337         {
    11338             ComObjPtr<MediumAttachment> pAtt = *it;
    11339             ComObjPtr<Medium> pMedium = pAtt->getMedium();
    11340             if (pMedium.isNull())
    11341                 continue;
    11342 
    11343             // Implicit attachments go on the list for deletion and back references are removed.
    11344             if (pAtt->isImplicit())
    11345             {
    11346                 /* Deassociate and mark for deletion */
    11347                 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
    11348                 rc = pMedium->removeBackReference(mData->mUuid);
    11349                 if (FAILED(rc))
    11350                    throw rc;
    11351                 implicitAtts.push_back(pAtt);
    11352                 continue;
    11353             }
    11354 
    11355             /* Was this medium attached before? */
    11356             if (!findAttachment(oldAtts, pMedium))
    11357             {
    11358                 /* no: de-associate */
    11359                 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
    11360                 rc = pMedium->removeBackReference(mData->mUuid);
    11361                 if (FAILED(rc))
    11362                     throw rc;
    11363                 continue;
    11364             }
    11365             LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
    11366         }
    11367 
    11368         /* If there are implicit attachments to delete, throw away the lock
    11369          * map contents (which will unlock all media) since the medium
    11370          * attachments will be rolled back. Below we need to completely
    11371          * recreate the lock map anyway since it is infinitely complex to
    11372          * do this incrementally (would need reconstructing each attachment
    11373          * change, which would be extremely hairy). */
    11374         if (implicitAtts.size() != 0)
    11375         {
    11376             ErrorInfoKeeper eik;
    11377 
    11378             HRESULT rc1 = lockedMediaMap->Clear();
    11379             AssertComRC(rc1);
    11380         }
    11381 
    11382         /* rollback hard disk changes */
    11383         mMediaData.rollback();
    11384 
    11385         MultiResult mrc(S_OK);
    11386 
    11387         // Delete unused implicit diffs.
    11388         if (implicitAtts.size() != 0)
    11389         {
    11390             alock.release();
    11391 
    11392             for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
    11393                  it != implicitAtts.end();
    11394                  ++it)
    11395             {
    11396                 // Remove medium associated with this attachment.
    11397                 ComObjPtr<MediumAttachment> pAtt = *it;
    11398                 Assert(pAtt);
    11399                 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
    11400                 ComObjPtr<Medium> pMedium = pAtt->getMedium();
    11401                 Assert(pMedium);
    11402 
    11403                 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
    11404                 // continue on delete failure, just collect error messages
    11405                 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
    11406                 mrc = rc;
    11407             }
    11408 
    11409             alock.acquire();
    11410 
    11411             /* if there is a VM recreate media lock map as mentioned above,
    11412              * otherwise it is a waste of time and we leave things unlocked */
    11413             if (aOnline)
    11414             {
    11415                 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
    11416                 /* must never be NULL, but better safe than sorry */
    11417                 if (!pMachine.isNull())
    11418                 {
    11419                     alock.release();
    11420                     rc = mData->mSession.mMachine->lockMedia();
    11421                     alock.acquire();
    11422                     if (FAILED(rc))
    11423                         throw rc;
    11424                 }
    11425             }
    11426         }
    11427     }
    11428     catch (HRESULT aRC) {rc = aRC;}
    11429 
    11430     if (mData->mMachineState == MachineState_SettingUp)
    11431         setMachineState(oldState);
    11432 
    11433     /* unlock all hard disks we locked when there is no VM */
    11434     if (!aOnline)
    11435     {
    11436         ErrorInfoKeeper eik;
    11437 
    11438         HRESULT rc1 = lockedMediaMap->Clear();
    11439         AssertComRC(rc1);
    11440     }
    11441 
    11442     return rc;
    11443 }
    11444 
    11445 
    11446 /**
    11447  * Looks through the given list of media attachments for one with the given parameters
    11448  * and returns it, or NULL if not found. The list is a parameter so that backup lists
    11449  * can be searched as well if needed.
    11450  *
    11451  * @param list
    11452  * @param aControllerName
    11453  * @param aControllerPort
    11454  * @param aDevice
    11455  * @return
    11456  */
    11457 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
    11458                                           IN_BSTR aControllerName,
    11459                                           LONG aControllerPort,
    11460                                           LONG aDevice)
    11461 {
    11462    for (MediaData::AttachmentList::const_iterator it = ll.begin();
    11463         it != ll.end();
    11464         ++it)
    11465     {
    11466         MediumAttachment *pAttach = *it;
    11467         if (pAttach->matches(aControllerName, aControllerPort, aDevice))
    11468             return pAttach;
    11469     }
    11470 
    11471     return NULL;
    11472 }
    11473 
    11474 /**
    11475  * Looks through the given list of media attachments for one with the given parameters
    11476  * and returns it, or NULL if not found. The list is a parameter so that backup lists
    11477  * can be searched as well if needed.
    11478  *
    11479  * @param list
    11480  * @param aControllerName
    11481  * @param aControllerPort
    11482  * @param aDevice
    11483  * @return
    11484  */
    11485 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
    11486                                           ComObjPtr<Medium> pMedium)
    11487 {
    11488    for (MediaData::AttachmentList::const_iterator it = ll.begin();
    11489         it != ll.end();
    11490         ++it)
    11491     {
    11492         MediumAttachment *pAttach = *it;
    11493         ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
    11494         if (pMediumThis == pMedium)
    11495             return pAttach;
    11496     }
    11497 
    11498     return NULL;
    11499 }
    11500 
    11501 /**
    11502  * Looks through the given list of media attachments for one with the given parameters
    11503  * and returns it, or NULL if not found. The list is a parameter so that backup lists
    11504  * can be searched as well if needed.
    11505  *
    11506  * @param list
    11507  * @param aControllerName
    11508  * @param aControllerPort
    11509  * @param aDevice
    11510  * @return
    11511  */
    11512 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
    11513                                           Guid &id)
    11514 {
    11515    for (MediaData::AttachmentList::const_iterator it = ll.begin();
    11516          it != ll.end();
    11517          ++it)
    11518     {
    11519         MediumAttachment *pAttach = *it;
    11520         ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
    11521         if (pMediumThis->getId() == id)
    11522             return pAttach;
    11523     }
    11524 
    11525     return NULL;
    11526 }
    11527 
    11528 /**
    11529  * Main implementation for Machine::DetachDevice. This also gets called
    11530  * from Machine::prepareUnregister() so it has been taken out for simplicity.
    11531  *
    11532  * @param pAttach Medium attachment to detach.
    11533  * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
    11534  * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
    11535  * @return
    11536  */
    11537 HRESULT Machine::detachDevice(MediumAttachment *pAttach,
    11538                               AutoWriteLock &writeLock,
    11539                               Snapshot *pSnapshot)
    11540 {
    11541     ComObjPtr<Medium> oldmedium = pAttach->getMedium();
    11542     DeviceType_T mediumType = pAttach->getType();
    11543 
    11544     LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
    11545 
    11546     if (pAttach->isImplicit())
    11547     {
    11548         /* attempt to implicitly delete the implicitly created diff */
    11549 
    11550         /// @todo move the implicit flag from MediumAttachment to Medium
    11551         /// and forbid any hard disk operation when it is implicit. Or maybe
    11552         /// a special media state for it to make it even more simple.
    11553 
    11554         Assert(mMediaData.isBackedUp());
    11555 
    11556         /* will release the lock before the potentially lengthy operation, so
    11557          * protect with the special state */
    11558         MachineState_T oldState = mData->mMachineState;
    11559         setMachineState(MachineState_SettingUp);
    11560 
    11561         writeLock.release();
    11562 
    11563         HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
    11564                                               true /*aWait*/);
    11565 
    11566         writeLock.acquire();
    11567 
    11568         setMachineState(oldState);
    11569 
    11570         if (FAILED(rc)) return rc;
    11571     }
    11572 
    11573     setModified(IsModified_Storage);
    11574     mMediaData.backup();
    11575     mMediaData->mAttachments.remove(pAttach);
    11576 
    11577     if (!oldmedium.isNull())
    11578     {
    11579         // if this is from a snapshot, do not defer detachment to commitMedia()
    11580         if (pSnapshot)
    11581             oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
    11582         // else if non-hard disk media, do not defer detachment to commitMedia() either
    11583         else if (mediumType != DeviceType_HardDisk)
    11584             oldmedium->removeBackReference(mData->mUuid);
    11585     }
    11586 
    11587     return S_OK;
    11588 }
    11589 
    11590 /**
    11591  * Goes thru all media of the given list and
    11592  *
    11593  * 1) calls detachDevice() on each of them for this machine and
    11594  * 2) adds all Medium objects found in the process to the given list,
    11595  *    depending on cleanupMode.
    11596  *
    11597  * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
    11598  * adds hard disks to the list. If it is CleanupMode_Full, this adds all
    11599  * media to the list.
    11600  *
    11601  * This gets called from Machine::Unregister, both for the actual Machine and
    11602  * the SnapshotMachine objects that might be found in the snapshots.
    11603  *
    11604  * Requires caller and locking. The machine lock must be passed in because it
    11605  * will be passed on to detachDevice which needs it for temporary unlocking.
    11606  *
    11607  * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
    11608  * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
    11609  * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
    11610  *          otherwise no media get added.
    11611  * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
    11612  * @return
    11613  */
    11614 HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
    11615                                 Snapshot *pSnapshot,
    11616                                 CleanupMode_T cleanupMode,
    11617                                 MediaList &llMedia)
    11618 {
    11619     Assert(isWriteLockOnCurrentThread());
    11620 
    11621     HRESULT rc;
    11622 
    11623     // make a temporary list because detachDevice invalidates iterators into
    11624     // mMediaData->mAttachments
    11625     MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
    11626 
    11627     for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
    11628          it != llAttachments2.end();
    11629          ++it)
    11630     {
    11631         ComObjPtr<MediumAttachment> &pAttach = *it;
    11632         ComObjPtr<Medium> pMedium = pAttach->getMedium();
    11633 
    11634         if (!pMedium.isNull())
    11635         {
    11636             AutoCaller mac(pMedium);
    11637             if (FAILED(mac.rc())) return mac.rc();
    11638             AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
    11639             DeviceType_T devType = pMedium->getDeviceType();
    11640             if (    (    cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
    11641                       && devType == DeviceType_HardDisk)
    11642                  || (cleanupMode == CleanupMode_Full)
    11643                )
    11644             {
    11645                 llMedia.push_back(pMedium);
    11646                 ComObjPtr<Medium> pParent = pMedium->getParent();
    11647                 /*
    11648                  * Search for medias which are not attached to any machine, but
    11649                  * in the chain to an attached disk. Mediums are only consided
    11650                  * if they are:
    11651                  * - have only one child
    11652                  * - no references to any machines
    11653                  * - are of normal medium type
    11654                  */
    11655                 while (!pParent.isNull())
    11656                 {
    11657                     AutoCaller mac1(pParent);
    11658                     if (FAILED(mac1.rc())) return mac1.rc();
    11659                     AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
    11660                     if (pParent->getChildren().size() == 1)
    11661                     {
    11662                         if (   pParent->getMachineBackRefCount() == 0
    11663                             && pParent->getType() == MediumType_Normal
    11664                             && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
    11665                             llMedia.push_back(pParent);
    11666                     }
    11667                     else
    11668                         break;
    11669                     pParent = pParent->getParent();
    11670                 }
    11671             }
    11672         }
    11673 
    11674         // real machine: then we need to use the proper method
    11675         rc = detachDevice(pAttach, writeLock, pSnapshot);
    11676 
    11677         if (FAILED(rc))
    11678             return rc;
    11679     }
    11680 
    11681     return S_OK;
    11682 }
    11683 
    11684 /**
    11685  * Perform deferred hard disk detachments.
    11686  *
    11687  * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
    11688  * backed up).
    11689  *
    11690  * If @a aOnline is @c true then this method will also unlock the old hard disks
    11691  * for which the new implicit diffs were created and will lock these new diffs for
    11692  * writing.
    11693  *
    11694  * @param aOnline       Whether the VM was online prior to this operation.
    11695  *
    11696  * @note Locks this object for writing!
    11697  */
    11698 void Machine::commitMedia(bool aOnline /*= false*/)
    11699 {
    11700     AutoCaller autoCaller(this);
    11701     AssertComRCReturnVoid(autoCaller.rc());
    11702 
    11703     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    11704 
    11705     LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
    11706 
    11707     HRESULT rc = S_OK;
    11708 
    11709     /* no attach/detach operations -- nothing to do */
    11710     if (!mMediaData.isBackedUp())
    11711         return;
    11712 
    11713     MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
    11714     bool fMediaNeedsLocking = false;
    11715 
    11716     /* enumerate new attachments */
    11717     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11718          it != mMediaData->mAttachments.end();
    11719          ++it)
    11720     {
    11721         MediumAttachment *pAttach = *it;
    11722 
    11723         pAttach->commit();
    11724 
    11725         Medium* pMedium = pAttach->getMedium();
    11726         bool fImplicit = pAttach->isImplicit();
    11727 
    11728         LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
    11729                          (pMedium) ? pMedium->getName().c_str() : "NULL",
    11730                          fImplicit));
    11731 
    11732         /** @todo convert all this Machine-based voodoo to MediumAttachment
    11733          * based commit logic. */
    11734         if (fImplicit)
    11735         {
    11736             /* convert implicit attachment to normal */
    11737             pAttach->setImplicit(false);
    11738 
    11739             if (    aOnline
    11740                  && pMedium
    11741                  && pAttach->getType() == DeviceType_HardDisk
    11742                )
    11743             {
    11744                 ComObjPtr<Medium> parent = pMedium->getParent();
    11745                 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
    11746 
    11747                 /* update the appropriate lock list */
    11748                 MediumLockList *pMediumLockList;
    11749                 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
    11750                 AssertComRC(rc);
    11751                 if (pMediumLockList)
    11752                 {
    11753                     /* unlock if there's a need to change the locking */
    11754                     if (!fMediaNeedsLocking)
    11755                     {
    11756                         rc = mData->mSession.mLockedMedia.Unlock();
    11757                         AssertComRC(rc);
    11758                         fMediaNeedsLocking = true;
    11759                     }
    11760                     rc = pMediumLockList->Update(parent, false);
    11761                     AssertComRC(rc);
    11762                     rc = pMediumLockList->Append(pMedium, true);
    11763                     AssertComRC(rc);
    11764                 }
    11765             }
    11766 
    11767             continue;
    11768         }
    11769 
    11770         if (pMedium)
    11771         {
    11772             /* was this medium attached before? */
    11773             for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
    11774                  oldIt != oldAtts.end();
    11775                  ++oldIt)
    11776             {
    11777                 MediumAttachment *pOldAttach = *oldIt;
    11778                 if (pOldAttach->getMedium() == pMedium)
    11779                 {
    11780                     LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
    11781 
    11782                     /* yes: remove from old to avoid de-association */
    11783                     oldAtts.erase(oldIt);
    11784                     break;
    11785                 }
    11786             }
    11787         }
    11788     }
    11789 
    11790     /* enumerate remaining old attachments and de-associate from the
    11791      * current machine state */
    11792     for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
    11793          it != oldAtts.end();
    11794          ++it)
    11795     {
    11796         MediumAttachment *pAttach = *it;
    11797         Medium* pMedium = pAttach->getMedium();
    11798 
    11799         /* Detach only hard disks, since DVD/floppy media is detached
    11800          * instantly in MountMedium. */
    11801         if (pAttach->getType() == DeviceType_HardDisk && pMedium)
    11802         {
    11803             LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
    11804 
    11805             /* now de-associate from the current machine state */
    11806             rc = pMedium->removeBackReference(mData->mUuid);
    11807             AssertComRC(rc);
    11808 
    11809             if (aOnline)
    11810             {
    11811                 /* unlock since medium is not used anymore */
    11812                 MediumLockList *pMediumLockList;
    11813                 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
    11814                 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
    11815                 {
    11816                     /* this happens for online snapshots, there the attachment
    11817                      * is changing, but only to a diff image created under
    11818                      * the old one, so there is no separate lock list */
    11819                     Assert(!pMediumLockList);
    11820                 }
    11821                 else
    11822                 {
    11823                     AssertComRC(rc);
    11824                     if (pMediumLockList)
    11825                     {
    11826                         rc = mData->mSession.mLockedMedia.Remove(pAttach);
    11827                         AssertComRC(rc);
    11828                     }
    11829                 }
    11830             }
    11831         }
    11832     }
    11833 
    11834     /* take media locks again so that the locking state is consistent */
    11835     if (fMediaNeedsLocking)
    11836     {
    11837         Assert(aOnline);
    11838         rc = mData->mSession.mLockedMedia.Lock();
    11839         AssertComRC(rc);
    11840     }
    11841 
    11842     /* commit the hard disk changes */
    11843     mMediaData.commit();
    11844 
    11845     if (isSessionMachine())
    11846     {
    11847         /*
    11848          * Update the parent machine to point to the new owner.
    11849          * This is necessary because the stored parent will point to the
    11850          * session machine otherwise and cause crashes or errors later
    11851          * when the session machine gets invalid.
    11852          */
    11853         /** @todo Change the MediumAttachment class to behave like any other
    11854          *        class in this regard by creating peer MediumAttachment
    11855          *        objects for session machines and share the data with the peer
    11856          *        machine.
    11857          */
    11858         for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11859              it != mMediaData->mAttachments.end();
    11860              ++it)
    11861         {
    11862             (*it)->updateParentMachine(mPeer);
    11863         }
    11864 
    11865         /* attach new data to the primary machine and reshare it */
    11866         mPeer->mMediaData.attach(mMediaData);
    11867     }
    11868 
    11869     return;
    11870 }
    11871 
    11872 /**
    11873  * Perform deferred deletion of implicitly created diffs.
    11874  *
    11875  * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
    11876  * backed up).
    11877  *
    11878  * @note Locks this object for writing!
    11879  */
    11880 void Machine::rollbackMedia()
    11881 {
    11882     AutoCaller autoCaller(this);
    11883     AssertComRCReturnVoid(autoCaller.rc());
    11884 
    11885     // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    11886     LogFlowThisFunc(("Entering rollbackMedia\n"));
    11887 
    11888     HRESULT rc = S_OK;
    11889 
    11890     /* no attach/detach operations -- nothing to do */
    11891     if (!mMediaData.isBackedUp())
    11892         return;
    11893 
    11894     /* enumerate new attachments */
    11895     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    11896          it != mMediaData->mAttachments.end();
    11897          ++it)
    11898     {
    11899         MediumAttachment *pAttach = *it;
    11900         /* Fix up the backrefs for DVD/floppy media. */
    11901         if (pAttach->getType() != DeviceType_HardDisk)
    11902         {
    11903             Medium* pMedium = pAttach->getMedium();
    11904             if (pMedium)
    11905             {
    11906                 rc = pMedium->removeBackReference(mData->mUuid);
    11907                 AssertComRC(rc);
    11908             }
    11909         }
    11910 
    11911         (*it)->rollback();
    11912 
    11913         pAttach = *it;
    11914         /* Fix up the backrefs for DVD/floppy media. */
    11915         if (pAttach->getType() != DeviceType_HardDisk)
    11916         {
    11917             Medium* pMedium = pAttach->getMedium();
    11918             if (pMedium)
    11919             {
    11920                 rc = pMedium->addBackReference(mData->mUuid);
    11921                 AssertComRC(rc);
    11922             }
    11923         }
    11924     }
    11925 
    11926     /** @todo convert all this Machine-based voodoo to MediumAttachment
    11927      * based rollback logic. */
    11928     deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
    11929 
    11930     return;
    11931 }
    11932 
    11933 /**
    11934  *  Returns true if the settings file is located in the directory named exactly
    11935  *  as the machine; this means, among other things, that the machine directory
    11936  *  should be auto-renamed.
    11937  *
    11938  *  @param aSettingsDir if not NULL, the full machine settings file directory
    11939  *                      name will be assigned there.
    11940  *
    11941  *  @note Doesn't lock anything.
    11942  *  @note Not thread safe (must be called from this object's lock).
    11943  */
    11944 bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
    11945 {
    11946     Utf8Str strMachineDirName(mData->m_strConfigFileFull);  // path/to/machinesfolder/vmname/vmname.vbox
    11947     strMachineDirName.stripFilename();                      // path/to/machinesfolder/vmname
    11948     if (aSettingsDir)
    11949         *aSettingsDir = strMachineDirName;
    11950     strMachineDirName.stripPath();                          // vmname
    11951     Utf8Str strConfigFileOnly(mData->m_strConfigFileFull);  // path/to/machinesfolder/vmname/vmname.vbox
    11952     strConfigFileOnly.stripPath()                           // vmname.vbox
    11953                      .stripExt();                           // vmname
    11954     /** @todo hack, make somehow use of ComposeMachineFilename */
    11955     if (mUserData->s.fDirectoryIncludesUUID)
    11956         strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
    11957 
    11958     AssertReturn(!strMachineDirName.isEmpty(), false);
    11959     AssertReturn(!strConfigFileOnly.isEmpty(), false);
    11960 
    11961     return strMachineDirName == strConfigFileOnly;
    11962 }
    11963 
    11964 /**
    11965  * Discards all changes to machine settings.
    11966  *
    11967  * @param aNotify   Whether to notify the direct session about changes or not.
    11968  *
    11969  * @note Locks objects for writing!
    11970  */
    11971 void Machine::rollback(bool aNotify)
    11972 {
    11973     AutoCaller autoCaller(this);
    11974     AssertComRCReturn(autoCaller.rc(), (void)0);
    11975 
    11976     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    11977 
    11978     if (!mStorageControllers.isNull())
    11979     {
    11980         if (mStorageControllers.isBackedUp())
    11981         {
    11982             /* unitialize all new devices (absent in the backed up list). */
    11983             StorageControllerList::const_iterator it = mStorageControllers->begin();
    11984             StorageControllerList *backedList = mStorageControllers.backedUpData();
    11985             while (it != mStorageControllers->end())
    11986             {
    11987                 if (   std::find(backedList->begin(), backedList->end(), *it)
    11988                     == backedList->end()
    11989                    )
    11990                 {
    11991                     (*it)->uninit();
    11992                 }
    11993                 ++it;
    11994             }
    11995 
    11996             /* restore the list */
    11997             mStorageControllers.rollback();
    11998         }
    11999 
    12000         /* rollback any changes to devices after restoring the list */
    12001         if (mData->flModifications & IsModified_Storage)
    12002         {
    12003             StorageControllerList::const_iterator it = mStorageControllers->begin();
    12004             while (it != mStorageControllers->end())
    12005             {
    12006                 (*it)->rollback();
    12007                 ++it;
    12008             }
    12009         }
    12010     }
    12011 
    12012     if (!mUSBControllers.isNull())
    12013     {
    12014         if (mUSBControllers.isBackedUp())
    12015         {
    12016             /* unitialize all new devices (absent in the backed up list). */
    12017             USBControllerList::const_iterator it = mUSBControllers->begin();
    12018             USBControllerList *backedList = mUSBControllers.backedUpData();
    12019             while (it != mUSBControllers->end())
    12020             {
    12021                 if (   std::find(backedList->begin(), backedList->end(), *it)
    12022                     == backedList->end()
    12023                    )
    12024                 {
    12025                     (*it)->uninit();
    12026                 }
    12027                 ++it;
    12028             }
    12029 
    12030             /* restore the list */
    12031             mUSBControllers.rollback();
    12032         }
    12033 
    12034         /* rollback any changes to devices after restoring the list */
    12035         if (mData->flModifications & IsModified_USB)
    12036         {
    12037             USBControllerList::const_iterator it = mUSBControllers->begin();
    12038             while (it != mUSBControllers->end())
    12039             {
    12040                 (*it)->rollback();
    12041                 ++it;
    12042             }
    12043         }
    12044     }
    12045 
    12046     mUserData.rollback();
    12047 
    12048     mHWData.rollback();
    12049 
    12050     if (mData->flModifications & IsModified_Storage)
    12051         rollbackMedia();
    12052 
    12053     if (mBIOSSettings)
    12054         mBIOSSettings->rollback();
    12055 
    12056     if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
    12057         mVRDEServer->rollback();
    12058 
    12059     if (mAudioAdapter)
    12060         mAudioAdapter->rollback();
    12061 
    12062     if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
    12063         mUSBDeviceFilters->rollback();
    12064 
    12065     if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
    12066         mBandwidthControl->rollback();
    12067 
    12068     if (!mHWData.isNull())
    12069         mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
    12070     NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
    12071     ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
    12072     ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
    12073 
    12074     if (mData->flModifications & IsModified_NetworkAdapters)
    12075         for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
    12076             if (    mNetworkAdapters[slot]
    12077                  && mNetworkAdapters[slot]->isModified())
    12078             {
    12079                 mNetworkAdapters[slot]->rollback();
    12080                 networkAdapters[slot] = mNetworkAdapters[slot];
    12081             }
    12082 
    12083     if (mData->flModifications & IsModified_SerialPorts)
    12084         for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    12085             if (    mSerialPorts[slot]
    12086                  && mSerialPorts[slot]->isModified())
    12087             {
    12088                 mSerialPorts[slot]->rollback();
    12089                 serialPorts[slot] = mSerialPorts[slot];
    12090             }
    12091 
    12092     if (mData->flModifications & IsModified_ParallelPorts)
    12093         for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    12094             if (    mParallelPorts[slot]
    12095                  && mParallelPorts[slot]->isModified())
    12096             {
    12097                 mParallelPorts[slot]->rollback();
    12098                 parallelPorts[slot] = mParallelPorts[slot];
    12099             }
    12100 
    12101     if (aNotify)
    12102     {
    12103         /* inform the direct session about changes */
    12104 
    12105         ComObjPtr<Machine> that = this;
    12106         uint32_t flModifications = mData->flModifications;
    12107         alock.release();
    12108 
    12109         if (flModifications & IsModified_SharedFolders)
    12110             that->onSharedFolderChange();
    12111 
    12112         if (flModifications & IsModified_VRDEServer)
    12113             that->onVRDEServerChange(/* aRestart */ TRUE);
    12114         if (flModifications & IsModified_USB)
    12115             that->onUSBControllerChange();
    12116 
    12117         for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
    12118             if (networkAdapters[slot])
    12119                 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
    12120         for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
    12121             if (serialPorts[slot])
    12122                 that->onSerialPortChange(serialPorts[slot]);
    12123         for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
    12124             if (parallelPorts[slot])
    12125                 that->onParallelPortChange(parallelPorts[slot]);
    12126 
    12127         if (flModifications & IsModified_Storage)
    12128             that->onStorageControllerChange();
    12129 
    12130 #if 0
    12131         if (flModifications & IsModified_BandwidthControl)
    12132             that->onBandwidthControlChange();
    12133 #endif
    12134     }
    12135 }
    12136 
    12137 /**
    12138  * Commits all the changes to machine settings.
    12139  *
    12140  * Note that this operation is supposed to never fail.
    12141  *
    12142  * @note Locks this object and children for writing.
    12143  */
    12144 void Machine::commit()
    12145 {
    12146     AutoCaller autoCaller(this);
    12147     AssertComRCReturnVoid(autoCaller.rc());
    12148 
    12149     AutoCaller peerCaller(mPeer);
    12150     AssertComRCReturnVoid(peerCaller.rc());
    12151 
    12152     AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
    12153 
    12154     /*
    12155      *  use safe commit to ensure Snapshot machines (that share mUserData)
    12156      *  will still refer to a valid memory location
    12157      */
    12158     mUserData.commitCopy();
    12159 
    12160     mHWData.commit();
    12161 
    12162     if (mMediaData.isBackedUp())
    12163         commitMedia(Global::IsOnline(mData->mMachineState));
    12164 
    12165     mBIOSSettings->commit();
    12166     mVRDEServer->commit();
    12167     mAudioAdapter->commit();
    12168     mUSBDeviceFilters->commit();
    12169     mBandwidthControl->commit();
    12170 
    12171     /* Since mNetworkAdapters is a list which might have been changed (resized)
    12172      * without using the Backupable<> template we need to handle the copying
    12173      * of the list entries manually, including the creation of peers for the
    12174      * new objects. */
    12175     bool commitNetworkAdapters = false;
    12176     size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
    12177     if (mPeer)
    12178     {
    12179         /* commit everything, even the ones which will go away */
    12180         for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
    12181             mNetworkAdapters[slot]->commit();
    12182         /* copy over the new entries, creating a peer and uninit the original */
    12183         mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
    12184         for (size_t slot = 0; slot < newSize; slot++)
    12185         {
    12186             /* look if this adapter has a peer device */
    12187             ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
    12188             if (!peer)
    12189             {
    12190                 /* no peer means the adapter is a newly created one;
    12191                  * create a peer owning data this data share it with */
    12192                 peer.createObject();
    12193                 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
    12194             }
    12195             mPeer->mNetworkAdapters[slot] = peer;
    12196         }
    12197         /* uninit any no longer needed network adapters */
    12198         for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
    12199             mNetworkAdapters[slot]->uninit();
    12200         for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
    12201         {
    12202             if (mPeer->mNetworkAdapters[slot])
    12203                 mPeer->mNetworkAdapters[slot]->uninit();
    12204         }
    12205         /* Keep the original network adapter count until this point, so that
    12206          * discarding a chipset type change will not lose settings. */
    12207         mNetworkAdapters.resize(newSize);
    12208         mPeer->mNetworkAdapters.resize(newSize);
    12209     }
    12210     else
    12211     {
    12212         /* we have no peer (our parent is the newly created machine);
    12213          * just commit changes to the network adapters */
    12214         commitNetworkAdapters = true;
    12215     }
    12216     if (commitNetworkAdapters)
    12217     {
    12218         for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
    12219             mNetworkAdapters[slot]->commit();
    12220     }
    12221 
    12222     for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    12223         mSerialPorts[slot]->commit();
    12224     for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    12225         mParallelPorts[slot]->commit();
    12226 
    12227     bool commitStorageControllers = false;
    12228 
    12229     if (mStorageControllers.isBackedUp())
    12230     {
    12231         mStorageControllers.commit();
    12232 
    12233         if (mPeer)
    12234         {
    12235             /* Commit all changes to new controllers (this will reshare data with
    12236              * peers for those who have peers) */
    12237             StorageControllerList *newList = new StorageControllerList();
    12238             StorageControllerList::const_iterator it = mStorageControllers->begin();
    12239             while (it != mStorageControllers->end())
    12240             {
    12241                 (*it)->commit();
    12242 
    12243                 /* look if this controller has a peer device */
    12244                 ComObjPtr<StorageController> peer = (*it)->getPeer();
    12245                 if (!peer)
    12246                 {
    12247                     /* no peer means the device is a newly created one;
    12248                      * create a peer owning data this device share it with */
    12249                     peer.createObject();
    12250                     peer->init(mPeer, *it, true /* aReshare */);
    12251                 }
    12252                 else
    12253                 {
    12254                     /* remove peer from the old list */
    12255                     mPeer->mStorageControllers->remove(peer);
    12256                 }
    12257                 /* and add it to the new list */
    12258                 newList->push_back(peer);
    12259 
    12260                 ++it;
    12261             }
    12262 
    12263             /* uninit old peer's controllers that are left */
    12264             it = mPeer->mStorageControllers->begin();
    12265             while (it != mPeer->mStorageControllers->end())
    12266             {
    12267                 (*it)->uninit();
    12268                 ++it;
    12269             }
    12270 
    12271             /* attach new list of controllers to our peer */
    12272             mPeer->mStorageControllers.attach(newList);
    12273         }
    12274         else
    12275         {
    12276             /* we have no peer (our parent is the newly created machine);
    12277              * just commit changes to devices */
    12278             commitStorageControllers = true;
    12279         }
    12280     }
    12281     else
    12282     {
    12283         /* the list of controllers itself is not changed,
    12284          * just commit changes to controllers themselves */
    12285         commitStorageControllers = true;
    12286     }
    12287 
    12288     if (commitStorageControllers)
    12289     {
    12290         StorageControllerList::const_iterator it = mStorageControllers->begin();
    12291         while (it != mStorageControllers->end())
    12292         {
    12293             (*it)->commit();
    12294             ++it;
    12295         }
    12296     }
    12297 
    12298     bool commitUSBControllers = false;
    12299 
    12300     if (mUSBControllers.isBackedUp())
    12301     {
    12302         mUSBControllers.commit();
    12303 
    12304         if (mPeer)
    12305         {
    12306             /* Commit all changes to new controllers (this will reshare data with
    12307              * peers for those who have peers) */
    12308             USBControllerList *newList = new USBControllerList();
    12309             USBControllerList::const_iterator it = mUSBControllers->begin();
    12310             while (it != mUSBControllers->end())
    12311             {
    12312                 (*it)->commit();
    12313 
    12314                 /* look if this controller has a peer device */
    12315                 ComObjPtr<USBController> peer = (*it)->getPeer();
    12316                 if (!peer)
    12317                 {
    12318                     /* no peer means the device is a newly created one;
    12319                      * create a peer owning data this device share it with */
    12320                     peer.createObject();
    12321                     peer->init(mPeer, *it, true /* aReshare */);
    12322                 }
    12323                 else
    12324                 {
    12325                     /* remove peer from the old list */
    12326                     mPeer->mUSBControllers->remove(peer);
    12327                 }
    12328                 /* and add it to the new list */
    12329                 newList->push_back(peer);
    12330 
    12331                 ++it;
    12332             }
    12333 
    12334             /* uninit old peer's controllers that are left */
    12335             it = mPeer->mUSBControllers->begin();
    12336             while (it != mPeer->mUSBControllers->end())
    12337             {
    12338                 (*it)->uninit();
    12339                 ++it;
    12340             }
    12341 
    12342             /* attach new list of controllers to our peer */
    12343             mPeer->mUSBControllers.attach(newList);
    12344         }
    12345         else
    12346         {
    12347             /* we have no peer (our parent is the newly created machine);
    12348              * just commit changes to devices */
    12349             commitUSBControllers = true;
    12350         }
    12351     }
    12352     else
    12353     {
    12354         /* the list of controllers itself is not changed,
    12355          * just commit changes to controllers themselves */
    12356         commitUSBControllers = true;
    12357     }
    12358 
    12359     if (commitUSBControllers)
    12360     {
    12361         USBControllerList::const_iterator it = mUSBControllers->begin();
    12362         while (it != mUSBControllers->end())
    12363         {
    12364             (*it)->commit();
    12365             ++it;
    12366         }
    12367     }
    12368 
    12369     if (isSessionMachine())
    12370     {
    12371         /* attach new data to the primary machine and reshare it */
    12372         mPeer->mUserData.attach(mUserData);
    12373         mPeer->mHWData.attach(mHWData);
    12374         /* mMediaData is reshared by fixupMedia */
    12375         // mPeer->mMediaData.attach(mMediaData);
    12376         Assert(mPeer->mMediaData.data() == mMediaData.data());
    12377     }
    12378 }
    12379 
    12380 /**
    12381  * Copies all the hardware data from the given machine.
    12382  *
    12383  * Currently, only called when the VM is being restored from a snapshot. In
    12384  * particular, this implies that the VM is not running during this method's
    12385  * call.
    12386  *
    12387  * @note This method must be called from under this object's lock.
    12388  *
    12389  * @note This method doesn't call #commit(), so all data remains backed up and
    12390  *       unsaved.
    12391  */
    12392 void Machine::copyFrom(Machine *aThat)
    12393 {
    12394     AssertReturnVoid(!isSnapshotMachine());
    12395     AssertReturnVoid(aThat->isSnapshotMachine());
    12396 
    12397     AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
    12398 
    12399     mHWData.assignCopy(aThat->mHWData);
    12400 
    12401     // create copies of all shared folders (mHWData after attaching a copy
    12402     // contains just references to original objects)
    12403     for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
    12404          it != mHWData->mSharedFolders.end();
    12405          ++it)
    12406     {
    12407         ComObjPtr<SharedFolder> folder;
    12408         folder.createObject();
    12409         HRESULT rc = folder->initCopy(getMachine(), *it);
    12410         AssertComRC(rc);
    12411         *it = folder;
    12412     }
    12413 
    12414     mBIOSSettings->copyFrom(aThat->mBIOSSettings);
    12415     mVRDEServer->copyFrom(aThat->mVRDEServer);
    12416     mAudioAdapter->copyFrom(aThat->mAudioAdapter);
    12417     mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
    12418     mBandwidthControl->copyFrom(aThat->mBandwidthControl);
    12419 
    12420     /* create private copies of all controllers */
    12421     mStorageControllers.backup();
    12422     mStorageControllers->clear();
    12423     for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
    12424          it != aThat->mStorageControllers->end();
    12425          ++it)
    12426     {
    12427         ComObjPtr<StorageController> ctrl;
    12428         ctrl.createObject();
    12429         ctrl->initCopy(this, *it);
    12430         mStorageControllers->push_back(ctrl);
    12431     }
    12432 
    12433     /* create private copies of all USB controllers */
    12434     mUSBControllers.backup();
    12435     mUSBControllers->clear();
    12436     for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
    12437          it != aThat->mUSBControllers->end();
    12438          ++it)
    12439     {
    12440         ComObjPtr<USBController> ctrl;
    12441         ctrl.createObject();
    12442         ctrl->initCopy(this, *it);
    12443         mUSBControllers->push_back(ctrl);
    12444     }
    12445 
    12446     mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
    12447     for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
    12448         mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
    12449     for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    12450         mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
    12451     for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    12452         mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
    12453 }
    12454 
    12455 /**
    12456  * Returns whether the given storage controller is hotplug capable.
    12457  *
    12458  * @returns true if the controller supports hotplugging
    12459  *          false otherwise.
    12460  * @param   enmCtrlType    The controller type to check for.
    12461  */
    12462 bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
    12463 {
    12464     switch (enmCtrlType)
    12465     {
    12466         case StorageControllerType_IntelAhci:
    12467             return true;
    12468         case StorageControllerType_LsiLogic:
    12469         case StorageControllerType_LsiLogicSas:
    12470         case StorageControllerType_BusLogic:
    12471         case StorageControllerType_PIIX3:
    12472         case StorageControllerType_PIIX4:
    12473         case StorageControllerType_ICH6:
    12474         case StorageControllerType_I82078:
    12475         default:
    12476             return false;
    12477     }
    12478 }
    12479 
    12480 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    12481 
    12482 void Machine::getDiskList(MediaList &list)
    12483 {
    12484     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    12485          it != mMediaData->mAttachments.end();
    12486          ++it)
    12487     {
    12488         MediumAttachment* pAttach = *it;
    12489         /* just in case */
    12490         AssertStmt(pAttach, continue);
    12491 
    12492         AutoCaller localAutoCallerA(pAttach);
    12493         if (FAILED(localAutoCallerA.rc())) continue;
    12494 
    12495         AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
    12496 
    12497         if (pAttach->getType() == DeviceType_HardDisk)
    12498             list.push_back(pAttach->getMedium());
    12499     }
    12500 }
    12501 
    12502 void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
    12503 {
    12504     AssertReturnVoid(isWriteLockOnCurrentThread());
    12505     AssertPtrReturnVoid(aCollector);
    12506 
    12507     pm::CollectorHAL *hal = aCollector->getHAL();
    12508     /* Create sub metrics */
    12509     pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
    12510         "Percentage of processor time spent in user mode by the VM process.");
    12511     pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
    12512         "Percentage of processor time spent in kernel mode by the VM process.");
    12513     pm::SubMetric *ramUsageUsed  = new pm::SubMetric("RAM/Usage/Used",
    12514         "Size of resident portion of VM process in memory.");
    12515     pm::SubMetric *diskUsageUsed  = new pm::SubMetric("Disk/Usage/Used",
    12516         "Actual size of all VM disks combined.");
    12517     pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
    12518         "Network receive rate.");
    12519     pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
    12520         "Network transmit rate.");
    12521     /* Create and register base metrics */
    12522     pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
    12523                                                         cpuLoadUser, cpuLoadKernel);
    12524     aCollector->registerBaseMetric(cpuLoad);
    12525     pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
    12526                                                        ramUsageUsed);
    12527     aCollector->registerBaseMetric(ramUsage);
    12528     MediaList disks;
    12529     getDiskList(disks);
    12530     pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
    12531                                                          diskUsageUsed);
    12532     aCollector->registerBaseMetric(diskUsage);
    12533 
    12534     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
    12535     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
    12536                                                 new pm::AggregateAvg()));
    12537     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
    12538                                               new pm::AggregateMin()));
    12539     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
    12540                                               new pm::AggregateMax()));
    12541     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
    12542     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
    12543                                               new pm::AggregateAvg()));
    12544     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
    12545                                               new pm::AggregateMin()));
    12546     aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
    12547                                               new pm::AggregateMax()));
    12548 
    12549     aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
    12550     aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
    12551                                               new pm::AggregateAvg()));
    12552     aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
    12553                                               new pm::AggregateMin()));
    12554     aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
    12555                                               new pm::AggregateMax()));
    12556 
    12557     aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
    12558     aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
    12559                                               new pm::AggregateAvg()));
    12560     aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
    12561                                               new pm::AggregateMin()));
    12562     aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
    12563                                               new pm::AggregateMax()));
    12564 
    12565 
    12566     /* Guest metrics collector */
    12567     mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
    12568     aCollector->registerGuest(mCollectorGuest);
    12569     LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
    12570                 this, __PRETTY_FUNCTION__, mCollectorGuest));
    12571 
    12572     /* Create sub metrics */
    12573     pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
    12574         "Percentage of processor time spent in user mode as seen by the guest.");
    12575     pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
    12576         "Percentage of processor time spent in kernel mode as seen by the guest.");
    12577     pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
    12578         "Percentage of processor time spent idling as seen by the guest.");
    12579 
    12580     /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
    12581     pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total",      "Total amount of physical guest RAM.");
    12582     pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free",        "Free amount of physical guest RAM.");
    12583     pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon",  "Amount of ballooned physical guest RAM.");
    12584     pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared",  "Amount of shared physical guest RAM.");
    12585     pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache",        "Total amount of guest (disk) cache memory.");
    12586 
    12587     pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total",    "Total amount of space in the page file.");
    12588 
    12589     /* Create and register base metrics */
    12590     pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
    12591                                                             machineNetRx, machineNetTx);
    12592     aCollector->registerBaseMetric(machineNetRate);
    12593 
    12594     pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
    12595                                                         guestLoadUser, guestLoadKernel, guestLoadIdle);
    12596     aCollector->registerBaseMetric(guestCpuLoad);
    12597 
    12598     pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
    12599                                                         guestMemTotal, guestMemFree,
    12600                                                         guestMemBalloon, guestMemShared,
    12601                                                         guestMemCache, guestPagedTotal);
    12602     aCollector->registerBaseMetric(guestCpuMem);
    12603 
    12604     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
    12605     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
    12606     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
    12607     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
    12608 
    12609     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
    12610     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
    12611     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
    12612     aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
    12613 
    12614     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
    12615     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
    12616     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
    12617     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
    12618 
    12619     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
    12620     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
    12621     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
    12622     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
    12623 
    12624     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
    12625     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
    12626     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
    12627     aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
    12628 
    12629     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
    12630     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
    12631     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
    12632     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
    12633 
    12634     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
    12635     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
    12636     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
    12637     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
    12638 
    12639     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
    12640     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
    12641     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
    12642     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
    12643 
    12644     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
    12645     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
    12646     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
    12647     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
    12648 
    12649     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
    12650     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
    12651     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
    12652     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
    12653 
    12654     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
    12655     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
    12656     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
    12657     aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
    12658 }
    12659 
    12660 void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
    12661 {
    12662     AssertReturnVoid(isWriteLockOnCurrentThread());
    12663 
    12664     if (aCollector)
    12665     {
    12666         aCollector->unregisterMetricsFor(aMachine);
    12667         aCollector->unregisterBaseMetricsFor(aMachine);
    12668     }
    12669 }
    12670 
    12671 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    12672 
    12673 
    12674 ////////////////////////////////////////////////////////////////////////////////
    12675 
    12676 DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
    12677 
    12678 HRESULT SessionMachine::FinalConstruct()
    12679 {
    12680     LogFlowThisFunc(("\n"));
    12681 
     52    if (mClientToken != NULLHANDLE)
     53        ::DosCloseMutexSem(mClientToken);
     54#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
     55    if (mClientToken >= 0)
     56        ::semctl(mClientToken, 0, IPC_RMID);
     57# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
     58    mClientTokenId = "0";
     59# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
     60#else
     61# error "Port me!"
     62#endif
     63    mClientToken = CTTOKENARG;
     64}
     65
     66Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine) :
     67    mMachine(pMachine)
     68{
    1268269#if defined(RT_OS_WINDOWS)
    12683     mIPCSem = NULL;
    12684 #elif defined(RT_OS_OS2)
    12685     mIPCSem = NULLHANDLE;
    12686 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    12687     mIPCSem = -1;
    12688 #else
    12689 # error "Port me!"
    12690 #endif
    12691 
    12692     return BaseFinalConstruct();
    12693 }
    12694 
    12695 void SessionMachine::FinalRelease()
    12696 {
    12697     LogFlowThisFunc(("\n"));
    12698 
    12699     uninit(Uninit::Unexpected);
    12700 
    12701     BaseFinalRelease();
    12702 }
    12703 
    12704 /**
    12705  *  @note Must be called only by Machine::openSession() from its own write lock.
    12706  */
    12707 HRESULT SessionMachine::init(Machine *aMachine)
    12708 {
    12709     LogFlowThisFuncEnter();
    12710     LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
    12711 
    12712     AssertReturn(aMachine, E_INVALIDARG);
    12713 
    12714     AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    12715 
    12716     /* Enclose the state transition NotReady->InInit->Ready */
    12717     AutoInitSpan autoInitSpan(this);
    12718     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    12719 
    12720     /* create the interprocess semaphore */
    12721 #if defined(RT_OS_WINDOWS)
    12722     mIPCSemName = aMachine->mData->m_strConfigFileFull;
    12723     for (size_t i = 0; i < mIPCSemName.length(); i++)
    12724         if (mIPCSemName.raw()[i] == '\\')
    12725             mIPCSemName.raw()[i] = '/';
    12726     mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
    12727     ComAssertMsgRet(mIPCSem,
    12728                     ("Cannot create IPC mutex '%ls', err=%d",
    12729                      mIPCSemName.raw(), ::GetLastError()),
    12730                     E_FAIL);
     70    Bstr tokenId = pMachine->mData->m_strConfigFileFull;
     71    for (size_t i = 0; i < tokenId.length(); i++)
     72        if (tokenId.raw()[i] == '\\')
     73            tokenId.raw()[i] = '/';
     74    mClientToken = ::CreateMutex(NULL, FALSE, tokenId.raw());
     75    mClientTokenId = tokenId;
     76    AssertMsg(mClientToken,
     77              ("Cannot create token '%s', err=%d",
     78               mClientTokenId.c_str(), ::GetLastError()));
    1273179#elif defined(RT_OS_OS2)
    1273280    Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
    12733                                 aMachine->mData->mUuid.raw());
    12734     mIPCSemName = ipcSem;
    12735     APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
    12736     ComAssertMsgRet(arc == NO_ERROR,
    12737                     ("Cannot create IPC mutex '%s', arc=%ld",
    12738                      ipcSem.c_str(), arc),
    12739                     E_FAIL);
     81                                pMachine->mData->mUuid.raw());
     82    mClientTokenId = ipcSem;
     83    APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE);
     84    AssertMsg(arc == NO_ERROR,
     85              ("Cannot create token '%s', arc=%ld",
     86               ipcSem.c_str(), arc));
    1274087#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    1274188# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
     
    1274794#  endif
    1274895    key_t key;
    12749     mIPCSem = -1;
    12750     mIPCKey = "0";
     96    mClientToken = -1;
     97    mClientTokenId = "0";
    1275198    for (uint32_t i = 0; i < 1 << 24; i++)
    1275299    {
     
    12755102        if (sem >= 0 || (errno != EEXIST && errno != EACCES))
    12756103        {
    12757             mIPCSem = sem;
     104            mClientToken = sem;
    12758105            if (sem >= 0)
    12759                 mIPCKey = BstrFmt("%u", key);
     106                mClientTokenId = BstrFmt("%u", key);
    12760107            break;
    12761108        }
    12762109    }
    12763110# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    12764     Utf8Str semName = aMachine->mData->m_strConfigFileFull;
     111    Utf8Str semName = pMachine->mData->m_strConfigFileFull;
    12765112    char *pszSemName = NULL;
    12766113    RTStrUtf8ToCurrentCP(&pszSemName, semName);
     
    12768115    RTStrFree(pszSemName);
    12769116
    12770     mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
     117    mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
    12771118# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    12772119
    12773120    int errnoSave = errno;
    12774     if (mIPCSem < 0 && errnoSave == ENOSYS)
    12775     {
    12776         setError(E_FAIL,
    12777                  tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
    12778                     "support for SysV IPC. Check the host kernel configuration for "
    12779                     "CONFIG_SYSVIPC=y"));
    12780         return E_FAIL;
     121    if (mClientToken < 0 && errnoSave == ENOSYS)
     122    {
     123        mMachine->setError(E_FAIL,
     124                           tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
     125                              "support for SysV IPC. Check the host kernel configuration for "
     126                              "CONFIG_SYSVIPC=y"));
     127        mClientToken = CTTOKENARG;
     128        return;
    12781129    }
    12782130    /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
    12783      * the IPC semaphores */
    12784     if (mIPCSem < 0 && errnoSave == ENOSPC)
     131     * the token */
     132    if (mClientToken < 0 && errnoSave == ENOSPC)
    12785133    {
    12786134#ifdef RT_OS_LINUX
    12787         setError(E_FAIL,
    12788                  tr("Cannot create IPC semaphore because the system limit for the "
    12789                     "maximum number of semaphore sets (SEMMNI), or the system wide "
    12790                     "maximum number of semaphores (SEMMNS) would be exceeded. The "
    12791                     "current set of SysV IPC semaphores can be determined from "
    12792                     "the file /proc/sysvipc/sem"));
    12793 #else
    12794         setError(E_FAIL,
    12795                  tr("Cannot create IPC semaphore because the system-imposed limit "
    12796                     "on the maximum number of allowed  semaphores or semaphore "
    12797                     "identifiers system-wide would be exceeded"));
    12798 #endif
    12799         return E_FAIL;
    12800     }
    12801     ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
    12802                     E_FAIL);
     135        mMachine->setError(E_FAIL,
     136                           tr("Cannot create IPC semaphore because the system limit for the "
     137                              "maximum number of semaphore sets (SEMMNI), or the system wide "
     138                              "maximum number of semaphores (SEMMNS) would be exceeded. The "
     139                              "current set of SysV IPC semaphores can be determined from "
     140                              "the file /proc/sysvipc/sem"));
     141#else
     142        mMachine->setError(E_FAIL,
     143                           tr("Cannot create IPC semaphore because the system-imposed limit "
     144                              "on the maximum number of allowed  semaphores or semaphore "
     145                              "identifiers system-wide would be exceeded"));
     146#endif
     147        mClientToken = CTTOKENARG;
     148        return;
     149    }
     150    AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave));
    12803151    /* set the initial value to 1 */
    12804     int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
    12805     ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
    12806                     E_FAIL);
     152    int rv = ::semctl(mClientToken, 0, SETVAL, 1);
     153    errnoSave = errno;
     154    if (rv != 0)
     155    {
     156        ::semctl(mClientToken, 0, IPC_RMID);
     157        mClientToken = CTTOKENARG;
     158        AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave));
     159    }
    12807160#else
    12808161# error "Port me!"
    12809162#endif
    12810 
    12811     /* memorize the peer Machine */
    12812     unconst(mPeer) = aMachine;
    12813     /* share the parent pointer */
    12814     unconst(mParent) = aMachine->mParent;
    12815 
    12816     /* take the pointers to data to share */
    12817     mData.share(aMachine->mData);
    12818     mSSData.share(aMachine->mSSData);
    12819 
    12820     mUserData.share(aMachine->mUserData);
    12821     mHWData.share(aMachine->mHWData);
    12822     mMediaData.share(aMachine->mMediaData);
    12823 
    12824     mStorageControllers.allocate();
    12825     for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
    12826          it != aMachine->mStorageControllers->end();
    12827          ++it)
    12828     {
    12829         ComObjPtr<StorageController> ctl;
    12830         ctl.createObject();
    12831         ctl->init(this, *it);
    12832         mStorageControllers->push_back(ctl);
    12833     }
    12834 
    12835     mUSBControllers.allocate();
    12836     for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
    12837          it != aMachine->mUSBControllers->end();
    12838          ++it)
    12839     {
    12840         ComObjPtr<USBController> ctl;
    12841         ctl.createObject();
    12842         ctl->init(this, *it);
    12843         mUSBControllers->push_back(ctl);
    12844     }
    12845 
    12846     unconst(mBIOSSettings).createObject();
    12847     mBIOSSettings->init(this, aMachine->mBIOSSettings);
    12848     /* create another VRDEServer object that will be mutable */
    12849     unconst(mVRDEServer).createObject();
    12850     mVRDEServer->init(this, aMachine->mVRDEServer);
    12851     /* create another audio adapter object that will be mutable */
    12852     unconst(mAudioAdapter).createObject();
    12853     mAudioAdapter->init(this, aMachine->mAudioAdapter);
    12854     /* create a list of serial ports that will be mutable */
    12855     for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
    12856     {
    12857         unconst(mSerialPorts[slot]).createObject();
    12858         mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
    12859     }
    12860     /* create a list of parallel ports that will be mutable */
    12861     for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
    12862     {
    12863         unconst(mParallelPorts[slot]).createObject();
    12864         mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
    12865     }
    12866 
    12867     /* create another USB device filters object that will be mutable */
    12868     unconst(mUSBDeviceFilters).createObject();
    12869     mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
    12870 
    12871     /* create a list of network adapters that will be mutable */
    12872     mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
    12873     for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
    12874     {
    12875         unconst(mNetworkAdapters[slot]).createObject();
    12876         mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
    12877     }
    12878 
    12879     /* create another bandwidth control object that will be mutable */
    12880     unconst(mBandwidthControl).createObject();
    12881     mBandwidthControl->init(this, aMachine->mBandwidthControl);
    12882 
    12883     /* default is to delete saved state on Saved -> PoweredOff transition */
    12884     mRemoveSavedState = true;
    12885 
    12886     /* Confirm a successful initialization when it's the case */
    12887     autoInitSpan.setSucceeded();
    12888 
    12889     LogFlowThisFuncLeave();
    12890     return S_OK;
    12891 }
    12892 
    12893 /**
    12894  *  Uninitializes this session object. If the reason is other than
    12895  *  Uninit::Unexpected, then this method MUST be called from #checkForDeath().
    12896  *
    12897  *  @param aReason          uninitialization reason
    12898  *
    12899  *  @note Locks mParent + this object for writing.
    12900  */
    12901 void SessionMachine::uninit(Uninit::Reason aReason)
    12902 {
    12903     LogFlowThisFuncEnter();
    12904     LogFlowThisFunc(("reason=%d\n", aReason));
    12905 
    12906     /*
    12907      *  Strongly reference ourselves to prevent this object deletion after
    12908      *  mData->mSession.mMachine.setNull() below (which can release the last
    12909      *  reference and call the destructor). Important: this must be done before
    12910      *  accessing any members (and before AutoUninitSpan that does it as well).
    12911      *  This self reference will be released as the very last step on return.
    12912      */
    12913     ComObjPtr<SessionMachine> selfRef = this;
    12914 
    12915     /* Enclose the state transition Ready->InUninit->NotReady */
    12916     AutoUninitSpan autoUninitSpan(this);
    12917     if (autoUninitSpan.uninitDone())
    12918     {
    12919         LogFlowThisFunc(("Already uninitialized\n"));
    12920         LogFlowThisFuncLeave();
    12921         return;
    12922     }
    12923 
    12924     if (autoUninitSpan.initFailed())
    12925     {
    12926         /* We've been called by init() because it's failed. It's not really
    12927          * necessary (nor it's safe) to perform the regular uninit sequence
    12928          * below, the following is enough.
    12929          */
    12930         LogFlowThisFunc(("Initialization failed.\n"));
     163}
     164
     165bool Machine::ClientToken::isReady()
     166{
     167    return mClientToken != CTTOKENARG;
     168}
     169
     170void Machine::ClientToken::getId(Utf8Str &strId)
     171{
     172    strId = mClientTokenId;
     173}
     174
     175CTTOKENTYPE Machine::ClientToken::getToken()
     176{
     177    return mClientToken;
     178}
     179
     180bool Machine::ClientToken::release()
     181{
     182    bool terminated = false;
     183
    12931184#if defined(RT_OS_WINDOWS)
    12932         if (mIPCSem)
    12933             ::CloseHandle(mIPCSem);
    12934         mIPCSem = NULL;
     185    AssertMsg(mClientToken, ("semaphore must be created"));
     186
     187    /* release the token */
     188    ::ReleaseMutex(mClientToken);
     189    terminated = true;
    12935190#elif defined(RT_OS_OS2)
    12936         if (mIPCSem != NULLHANDLE)
    12937             ::DosCloseMutexSem(mIPCSem);
    12938         mIPCSem = NULLHANDLE;
     191    AssertMsg(mClientToken, ("semaphore must be created"));
     192
     193    /* release the token */
     194    ::DosReleaseMutexSem(mClientToken);
     195    terminated = true;
    12939196#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    12940         if (mIPCSem >= 0)
    12941             ::semctl(mIPCSem, 0, IPC_RMID);
    12942         mIPCSem = -1;
    12943 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    12944         mIPCKey = "0";
    12945 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
     197    AssertMsg(mClientToken >= 0, ("semaphore must be created"));
     198    int val = ::semctl(mClientToken, 0, GETVAL);
     199    if (val > 0)
     200    {
     201        /* the semaphore is signaled, meaning the session is terminated */
     202        terminated = true;
     203    }
    12946204#else
    12947205# error "Port me!"
    12948206#endif
    12949         uninitDataAndChildObjects();
    12950         mData.free();
    12951         unconst(mParent) = NULL;
    12952         unconst(mPeer) = NULL;
    12953         LogFlowThisFuncLeave();
    12954         return;
    12955     }
    12956 
    12957     MachineState_T lastState;
    12958     {
    12959         AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
    12960         lastState = mData->mMachineState;
    12961     }
    12962     NOREF(lastState);
    12963 
    12964 #ifdef VBOX_WITH_USB
    12965     // release all captured USB devices, but do this before requesting the locks below
    12966     if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
    12967     {
    12968         /* Console::captureUSBDevices() is called in the VM process only after
    12969          * setting the machine state to Starting or Restoring.
    12970          * Console::detachAllUSBDevices() will be called upon successful
    12971          * termination. So, we need to release USB devices only if there was
    12972          * an abnormal termination of a running VM.
    12973          *
    12974          * This is identical to SessionMachine::DetachAllUSBDevices except
    12975          * for the aAbnormal argument. */
    12976         HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
    12977         AssertComRC(rc);
    12978         NOREF(rc);
    12979 
    12980         USBProxyService *service = mParent->host()->usbProxyService();
    12981         if (service)
    12982             service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
    12983     }
    12984 #endif /* VBOX_WITH_USB */
    12985 
    12986     // we need to lock this object in uninit() because the lock is shared
    12987     // with mPeer (as well as data we modify below). mParent->addProcessToReap()
    12988     // and others need mParent lock, and USB needs host lock.
    12989     AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
    12990 
    12991 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    12992     /*
    12993      * It is safe to call Machine::unregisterMetrics() here because
    12994      * PerformanceCollector::samplerCallback no longer accesses guest methods
    12995      * holding the lock.
    12996      */
    12997     unregisterMetrics(mParent->performanceCollector(), mPeer);
    12998     /* The guest must be unregistered after its metrics (@bugref{5949}). */
    12999     LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
    13000                 this, __PRETTY_FUNCTION__, mCollectorGuest));
    13001     if (mCollectorGuest)
    13002     {
    13003         mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
    13004         // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
    13005         mCollectorGuest = NULL;
    13006     }
    13007 #endif
    13008 
    13009     if (aReason == Uninit::Abnormal)
    13010     {
    13011         LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
    13012                              Global::IsOnlineOrTransient(lastState)));
    13013 
    13014         /* reset the state to Aborted */
    13015         if (mData->mMachineState != MachineState_Aborted)
    13016             setMachineState(MachineState_Aborted);
    13017     }
    13018 
    13019     // any machine settings modified?
    13020     if (mData->flModifications)
    13021     {
    13022         LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
    13023         rollback(false /* aNotify */);
    13024     }
    13025 
    13026     Assert(    mConsoleTaskData.strStateFilePath.isEmpty()
    13027             || !mConsoleTaskData.mSnapshot);
    13028     if (!mConsoleTaskData.strStateFilePath.isEmpty())
    13029     {
    13030         LogWarningThisFunc(("canceling failed save state request!\n"));
    13031         endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
    13032     }
    13033     else if (!mConsoleTaskData.mSnapshot.isNull())
    13034     {
    13035         LogWarningThisFunc(("canceling untaken snapshot!\n"));
    13036 
    13037         /* delete all differencing hard disks created (this will also attach
    13038          * their parents back by rolling back mMediaData) */
    13039         rollbackMedia();
    13040 
    13041         // delete the saved state file (it might have been already created)
    13042         // AFTER killing the snapshot so that releaseSavedStateFile() won't
    13043         // think it's still in use
    13044         Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
    13045         mConsoleTaskData.mSnapshot->uninit();
    13046         releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
    13047     }
    13048 
    13049     if (!mData->mSession.mType.isEmpty())
    13050     {
    13051         /* mType is not null when this machine's process has been started by
    13052          * Machine::LaunchVMProcess(), therefore it is our child.  We
    13053          * need to queue the PID to reap the process (and avoid zombies on
    13054          * Linux). */
    13055         Assert(mData->mSession.mPID != NIL_RTPROCESS);
    13056         mParent->addProcessToReap(mData->mSession.mPID);
    13057     }
    13058 
    13059     mData->mSession.mPID = NIL_RTPROCESS;
    13060 
    13061     if (aReason == Uninit::Unexpected)
    13062     {
    13063         /* Uninitialization didn't come from #checkForDeath(), so tell the
    13064          * client watcher thread to update the set of machines that have open
    13065          * sessions. */
    13066         mParent->updateClientWatcher();
    13067     }
    13068 
    13069     /* uninitialize all remote controls */
    13070     if (mData->mSession.mRemoteControls.size())
    13071     {
    13072         LogFlowThisFunc(("Closing remote sessions (%d):\n",
    13073                           mData->mSession.mRemoteControls.size()));
    13074 
    13075         Data::Session::RemoteControlList::iterator it =
    13076             mData->mSession.mRemoteControls.begin();
    13077         while (it != mData->mSession.mRemoteControls.end())
    13078         {
    13079             LogFlowThisFunc(("  Calling remoteControl->Uninitialize()...\n"));
    13080             HRESULT rc = (*it)->Uninitialize();
    13081             LogFlowThisFunc(("  remoteControl->Uninitialize() returned %08X\n", rc));
    13082             if (FAILED(rc))
    13083                 LogWarningThisFunc(("Forgot to close the remote session?\n"));
    13084             ++it;
    13085         }
    13086         mData->mSession.mRemoteControls.clear();
    13087     }
    13088 
    13089     /*
    13090      *  An expected uninitialization can come only from #checkForDeath().
    13091      *  Otherwise it means that something's gone really wrong (for example,
    13092      *  the Session implementation has released the VirtualBox reference
    13093      *  before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
    13094      *  etc). However, it's also possible, that the client releases the IPC
    13095      *  semaphore correctly (i.e. before it releases the VirtualBox reference),
    13096      *  but the VirtualBox release event comes first to the server process.
    13097      *  This case is practically possible, so we should not assert on an
    13098      *  unexpected uninit, just log a warning.
    13099      */
    13100 
    13101     if ((aReason == Uninit::Unexpected))
    13102         LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
    13103 
    13104     if (aReason != Uninit::Normal)
    13105     {
    13106         mData->mSession.mDirectControl.setNull();
    13107     }
    13108     else
    13109     {
    13110         /* this must be null here (see #OnSessionEnd()) */
    13111         Assert(mData->mSession.mDirectControl.isNull());
    13112         Assert(mData->mSession.mState == SessionState_Unlocking);
    13113         Assert(!mData->mSession.mProgress.isNull());
    13114     }
    13115     if (mData->mSession.mProgress)
    13116     {
    13117         if (aReason == Uninit::Normal)
    13118             mData->mSession.mProgress->notifyComplete(S_OK);
    13119         else
    13120             mData->mSession.mProgress->notifyComplete(E_FAIL,
    13121                                                       COM_IIDOF(ISession),
    13122                                                       getComponentName(),
    13123                                                       tr("The VM session was aborted"));
    13124         mData->mSession.mProgress.setNull();
    13125     }
    13126 
    13127     /* remove the association between the peer machine and this session machine */
    13128     Assert(   (SessionMachine*)mData->mSession.mMachine == this
    13129             || aReason == Uninit::Unexpected);
    13130 
    13131     /* reset the rest of session data */
    13132     mData->mSession.mMachine.setNull();
    13133     mData->mSession.mState = SessionState_Unlocked;
    13134     mData->mSession.mType.setNull();
    13135 
    13136     /* close the interprocess semaphore before leaving the exclusive lock */
    13137 #if defined(RT_OS_WINDOWS)
    13138     if (mIPCSem)
    13139         ::CloseHandle(mIPCSem);
    13140     mIPCSem = NULL;
    13141 #elif defined(RT_OS_OS2)
    13142     if (mIPCSem != NULLHANDLE)
    13143         ::DosCloseMutexSem(mIPCSem);
    13144     mIPCSem = NULLHANDLE;
    13145 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    13146     if (mIPCSem >= 0)
    13147         ::semctl(mIPCSem, 0, IPC_RMID);
    13148     mIPCSem = -1;
    13149 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    13150     mIPCKey = "0";
    13151 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
    13152 #else
    13153 # error "Port me!"
    13154 #endif
    13155 
    13156     /* fire an event */
    13157     mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
    13158 
    13159     uninitDataAndChildObjects();
    13160 
    13161     /* free the essential data structure last */
    13162     mData.free();
    13163 
    13164     /* release the exclusive lock before setting the below two to NULL */
    13165     multilock.release();
    13166 
    13167     unconst(mParent) = NULL;
    13168     unconst(mPeer) = NULL;
    13169 
    13170     LogFlowThisFuncLeave();
    13171 }
    13172 
    13173 // util::Lockable interface
    13174 ////////////////////////////////////////////////////////////////////////////////
    13175 
    13176 /**
    13177  *  Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
    13178  *  with the primary Machine instance (mPeer).
    13179  */
    13180 RWLockHandle *SessionMachine::lockHandle() const
    13181 {
    13182     AssertReturn(mPeer != NULL, NULL);
    13183     return mPeer->lockHandle();
    13184 }
    13185 
    13186 // IInternalMachineControl methods
    13187 ////////////////////////////////////////////////////////////////////////////////
    13188 
    13189 /**
    13190  *  Passes collected guest statistics to performance collector object
    13191  */
    13192 STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
    13193                                                 ULONG aCpuKernel, ULONG aCpuIdle,
    13194                                                 ULONG aMemTotal, ULONG aMemFree,
    13195                                                 ULONG aMemBalloon, ULONG aMemShared,
    13196                                                 ULONG aMemCache, ULONG aPageTotal,
    13197                                                 ULONG aAllocVMM, ULONG aFreeVMM,
    13198                                                 ULONG aBalloonedVMM, ULONG aSharedVMM,
    13199                                                 ULONG aVmNetRx, ULONG aVmNetTx)
    13200 {
    13201 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    13202     if (mCollectorGuest)
    13203         mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
    13204                                      aMemTotal, aMemFree, aMemBalloon, aMemShared,
    13205                                      aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
    13206                                      aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
    13207 
    13208     return S_OK;
    13209 #else
    13210     NOREF(aValidStats);
    13211     NOREF(aCpuUser);
    13212     NOREF(aCpuKernel);
    13213     NOREF(aCpuIdle);
    13214     NOREF(aMemTotal);
    13215     NOREF(aMemFree);
    13216     NOREF(aMemBalloon);
    13217     NOREF(aMemShared);
    13218     NOREF(aMemCache);
    13219     NOREF(aPageTotal);
    13220     NOREF(aAllocVMM);
    13221     NOREF(aFreeVMM);
    13222     NOREF(aBalloonedVMM);
    13223     NOREF(aSharedVMM);
    13224     NOREF(aVmNetRx);
    13225     NOREF(aVmNetTx);
    13226     return E_NOTIMPL;
    13227 #endif
    13228 }
    13229 
    13230 /**
    13231  *  @note Locks this object for writing.
    13232  */
    13233 STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
    13234 {
    13235     AutoCaller autoCaller(this);
    13236     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13237 
    13238     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13239 
    13240     mRemoveSavedState = aRemove;
    13241 
    13242     return S_OK;
    13243 }
    13244 
    13245 /**
    13246  *  @note Locks the same as #setMachineState() does.
    13247  */
    13248 STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
    13249 {
    13250     return setMachineState(aMachineState);
    13251 }
    13252 
    13253 /**
    13254  *  @note Locks this object for reading.
    13255  */
    13256 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
    13257 {
    13258     AutoCaller autoCaller(this);
    13259     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13260 
    13261     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    13262 
    13263 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    13264     mIPCSemName.cloneTo(aId);
    13265     return S_OK;
    13266 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    13267 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    13268     mIPCKey.cloneTo(aId);
    13269 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    13270     mData->m_strConfigFileFull.cloneTo(aId);
    13271 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    13272     return S_OK;
    13273 #else
    13274 # error "Port me!"
    13275 #endif
    13276 }
    13277 
    13278 /**
    13279  *  @note Locks this object for writing.
    13280  */
    13281 STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
    13282 {
    13283     LogFlowThisFunc(("aProgress=%p\n", aProgress));
    13284     AutoCaller autoCaller(this);
    13285     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13286 
    13287     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13288 
    13289     if (mData->mSession.mState != SessionState_Locked)
    13290         return VBOX_E_INVALID_OBJECT_STATE;
    13291 
    13292     if (!mData->mSession.mProgress.isNull())
    13293         mData->mSession.mProgress->setOtherProgressObject(aProgress);
    13294 
    13295     LogFlowThisFunc(("returns S_OK.\n"));
    13296     return S_OK;
    13297 }
    13298 
    13299 /**
    13300  *  @note Locks this object for writing.
    13301  */
    13302 STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
    13303 {
    13304     AutoCaller autoCaller(this);
    13305     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13306 
    13307     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13308 
    13309     if (mData->mSession.mState != SessionState_Locked)
    13310         return VBOX_E_INVALID_OBJECT_STATE;
    13311 
    13312     /* Finalize the LaunchVMProcess progress object. */
    13313     if (mData->mSession.mProgress)
    13314     {
    13315         mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
    13316         mData->mSession.mProgress.setNull();
    13317     }
    13318 
    13319     if (SUCCEEDED((HRESULT)iResult))
    13320     {
    13321 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    13322         /* The VM has been powered up successfully, so it makes sense
    13323          * now to offer the performance metrics for a running machine
    13324          * object. Doing it earlier wouldn't be safe. */
    13325         registerMetrics(mParent->performanceCollector(), mPeer,
    13326                         mData->mSession.mPID);
    13327 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    13328     }
    13329 
    13330     return S_OK;
    13331 }
    13332 
    13333 /**
    13334  *  @note Locks this object for writing.
    13335  */
    13336 STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
    13337 {
    13338     LogFlowThisFuncEnter();
    13339 
    13340     CheckComArgOutPointerValid(aProgress);
    13341 
    13342     AutoCaller autoCaller(this);
    13343     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13344 
    13345     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13346 
    13347     AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
    13348                  E_FAIL);
    13349 
    13350     /* create a progress object to track operation completion */
    13351     ComObjPtr<Progress> pProgress;
    13352     pProgress.createObject();
    13353     pProgress->init(getVirtualBox(),
    13354                     static_cast<IMachine *>(this) /* aInitiator */,
    13355                     Bstr(tr("Stopping the virtual machine")).raw(),
    13356                     FALSE /* aCancelable */);
    13357 
    13358     /* fill in the console task data */
    13359     mConsoleTaskData.mLastState = mData->mMachineState;
    13360     mConsoleTaskData.mProgress = pProgress;
    13361 
    13362     /* set the state to Stopping (this is expected by Console::PowerDown()) */
    13363     setMachineState(MachineState_Stopping);
    13364 
    13365     pProgress.queryInterfaceTo(aProgress);
    13366 
    13367     return S_OK;
    13368 }
    13369 
    13370 /**
    13371  *  @note Locks this object for writing.
    13372  */
    13373 STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
    13374 {
    13375     LogFlowThisFuncEnter();
    13376 
    13377     AutoCaller autoCaller(this);
    13378     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13379 
    13380     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13381 
    13382     AssertReturn(    (   (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
    13383                       || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
    13384                   && mConsoleTaskData.mLastState != MachineState_Null,
    13385                  E_FAIL);
    13386 
    13387     /*
    13388      * On failure, set the state to the state we had when BeginPoweringDown()
    13389      * was called (this is expected by Console::PowerDown() and the associated
    13390      * task). On success the VM process already changed the state to
    13391      * MachineState_PoweredOff, so no need to do anything.
    13392      */
    13393     if (FAILED(iResult))
    13394         setMachineState(mConsoleTaskData.mLastState);
    13395 
    13396     /* notify the progress object about operation completion */
    13397     Assert(mConsoleTaskData.mProgress);
    13398     if (SUCCEEDED(iResult))
    13399         mConsoleTaskData.mProgress->notifyComplete(S_OK);
    13400     else
    13401     {
    13402         Utf8Str strErrMsg(aErrMsg);
    13403         if (strErrMsg.length())
    13404             mConsoleTaskData.mProgress->notifyComplete(iResult,
    13405                                                        COM_IIDOF(ISession),
    13406                                                        getComponentName(),
    13407                                                        strErrMsg.c_str());
    13408         else
    13409             mConsoleTaskData.mProgress->notifyComplete(iResult);
    13410     }
    13411 
    13412     /* clear out the temporary saved state data */
    13413     mConsoleTaskData.mLastState = MachineState_Null;
    13414     mConsoleTaskData.mProgress.setNull();
    13415 
    13416     LogFlowThisFuncLeave();
    13417     return S_OK;
    13418 }
    13419 
    13420 
    13421 /**
    13422  *  Goes through the USB filters of the given machine to see if the given
    13423  *  device matches any filter or not.
    13424  *
    13425  *  @note Locks the same as USBController::hasMatchingFilter() does.
    13426  */
    13427 STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
    13428                                                  BOOL *aMatched,
    13429                                                  ULONG *aMaskedIfs)
    13430 {
    13431     LogFlowThisFunc(("\n"));
    13432 
    13433     CheckComArgNotNull(aUSBDevice);
    13434     CheckComArgOutPointerValid(aMatched);
    13435 
    13436     AutoCaller autoCaller(this);
    13437     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13438 
    13439 #ifdef VBOX_WITH_USB
    13440     *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
    13441 #else
    13442     NOREF(aUSBDevice);
    13443     NOREF(aMaskedIfs);
    13444     *aMatched = FALSE;
    13445 #endif
    13446 
    13447     return S_OK;
    13448 }
    13449 
    13450 /**
    13451  *  @note Locks the same as Host::captureUSBDevice() does.
    13452  */
    13453 STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
    13454 {
    13455     LogFlowThisFunc(("\n"));
    13456 
    13457     AutoCaller autoCaller(this);
    13458     AssertComRCReturnRC(autoCaller.rc());
    13459 
    13460 #ifdef VBOX_WITH_USB
    13461     /* if captureDeviceForVM() fails, it must have set extended error info */
    13462     clearError();
    13463     MultiResult rc = mParent->host()->checkUSBProxyService();
    13464     if (FAILED(rc)) return rc;
    13465 
    13466     USBProxyService *service = mParent->host()->usbProxyService();
    13467     AssertReturn(service, E_FAIL);
    13468     return service->captureDeviceForVM(this, Guid(aId).ref());
    13469 #else
    13470     NOREF(aId);
    13471     return E_NOTIMPL;
    13472 #endif
    13473 }
    13474 
    13475 /**
    13476  *  @note Locks the same as Host::detachUSBDevice() does.
    13477  */
    13478 STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
    13479 {
    13480     LogFlowThisFunc(("\n"));
    13481 
    13482     AutoCaller autoCaller(this);
    13483     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13484 
    13485 #ifdef VBOX_WITH_USB
    13486     USBProxyService *service = mParent->host()->usbProxyService();
    13487     AssertReturn(service, E_FAIL);
    13488     return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
    13489 #else
    13490     NOREF(aId);
    13491     NOREF(aDone);
    13492     return E_NOTIMPL;
    13493 #endif
    13494 }
    13495 
    13496 /**
    13497  *  Inserts all machine filters to the USB proxy service and then calls
    13498  *  Host::autoCaptureUSBDevices().
    13499  *
    13500  *  Called by Console from the VM process upon VM startup.
    13501  *
    13502  *  @note Locks what called methods lock.
    13503  */
    13504 STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
    13505 {
    13506     LogFlowThisFunc(("\n"));
    13507 
    13508     AutoCaller autoCaller(this);
    13509     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13510 
    13511 #ifdef VBOX_WITH_USB
    13512     HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
    13513     AssertComRC(rc);
    13514     NOREF(rc);
    13515 
    13516     USBProxyService *service = mParent->host()->usbProxyService();
    13517     AssertReturn(service, E_FAIL);
    13518     return service->autoCaptureDevicesForVM(this);
    13519 #else
    13520     return S_OK;
    13521 #endif
    13522 }
    13523 
    13524 /**
    13525  *  Removes all machine filters from the USB proxy service and then calls
    13526  *  Host::detachAllUSBDevices().
    13527  *
    13528  *  Called by Console from the VM process upon normal VM termination or by
    13529  *  SessionMachine::uninit() upon abnormal VM termination (from under the
    13530  *  Machine/SessionMachine lock).
    13531  *
    13532  *  @note Locks what called methods lock.
    13533  */
    13534 STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
    13535 {
    13536     LogFlowThisFunc(("\n"));
    13537 
    13538     AutoCaller autoCaller(this);
    13539     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13540 
    13541 #ifdef VBOX_WITH_USB
    13542     HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
    13543     AssertComRC(rc);
    13544     NOREF(rc);
    13545 
    13546     USBProxyService *service = mParent->host()->usbProxyService();
    13547     AssertReturn(service, E_FAIL);
    13548     return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
    13549 #else
    13550     NOREF(aDone);
    13551     return S_OK;
    13552 #endif
    13553 }
    13554 
    13555 /**
    13556  *  @note Locks this object for writing.
    13557  */
    13558 STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
    13559                                           IProgress **aProgress)
    13560 {
    13561     LogFlowThisFuncEnter();
    13562 
    13563     AssertReturn(aSession, E_INVALIDARG);
    13564     AssertReturn(aProgress, E_INVALIDARG);
    13565 
    13566     AutoCaller autoCaller(this);
    13567 
    13568     LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
    13569     /*
    13570      *  We don't assert below because it might happen that a non-direct session
    13571      *  informs us it is closed right after we've been uninitialized -- it's ok.
    13572      */
    13573     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    13574 
    13575     /* get IInternalSessionControl interface */
    13576     ComPtr<IInternalSessionControl> control(aSession);
    13577 
    13578     ComAssertRet(!control.isNull(), E_INVALIDARG);
    13579 
    13580     /* Creating a Progress object requires the VirtualBox lock, and
    13581      * thus locking it here is required by the lock order rules. */
    13582     AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
    13583 
    13584     if (control == mData->mSession.mDirectControl)
    13585     {
    13586         ComAssertRet(aProgress, E_POINTER);
    13587 
    13588         /* The direct session is being normally closed by the client process
    13589          * ----------------------------------------------------------------- */
    13590 
    13591         /* go to the closing state (essential for all open*Session() calls and
    13592          * for #checkForDeath()) */
    13593         Assert(mData->mSession.mState == SessionState_Locked);
    13594         mData->mSession.mState = SessionState_Unlocking;
    13595 
    13596         /* set direct control to NULL to release the remote instance */
    13597         mData->mSession.mDirectControl.setNull();
    13598         LogFlowThisFunc(("Direct control is set to NULL\n"));
    13599 
    13600         if (mData->mSession.mProgress)
    13601         {
    13602             /* finalize the progress, someone might wait if a frontend
    13603              * closes the session before powering on the VM. */
    13604             mData->mSession.mProgress->notifyComplete(E_FAIL,
    13605                                                       COM_IIDOF(ISession),
    13606                                                       getComponentName(),
    13607                                                       tr("The VM session was closed before any attempt to power it on"));
    13608             mData->mSession.mProgress.setNull();
    13609         }
    13610 
    13611         /*  Create the progress object the client will use to wait until
    13612          * #checkForDeath() is called to uninitialize this session object after
    13613          * it releases the IPC semaphore.
    13614          * Note! Because we're "reusing" mProgress here, this must be a proxy
    13615          *       object just like for LaunchVMProcess. */
    13616         Assert(mData->mSession.mProgress.isNull());
    13617         ComObjPtr<ProgressProxy> progress;
    13618         progress.createObject();
    13619         ComPtr<IUnknown> pPeer(mPeer);
    13620         progress->init(mParent, pPeer,
    13621                        Bstr(tr("Closing session")).raw(),
    13622                        FALSE /* aCancelable */);
    13623         progress.queryInterfaceTo(aProgress);
    13624         mData->mSession.mProgress = progress;
    13625     }
    13626     else
    13627     {
    13628         /* the remote session is being normally closed */
    13629         Data::Session::RemoteControlList::iterator it =
    13630             mData->mSession.mRemoteControls.begin();
    13631         while (it != mData->mSession.mRemoteControls.end())
    13632         {
    13633             if (control == *it)
    13634                 break;
    13635             ++it;
    13636         }
    13637         BOOL found = it != mData->mSession.mRemoteControls.end();
    13638         ComAssertMsgRet(found, ("The session is not found in the session list!"),
    13639                          E_INVALIDARG);
    13640         // This MUST be erase(it), not remove(*it) as the latter triggers a
    13641         // very nasty use after free due to the place where the value "lives".
    13642         mData->mSession.mRemoteControls.erase(it);
    13643     }
    13644 
    13645     /* signal the client watcher thread, because the client is going away */
    13646     mParent->updateClientWatcher();
    13647 
    13648     LogFlowThisFuncLeave();
    13649     return S_OK;
    13650 }
    13651 
    13652 /**
    13653  *  @note Locks this object for writing.
    13654  */
    13655 STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
    13656 {
    13657     LogFlowThisFuncEnter();
    13658 
    13659     CheckComArgOutPointerValid(aProgress);
    13660     CheckComArgOutPointerValid(aStateFilePath);
    13661 
    13662     AutoCaller autoCaller(this);
    13663     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13664 
    13665     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13666 
    13667     AssertReturn(    mData->mMachineState == MachineState_Paused
    13668                   && mConsoleTaskData.mLastState == MachineState_Null
    13669                   && mConsoleTaskData.strStateFilePath.isEmpty(),
    13670                  E_FAIL);
    13671 
    13672     /* create a progress object to track operation completion */
    13673     ComObjPtr<Progress> pProgress;
    13674     pProgress.createObject();
    13675     pProgress->init(getVirtualBox(),
    13676                     static_cast<IMachine *>(this) /* aInitiator */,
    13677                     Bstr(tr("Saving the execution state of the virtual machine")).raw(),
    13678                     FALSE /* aCancelable */);
    13679 
    13680     Utf8Str strStateFilePath;
    13681     /* stateFilePath is null when the machine is not running */
    13682     if (mData->mMachineState == MachineState_Paused)
    13683         composeSavedStateFilename(strStateFilePath);
    13684 
    13685     /* fill in the console task data */
    13686     mConsoleTaskData.mLastState = mData->mMachineState;
    13687     mConsoleTaskData.strStateFilePath = strStateFilePath;
    13688     mConsoleTaskData.mProgress = pProgress;
    13689 
    13690     /* set the state to Saving (this is expected by Console::SaveState()) */
    13691     setMachineState(MachineState_Saving);
    13692 
    13693     strStateFilePath.cloneTo(aStateFilePath);
    13694     pProgress.queryInterfaceTo(aProgress);
    13695 
    13696     return S_OK;
    13697 }
    13698 
    13699 /**
    13700  *  @note Locks mParent + this object for writing.
    13701  */
    13702 STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
    13703 {
    13704     LogFlowThisFunc(("\n"));
    13705 
    13706     AutoCaller autoCaller(this);
    13707     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13708 
    13709     /* endSavingState() need mParent lock */
    13710     AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
    13711 
    13712     AssertReturn(    (   (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
    13713                       || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
    13714                   && mConsoleTaskData.mLastState != MachineState_Null
    13715                   && !mConsoleTaskData.strStateFilePath.isEmpty(),
    13716                  E_FAIL);
    13717 
    13718     /*
    13719      * On failure, set the state to the state we had when BeginSavingState()
    13720      * was called (this is expected by Console::SaveState() and the associated
    13721      * task). On success the VM process already changed the state to
    13722      * MachineState_Saved, so no need to do anything.
    13723      */
    13724     if (FAILED(iResult))
    13725         setMachineState(mConsoleTaskData.mLastState);
    13726 
    13727     return endSavingState(iResult, aErrMsg);
    13728 }
    13729 
    13730 /**
    13731  *  @note Locks this object for writing.
    13732  */
    13733 STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
    13734 {
    13735     LogFlowThisFunc(("\n"));
    13736 
    13737     CheckComArgStrNotEmptyOrNull(aSavedStateFile);
    13738 
    13739     AutoCaller autoCaller(this);
    13740     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13741 
    13742     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13743 
    13744     AssertReturn(   mData->mMachineState == MachineState_PoweredOff
    13745                  || mData->mMachineState == MachineState_Teleported
    13746                  || mData->mMachineState == MachineState_Aborted
    13747                  , E_FAIL); /** @todo setError. */
    13748 
    13749     Utf8Str stateFilePathFull = aSavedStateFile;
    13750     int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
    13751     if (RT_FAILURE(vrc))
    13752         return setError(VBOX_E_FILE_ERROR,
    13753                         tr("Invalid saved state file path '%ls' (%Rrc)"),
    13754                         aSavedStateFile,
    13755                         vrc);
    13756 
    13757     mSSData->strStateFilePath = stateFilePathFull;
    13758 
    13759     /* The below setMachineState() will detect the state transition and will
    13760      * update the settings file */
    13761 
    13762     return setMachineState(MachineState_Saved);
    13763 }
    13764 
    13765 STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
    13766                                                  ComSafeArrayOut(BSTR, aValues),
    13767                                                  ComSafeArrayOut(LONG64, aTimestamps),
    13768                                                  ComSafeArrayOut(BSTR, aFlags))
    13769 {
    13770     LogFlowThisFunc(("\n"));
    13771 
    13772 #ifdef VBOX_WITH_GUEST_PROPS
    13773     using namespace guestProp;
    13774 
    13775     AutoCaller autoCaller(this);
    13776     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13777 
    13778     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    13779 
    13780     CheckComArgOutSafeArrayPointerValid(aNames);
    13781     CheckComArgOutSafeArrayPointerValid(aValues);
    13782     CheckComArgOutSafeArrayPointerValid(aTimestamps);
    13783     CheckComArgOutSafeArrayPointerValid(aFlags);
    13784 
    13785     size_t cEntries = mHWData->mGuestProperties.size();
    13786     com::SafeArray<BSTR> names(cEntries);
    13787     com::SafeArray<BSTR> values(cEntries);
    13788     com::SafeArray<LONG64> timestamps(cEntries);
    13789     com::SafeArray<BSTR> flags(cEntries);
    13790     unsigned i = 0;
    13791     for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
    13792          it != mHWData->mGuestProperties.end();
    13793          ++it)
    13794     {
    13795         char szFlags[MAX_FLAGS_LEN + 1];
    13796         it->first.cloneTo(&names[i]);
    13797         it->second.strValue.cloneTo(&values[i]);
    13798         timestamps[i] = it->second.mTimestamp;
    13799         /* If it is NULL, keep it NULL. */
    13800         if (it->second.mFlags)
    13801         {
    13802             writeFlags(it->second.mFlags, szFlags);
    13803             Bstr(szFlags).cloneTo(&flags[i]);
    13804         }
    13805         else
    13806             flags[i] = NULL;
    13807         ++i;
    13808     }
    13809     names.detachTo(ComSafeArrayOutArg(aNames));
    13810     values.detachTo(ComSafeArrayOutArg(aValues));
    13811     timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
    13812     flags.detachTo(ComSafeArrayOutArg(aFlags));
    13813     return S_OK;
    13814 #else
    13815     ReturnComNotImplemented();
    13816 #endif
    13817 }
    13818 
    13819 STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
    13820                                                IN_BSTR aValue,
    13821                                                LONG64 aTimestamp,
    13822                                                IN_BSTR aFlags)
    13823 {
    13824     LogFlowThisFunc(("\n"));
    13825 
    13826 #ifdef VBOX_WITH_GUEST_PROPS
    13827     using namespace guestProp;
    13828 
    13829     CheckComArgStrNotEmptyOrNull(aName);
    13830     CheckComArgNotNull(aValue);
    13831     CheckComArgNotNull(aFlags);
    13832 
    13833     try
    13834     {
    13835         /*
    13836          * Convert input up front.
    13837          */
    13838         Utf8Str  utf8Name(aName);
    13839         uint32_t fFlags = NILFLAG;
    13840         if (aFlags)
    13841         {
    13842             Utf8Str utf8Flags(aFlags);
    13843             int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
    13844             AssertRCReturn(vrc, E_INVALIDARG);
    13845         }
    13846 
    13847         /*
    13848          * Now grab the object lock, validate the state and do the update.
    13849          */
    13850         AutoCaller autoCaller(this);
    13851         if (FAILED(autoCaller.rc())) return autoCaller.rc();
    13852 
    13853         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    13854 
    13855         switch (mData->mMachineState)
    13856         {
    13857             case MachineState_Paused:
    13858             case MachineState_Running:
    13859             case MachineState_Teleporting:
    13860             case MachineState_TeleportingPausedVM:
    13861             case MachineState_LiveSnapshotting:
    13862             case MachineState_DeletingSnapshotOnline:
    13863             case MachineState_DeletingSnapshotPaused:
    13864             case MachineState_Saving:
    13865             case MachineState_Stopping:
    13866                 break;
    13867 
    13868             default:
    13869                 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
    13870                                       VBOX_E_INVALID_VM_STATE);
    13871         }
    13872 
    13873         setModified(IsModified_MachineData);
    13874         mHWData.backup();
    13875 
    13876         bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
    13877         HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
    13878         if (it != mHWData->mGuestProperties.end())
    13879         {
    13880             if (!fDelete)
    13881             {
    13882                 it->second.strValue   = aValue;
    13883                 it->second.mTimestamp = aTimestamp;
    13884                 it->second.mFlags     = fFlags;
    13885             }
    13886             else
    13887                 mHWData->mGuestProperties.erase(it);
    13888 
    13889             mData->mGuestPropertiesModified = TRUE;
    13890         }
    13891         else if (!fDelete)
    13892         {
    13893             HWData::GuestProperty prop;
    13894             prop.strValue   = aValue;
    13895             prop.mTimestamp = aTimestamp;
    13896             prop.mFlags     = fFlags;
    13897 
    13898             mHWData->mGuestProperties[utf8Name] = prop;
    13899             mData->mGuestPropertiesModified = TRUE;
    13900         }
    13901 
    13902         /*
    13903          * Send a callback notification if appropriate
    13904          */
    13905         if (    mHWData->mGuestPropertyNotificationPatterns.isEmpty()
    13906              || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
    13907                                              RTSTR_MAX,
    13908                                              utf8Name.c_str(),
    13909                                              RTSTR_MAX, NULL)
    13910            )
    13911         {
    13912             alock.release();
    13913 
    13914             mParent->onGuestPropertyChange(mData->mUuid,
    13915                                            aName,
    13916                                            aValue,
    13917                                            aFlags);
    13918         }
    13919     }
    13920     catch (...)
    13921     {
    13922         return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    13923     }
    13924     return S_OK;
    13925 #else
    13926     ReturnComNotImplemented();
    13927 #endif
    13928 }
    13929 
    13930 STDMETHODIMP SessionMachine::LockMedia()
    13931 {
    13932     AutoCaller autoCaller(this);
    13933     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13934 
    13935     AutoMultiWriteLock2 alock(this->lockHandle(),
    13936                               &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    13937 
    13938     AssertReturn(   mData->mMachineState == MachineState_Starting
    13939                  || mData->mMachineState == MachineState_Restoring
    13940                  || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
    13941 
    13942     clearError();
    13943     alock.release();
    13944     return lockMedia();
    13945 }
    13946 
    13947 STDMETHODIMP SessionMachine::UnlockMedia()
    13948 {
    13949     unlockMedia();
    13950     return S_OK;
    13951 }
    13952 
    13953 STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
    13954                                          IMediumAttachment **aNewAttachment)
    13955 {
    13956     CheckComArgNotNull(aAttachment);
    13957     CheckComArgOutPointerValid(aNewAttachment);
    13958 
    13959     AutoCaller autoCaller(this);
    13960     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    13961 
    13962     // request the host lock first, since might be calling Host methods for getting host drives;
    13963     // next, protect the media tree all the while we're in here, as well as our member variables
    13964     AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
    13965                                   this->lockHandle(),
    13966                                   &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    13967 
    13968     ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
    13969 
    13970     Bstr ctrlName;
    13971     LONG lPort;
    13972     LONG lDevice;
    13973     bool fTempEject;
    13974     {
    13975         AutoCaller autoAttachCaller(this);
    13976         if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
    13977 
    13978         AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    13979 
    13980         /* Need to query the details first, as the IMediumAttachment reference
    13981          * might be to the original settings, which we are going to change. */
    13982         ctrlName = pAttach->getControllerName();
    13983         lPort = pAttach->getPort();
    13984         lDevice = pAttach->getDevice();
    13985         fTempEject = pAttach->getTempEject();
    13986     }
    13987 
    13988     if (!fTempEject)
    13989     {
    13990         /* Remember previously mounted medium. The medium before taking the
    13991          * backup is not necessarily the same thing. */
    13992         ComObjPtr<Medium> oldmedium;
    13993         oldmedium = pAttach->getMedium();
    13994 
    13995         setModified(IsModified_Storage);
    13996         mMediaData.backup();
    13997 
    13998         // The backup operation makes the pAttach reference point to the
    13999         // old settings. Re-get the correct reference.
    14000         pAttach = findAttachment(mMediaData->mAttachments,
    14001                                  ctrlName.raw(),
    14002                                  lPort,
    14003                                  lDevice);
    14004 
    14005         {
    14006             AutoCaller autoAttachCaller(this);
    14007             if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
    14008 
    14009             AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    14010             if (!oldmedium.isNull())
    14011                 oldmedium->removeBackReference(mData->mUuid);
    14012 
    14013             pAttach->updateMedium(NULL);
    14014             pAttach->updateEjected();
    14015         }
    14016 
    14017         setModified(IsModified_Storage);
    14018     }
    14019     else
    14020     {
    14021         {
    14022             AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    14023             pAttach->updateEjected();
    14024         }
    14025     }
    14026 
    14027     pAttach.queryInterfaceTo(aNewAttachment);
    14028 
    14029     return S_OK;
    14030 }
    14031 
    14032 // public methods only for internal purposes
    14033 /////////////////////////////////////////////////////////////////////////////
    14034 
    14035 /**
    14036  * Called from the client watcher thread to check for expected or unexpected
    14037  * death of the client process that has a direct session to this machine.
    14038  *
    14039  * On Win32 and on OS/2, this method is called only when we've got the
    14040  * mutex (i.e. the client has either died or terminated normally) so it always
    14041  * returns @c true (the client is terminated, the session machine is
    14042  * uninitialized).
    14043  *
    14044  * On other platforms, the method returns @c true if the client process has
    14045  * terminated normally or abnormally and the session machine was uninitialized,
    14046  * and @c false if the client process is still alive.
    14047  *
    14048  * @note Locks this object for writing.
    14049  */
    14050 bool SessionMachine::checkForDeath()
    14051 {
    14052     Uninit::Reason reason;
    14053     bool terminated = false;
    14054 
    14055     /* Enclose autoCaller with a block because calling uninit() from under it
    14056      * will deadlock. */
    14057     {
    14058         AutoCaller autoCaller(this);
    14059         if (!autoCaller.isOk())
    14060         {
    14061             /* return true if not ready, to cause the client watcher to exclude
    14062              * the corresponding session from watching */
    14063             LogFlowThisFunc(("Already uninitialized!\n"));
    14064             return true;
    14065         }
    14066 
    14067         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    14068 
    14069         /* Determine the reason of death: if the session state is Closing here,
    14070          * everything is fine. Otherwise it means that the client did not call
    14071          * OnSessionEnd() before it released the IPC semaphore. This may happen
    14072          * either because the client process has abnormally terminated, or
    14073          * because it simply forgot to call ISession::Close() before exiting. We
    14074          * threat the latter also as an abnormal termination (see
    14075          * Session::uninit() for details). */
    14076         reason = mData->mSession.mState == SessionState_Unlocking ?
    14077                  Uninit::Normal :
    14078                  Uninit::Abnormal;
    14079 
    14080 #if defined(RT_OS_WINDOWS)
    14081 
    14082         AssertMsg(mIPCSem, ("semaphore must be created"));
    14083 
    14084         /* release the IPC mutex */
    14085         ::ReleaseMutex(mIPCSem);
    14086 
    14087         terminated = true;
    14088 
    14089 #elif defined(RT_OS_OS2)
    14090 
    14091         AssertMsg(mIPCSem, ("semaphore must be created"));
    14092 
    14093         /* release the IPC mutex */
    14094         ::DosReleaseMutexSem(mIPCSem);
    14095 
    14096         terminated = true;
    14097 
    14098 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    14099 
    14100         AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
    14101 
    14102         int val = ::semctl(mIPCSem, 0, GETVAL);
    14103         if (val > 0)
    14104         {
    14105             /* the semaphore is signaled, meaning the session is terminated */
    14106             terminated = true;
    14107         }
    14108 
    14109 #else
    14110 # error "Port me!"
    14111 #endif
    14112 
    14113     } /* AutoCaller block */
    14114 
    14115     if (terminated)
    14116         uninit(reason);
    14117 
    14118207    return terminated;
    14119208}
    14120209
    14121 /**
    14122  *  @note Locks this object for reading.
    14123  */
    14124 HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
    14125 {
    14126     LogFlowThisFunc(("\n"));
    14127 
    14128     AutoCaller autoCaller(this);
    14129     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14130 
    14131     ComPtr<IInternalSessionControl> directControl;
    14132     {
    14133         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14134         directControl = mData->mSession.mDirectControl;
    14135     }
    14136 
    14137     /* ignore notifications sent after #OnSessionEnd() is called */
    14138     if (!directControl)
    14139         return S_OK;
    14140 
    14141     return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
    14142 }
    14143 
    14144 /**
    14145  *  @note Locks this object for reading.
    14146  */
    14147 HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
    14148                                  NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
    14149 {
    14150     LogFlowThisFunc(("\n"));
    14151 
    14152     AutoCaller autoCaller(this);
    14153     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14154 
    14155     ComPtr<IInternalSessionControl> directControl;
    14156     {
    14157         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14158         directControl = mData->mSession.mDirectControl;
    14159     }
    14160 
    14161     /* ignore notifications sent after #OnSessionEnd() is called */
    14162     if (!directControl)
    14163         return S_OK;
    14164     /*
    14165      * instead acting like callback we ask IVirtualBox deliver corresponding event
    14166      */
    14167 
    14168     mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
    14169     return S_OK;
    14170 }
    14171 
    14172 /**
    14173  *  @note Locks this object for reading.
    14174  */
    14175 HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
    14176 {
    14177     LogFlowThisFunc(("\n"));
    14178 
    14179     AutoCaller autoCaller(this);
    14180     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14181 
    14182     ComPtr<IInternalSessionControl> directControl;
    14183     {
    14184         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14185         directControl = mData->mSession.mDirectControl;
    14186     }
    14187 
    14188     /* ignore notifications sent after #OnSessionEnd() is called */
    14189     if (!directControl)
    14190         return S_OK;
    14191 
    14192     return directControl->OnSerialPortChange(serialPort);
    14193 }
    14194 
    14195 /**
    14196  *  @note Locks this object for reading.
    14197  */
    14198 HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
    14199 {
    14200     LogFlowThisFunc(("\n"));
    14201 
    14202     AutoCaller autoCaller(this);
    14203     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14204 
    14205     ComPtr<IInternalSessionControl> directControl;
    14206     {
    14207         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14208         directControl = mData->mSession.mDirectControl;
    14209     }
    14210 
    14211     /* ignore notifications sent after #OnSessionEnd() is called */
    14212     if (!directControl)
    14213         return S_OK;
    14214 
    14215     return directControl->OnParallelPortChange(parallelPort);
    14216 }
    14217 
    14218 /**
    14219  *  @note Locks this object for reading.
    14220  */
    14221 HRESULT SessionMachine::onStorageControllerChange()
    14222 {
    14223     LogFlowThisFunc(("\n"));
    14224 
    14225     AutoCaller autoCaller(this);
    14226     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14227 
    14228     ComPtr<IInternalSessionControl> directControl;
    14229     {
    14230         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14231         directControl = mData->mSession.mDirectControl;
    14232     }
    14233 
    14234     /* ignore notifications sent after #OnSessionEnd() is called */
    14235     if (!directControl)
    14236         return S_OK;
    14237 
    14238     return directControl->OnStorageControllerChange();
    14239 }
    14240 
    14241 /**
    14242  *  @note Locks this object for reading.
    14243  */
    14244 HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
    14245 {
    14246     LogFlowThisFunc(("\n"));
    14247 
    14248     AutoCaller autoCaller(this);
    14249     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14250 
    14251     ComPtr<IInternalSessionControl> directControl;
    14252     {
    14253         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14254         directControl = mData->mSession.mDirectControl;
    14255     }
    14256 
    14257     /* ignore notifications sent after #OnSessionEnd() is called */
    14258     if (!directControl)
    14259         return S_OK;
    14260 
    14261     return directControl->OnMediumChange(aAttachment, aForce);
    14262 }
    14263 
    14264 /**
    14265  *  @note Locks this object for reading.
    14266  */
    14267 HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
    14268 {
    14269     LogFlowThisFunc(("\n"));
    14270 
    14271     AutoCaller autoCaller(this);
    14272     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14273 
    14274     ComPtr<IInternalSessionControl> directControl;
    14275     {
    14276         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14277         directControl = mData->mSession.mDirectControl;
    14278     }
    14279 
    14280     /* ignore notifications sent after #OnSessionEnd() is called */
    14281     if (!directControl)
    14282         return S_OK;
    14283 
    14284     return directControl->OnCPUChange(aCPU, aRemove);
    14285 }
    14286 
    14287 HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
    14288 {
    14289     LogFlowThisFunc(("\n"));
    14290 
    14291     AutoCaller autoCaller(this);
    14292     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14293 
    14294     ComPtr<IInternalSessionControl> directControl;
    14295     {
    14296         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14297         directControl = mData->mSession.mDirectControl;
    14298     }
    14299 
    14300     /* ignore notifications sent after #OnSessionEnd() is called */
    14301     if (!directControl)
    14302         return S_OK;
    14303 
    14304     return directControl->OnCPUExecutionCapChange(aExecutionCap);
    14305 }
    14306 
    14307 /**
    14308  *  @note Locks this object for reading.
    14309  */
    14310 HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
    14311 {
    14312     LogFlowThisFunc(("\n"));
    14313 
    14314     AutoCaller autoCaller(this);
    14315     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14316 
    14317     ComPtr<IInternalSessionControl> directControl;
    14318     {
    14319         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14320         directControl = mData->mSession.mDirectControl;
    14321     }
    14322 
    14323     /* ignore notifications sent after #OnSessionEnd() is called */
    14324     if (!directControl)
    14325         return S_OK;
    14326 
    14327     return directControl->OnVRDEServerChange(aRestart);
    14328 }
    14329 
    14330 /**
    14331  * @note Locks this object for reading.
    14332  */
    14333 HRESULT SessionMachine::onVideoCaptureChange()
    14334 {
    14335     LogFlowThisFunc(("\n"));
    14336 
    14337     AutoCaller autoCaller(this);
    14338     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14339 
    14340     ComPtr<IInternalSessionControl> directControl;
    14341     {
    14342         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14343         directControl = mData->mSession.mDirectControl;
    14344     }
    14345 
    14346     /* ignore notifications sent after #OnSessionEnd() is called */
    14347     if (!directControl)
    14348         return S_OK;
    14349 
    14350     return directControl->OnVideoCaptureChange();
    14351 }
    14352 
    14353 /**
    14354  *  @note Locks this object for reading.
    14355  */
    14356 HRESULT SessionMachine::onUSBControllerChange()
    14357 {
    14358     LogFlowThisFunc(("\n"));
    14359 
    14360     AutoCaller autoCaller(this);
    14361     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14362 
    14363     ComPtr<IInternalSessionControl> directControl;
    14364     {
    14365         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14366         directControl = mData->mSession.mDirectControl;
    14367     }
    14368 
    14369     /* ignore notifications sent after #OnSessionEnd() is called */
    14370     if (!directControl)
    14371         return S_OK;
    14372 
    14373     return directControl->OnUSBControllerChange();
    14374 }
    14375 
    14376 /**
    14377  *  @note Locks this object for reading.
    14378  */
    14379 HRESULT SessionMachine::onSharedFolderChange()
    14380 {
    14381     LogFlowThisFunc(("\n"));
    14382 
    14383     AutoCaller autoCaller(this);
    14384     AssertComRCReturnRC(autoCaller.rc());
    14385 
    14386     ComPtr<IInternalSessionControl> directControl;
    14387     {
    14388         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14389         directControl = mData->mSession.mDirectControl;
    14390     }
    14391 
    14392     /* ignore notifications sent after #OnSessionEnd() is called */
    14393     if (!directControl)
    14394         return S_OK;
    14395 
    14396     return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
    14397 }
    14398 
    14399 /**
    14400  * @note Locks this object for reading.
    14401  */
    14402 HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
    14403 {
    14404     LogFlowThisFunc(("\n"));
    14405 
    14406     AutoCaller autoCaller(this);
    14407     AssertComRCReturnRC(autoCaller.rc());
    14408 
    14409     ComPtr<IInternalSessionControl> directControl;
    14410     {
    14411         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14412         directControl = mData->mSession.mDirectControl;
    14413     }
    14414 
    14415     /* ignore notifications sent after #OnSessionEnd() is called */
    14416     if (!directControl)
    14417         return S_OK;
    14418 
    14419     return directControl->OnClipboardModeChange(aClipboardMode);
    14420 }
    14421 
    14422 /**
    14423  * @note Locks this object for reading.
    14424  */
    14425 HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
    14426 {
    14427     LogFlowThisFunc(("\n"));
    14428 
    14429     AutoCaller autoCaller(this);
    14430     AssertComRCReturnRC(autoCaller.rc());
    14431 
    14432     ComPtr<IInternalSessionControl> directControl;
    14433     {
    14434         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14435         directControl = mData->mSession.mDirectControl;
    14436     }
    14437 
    14438     /* ignore notifications sent after #OnSessionEnd() is called */
    14439     if (!directControl)
    14440         return S_OK;
    14441 
    14442     return directControl->OnDragAndDropModeChange(aDragAndDropMode);
    14443 }
    14444 
    14445 /**
    14446  *  @note Locks this object for reading.
    14447  */
    14448 HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
    14449 {
    14450     LogFlowThisFunc(("\n"));
    14451 
    14452     AutoCaller autoCaller(this);
    14453     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14454 
    14455     ComPtr<IInternalSessionControl> directControl;
    14456     {
    14457         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14458         directControl = mData->mSession.mDirectControl;
    14459     }
    14460 
    14461     /* ignore notifications sent after #OnSessionEnd() is called */
    14462     if (!directControl)
    14463         return S_OK;
    14464 
    14465     return directControl->OnBandwidthGroupChange(aBandwidthGroup);
    14466 }
    14467 
    14468 /**
    14469  *  @note Locks this object for reading.
    14470  */
    14471 HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
    14472 {
    14473     LogFlowThisFunc(("\n"));
    14474 
    14475     AutoCaller autoCaller(this);
    14476     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14477 
    14478     ComPtr<IInternalSessionControl> directControl;
    14479     {
    14480         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14481         directControl = mData->mSession.mDirectControl;
    14482     }
    14483 
    14484     /* ignore notifications sent after #OnSessionEnd() is called */
    14485     if (!directControl)
    14486         return S_OK;
    14487 
    14488     return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
    14489 }
    14490 
    14491 /**
    14492  *  Returns @c true if this machine's USB controller reports it has a matching
    14493  *  filter for the given USB device and @c false otherwise.
    14494  *
    14495  *  @note locks this object for reading.
    14496  */
    14497 bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
    14498 {
    14499     AutoCaller autoCaller(this);
    14500     /* silently return if not ready -- this method may be called after the
    14501      * direct machine session has been called */
    14502     if (!autoCaller.isOk())
    14503         return false;
    14504 
    14505 #ifdef VBOX_WITH_USB
    14506     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14507 
    14508     switch (mData->mMachineState)
    14509     {
    14510         case MachineState_Starting:
    14511         case MachineState_Restoring:
    14512         case MachineState_TeleportingIn:
    14513         case MachineState_Paused:
    14514         case MachineState_Running:
    14515         /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
    14516          *        elsewhere... */
    14517             alock.release();
    14518             return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
    14519         default: break;
    14520     }
    14521 #else
    14522     NOREF(aDevice);
    14523     NOREF(aMaskedIfs);
    14524 #endif
    14525     return false;
    14526 }
    14527 
    14528 /**
    14529  *  @note The calls shall hold no locks. Will temporarily lock this object for reading.
    14530  */
    14531 HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
    14532                                           IVirtualBoxErrorInfo *aError,
    14533                                           ULONG aMaskedIfs)
    14534 {
    14535     LogFlowThisFunc(("\n"));
    14536 
    14537     AutoCaller autoCaller(this);
    14538 
    14539     /* This notification may happen after the machine object has been
    14540      * uninitialized (the session was closed), so don't assert. */
    14541     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    14542 
    14543     ComPtr<IInternalSessionControl> directControl;
    14544     {
    14545         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14546         directControl = mData->mSession.mDirectControl;
    14547     }
    14548 
    14549     /* fail on notifications sent after #OnSessionEnd() is called, it is
    14550      * expected by the caller */
    14551     if (!directControl)
    14552         return E_FAIL;
    14553 
    14554     /* No locks should be held at this point. */
    14555     AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
    14556     AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
    14557 
    14558     return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
    14559 }
    14560 
    14561 /**
    14562  *  @note The calls shall hold no locks. Will temporarily lock this object for reading.
    14563  */
    14564 HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
    14565                                           IVirtualBoxErrorInfo *aError)
    14566 {
    14567     LogFlowThisFunc(("\n"));
    14568 
    14569     AutoCaller autoCaller(this);
    14570 
    14571     /* This notification may happen after the machine object has been
    14572      * uninitialized (the session was closed), so don't assert. */
    14573     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    14574 
    14575     ComPtr<IInternalSessionControl> directControl;
    14576     {
    14577         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    14578         directControl = mData->mSession.mDirectControl;
    14579     }
    14580 
    14581     /* fail on notifications sent after #OnSessionEnd() is called, it is
    14582      * expected by the caller */
    14583     if (!directControl)
    14584         return E_FAIL;
    14585 
    14586     /* No locks should be held at this point. */
    14587     AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
    14588     AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
    14589 
    14590     return directControl->OnUSBDeviceDetach(aId, aError);
    14591 }
    14592 
    14593 // protected methods
    14594 /////////////////////////////////////////////////////////////////////////////
    14595 
    14596 /**
    14597  *  Helper method to finalize saving the state.
    14598  *
    14599  *  @note Must be called from under this object's lock.
    14600  *
    14601  *  @param aRc      S_OK if the snapshot has been taken successfully
    14602  *  @param aErrMsg  human readable error message for failure
    14603  *
    14604  *  @note Locks mParent + this objects for writing.
    14605  */
    14606 HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
    14607 {
    14608     LogFlowThisFuncEnter();
    14609 
    14610     AutoCaller autoCaller(this);
    14611     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14612 
    14613     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    14614 
    14615     HRESULT rc = S_OK;
    14616 
    14617     if (SUCCEEDED(aRc))
    14618     {
    14619         mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
    14620 
    14621         /* save all VM settings */
    14622         rc = saveSettings(NULL);
    14623                 // no need to check whether VirtualBox.xml needs saving also since
    14624                 // we can't have a name change pending at this point
    14625     }
    14626     else
    14627     {
    14628         // delete the saved state file (it might have been already created);
    14629         // we need not check whether this is shared with a snapshot here because
    14630         // we certainly created this saved state file here anew
    14631         RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
    14632     }
    14633 
    14634     /* notify the progress object about operation completion */
    14635     Assert(mConsoleTaskData.mProgress);
    14636     if (SUCCEEDED(aRc))
    14637         mConsoleTaskData.mProgress->notifyComplete(S_OK);
    14638     else
    14639     {
    14640         if (aErrMsg.length())
    14641             mConsoleTaskData.mProgress->notifyComplete(aRc,
    14642                                                        COM_IIDOF(ISession),
    14643                                                        getComponentName(),
    14644                                                        aErrMsg.c_str());
    14645         else
    14646             mConsoleTaskData.mProgress->notifyComplete(aRc);
    14647     }
    14648 
    14649     /* clear out the temporary saved state data */
    14650     mConsoleTaskData.mLastState = MachineState_Null;
    14651     mConsoleTaskData.strStateFilePath.setNull();
    14652     mConsoleTaskData.mProgress.setNull();
    14653 
    14654     LogFlowThisFuncLeave();
    14655     return rc;
    14656 }
    14657 
    14658 /**
    14659  * Deletes the given file if it is no longer in use by either the current machine state
    14660  * (if the machine is "saved") or any of the machine's snapshots.
    14661  *
    14662  * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
    14663  * but is different for each SnapshotMachine. When calling this, the order of calling this
    14664  * function on the one hand and changing that variable OR the snapshots tree on the other hand
    14665  * is therefore critical. I know, it's all rather messy.
    14666  *
    14667  * @param strStateFile
    14668  * @param pSnapshotToIgnore  Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
    14669  */
    14670 void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
    14671                                            Snapshot *pSnapshotToIgnore)
    14672 {
    14673     // it is safe to delete this saved state file if it is not currently in use by the machine ...
    14674     if (    (strStateFile.isNotEmpty())
    14675          && (strStateFile != mSSData->strStateFilePath)     // session machine's saved state
    14676        )
    14677         // ... and it must also not be shared with other snapshots
    14678         if (    !mData->mFirstSnapshot
    14679              || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
    14680                                 // this checks the SnapshotMachine's state file paths
    14681            )
    14682             RTFileDelete(strStateFile.c_str());
    14683 }
    14684 
    14685 /**
    14686  * Locks the attached media.
    14687  *
    14688  * All attached hard disks are locked for writing and DVD/floppy are locked for
    14689  * reading. Parents of attached hard disks (if any) are locked for reading.
    14690  *
    14691  * This method also performs accessibility check of all media it locks: if some
    14692  * media is inaccessible, the method will return a failure and a bunch of
    14693  * extended error info objects per each inaccessible medium.
    14694  *
    14695  * Note that this method is atomic: if it returns a success, all media are
    14696  * locked as described above; on failure no media is locked at all (all
    14697  * succeeded individual locks will be undone).
    14698  *
    14699  * The caller is responsible for doing the necessary state sanity checks.
    14700  *
    14701  * The locks made by this method must be undone by calling #unlockMedia() when
    14702  * no more needed.
    14703  */
    14704 HRESULT SessionMachine::lockMedia()
    14705 {
    14706     AutoCaller autoCaller(this);
    14707     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14708 
    14709     AutoMultiWriteLock2 alock(this->lockHandle(),
    14710                               &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    14711 
    14712     /* bail out if trying to lock things with already set up locking */
    14713     AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
    14714 
    14715     MultiResult mrc(S_OK);
    14716 
    14717     /* Collect locking information for all medium objects attached to the VM. */
    14718     for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    14719          it != mMediaData->mAttachments.end();
    14720          ++it)
    14721     {
    14722         MediumAttachment* pAtt = *it;
    14723         DeviceType_T devType = pAtt->getType();
    14724         Medium *pMedium = pAtt->getMedium();
    14725 
    14726         MediumLockList *pMediumLockList(new MediumLockList());
    14727         // There can be attachments without a medium (floppy/dvd), and thus
    14728         // it's impossible to create a medium lock list. It still makes sense
    14729         // to have the empty medium lock list in the map in case a medium is
    14730         // attached later.
    14731         if (pMedium != NULL)
    14732         {
    14733             MediumType_T mediumType = pMedium->getType();
    14734             bool fIsReadOnlyLock =    mediumType == MediumType_Readonly
    14735                                    || mediumType == MediumType_Shareable;
    14736             bool fIsVitalImage = (devType == DeviceType_HardDisk);
    14737 
    14738             alock.release();
    14739             mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
    14740                                                 !fIsReadOnlyLock /* fMediumLockWrite */,
    14741                                                 NULL,
    14742                                                 *pMediumLockList);
    14743             alock.acquire();
    14744             if (FAILED(mrc))
    14745             {
    14746                 delete pMediumLockList;
    14747                 mData->mSession.mLockedMedia.Clear();
    14748                 break;
    14749             }
    14750         }
    14751 
    14752         HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
    14753         if (FAILED(rc))
    14754         {
    14755             mData->mSession.mLockedMedia.Clear();
    14756             mrc = setError(rc,
    14757                            tr("Collecting locking information for all attached media failed"));
    14758             break;
    14759         }
    14760     }
    14761 
    14762     if (SUCCEEDED(mrc))
    14763     {
    14764         /* Now lock all media. If this fails, nothing is locked. */
    14765         alock.release();
    14766         HRESULT rc = mData->mSession.mLockedMedia.Lock();
    14767         alock.acquire();
    14768         if (FAILED(rc))
    14769         {
    14770             mrc = setError(rc,
    14771                            tr("Locking of attached media failed"));
    14772         }
    14773     }
    14774 
    14775     return mrc;
    14776 }
    14777 
    14778 /**
    14779  * Undoes the locks made by by #lockMedia().
    14780  */
    14781 void SessionMachine::unlockMedia()
    14782 {
    14783     AutoCaller autoCaller(this);
    14784     AssertComRCReturnVoid(autoCaller.rc());
    14785 
    14786     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    14787 
    14788     /* we may be holding important error info on the current thread;
    14789      * preserve it */
    14790     ErrorInfoKeeper eik;
    14791 
    14792     HRESULT rc = mData->mSession.mLockedMedia.Clear();
    14793     AssertComRC(rc);
    14794 }
    14795 
    14796 /**
    14797  * Helper to change the machine state (reimplementation).
    14798  *
    14799  * @note Locks this object for writing.
    14800  * @note This method must not call saveSettings or SaveSettings, otherwise
    14801  *       it can cause crashes in random places due to unexpectedly committing
    14802  *       the current settings. The caller is responsible for that. The call
    14803  *       to saveStateSettings is fine, because this method does not commit.
    14804  */
    14805 HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
    14806 {
    14807     LogFlowThisFuncEnter();
    14808     LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
    14809 
    14810     AutoCaller autoCaller(this);
    14811     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    14812 
    14813     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    14814 
    14815     MachineState_T oldMachineState = mData->mMachineState;
    14816 
    14817     AssertMsgReturn(oldMachineState != aMachineState,
    14818                     ("oldMachineState=%s, aMachineState=%s\n",
    14819                      Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
    14820                     E_FAIL);
    14821 
    14822     HRESULT rc = S_OK;
    14823 
    14824     int stsFlags = 0;
    14825     bool deleteSavedState = false;
    14826 
    14827     /* detect some state transitions */
    14828 
    14829     if (   (   oldMachineState == MachineState_Saved
    14830             && aMachineState   == MachineState_Restoring)
    14831         || (   (   oldMachineState == MachineState_PoweredOff
    14832                 || oldMachineState == MachineState_Teleported
    14833                 || oldMachineState == MachineState_Aborted
    14834                )
    14835             && (   aMachineState   == MachineState_TeleportingIn
    14836                 || aMachineState   == MachineState_Starting
    14837                )
    14838            )
    14839        )
    14840     {
    14841         /* The EMT thread is about to start */
    14842 
    14843         /* Nothing to do here for now... */
    14844 
    14845         /// @todo NEWMEDIA don't let mDVDDrive and other children
    14846         /// change anything when in the Starting/Restoring state
    14847     }
    14848     else if (   (   oldMachineState == MachineState_Running
    14849                  || oldMachineState == MachineState_Paused
    14850                  || oldMachineState == MachineState_Teleporting
    14851                  || oldMachineState == MachineState_LiveSnapshotting
    14852                  || oldMachineState == MachineState_Stuck
    14853                  || oldMachineState == MachineState_Starting
    14854                  || oldMachineState == MachineState_Stopping
    14855                  || oldMachineState == MachineState_Saving
    14856                  || oldMachineState == MachineState_Restoring
    14857                  || oldMachineState == MachineState_TeleportingPausedVM
    14858                  || oldMachineState == MachineState_TeleportingIn
    14859                  )
    14860              && (   aMachineState == MachineState_PoweredOff
    14861                  || aMachineState == MachineState_Saved
    14862                  || aMachineState == MachineState_Teleported
    14863                  || aMachineState == MachineState_Aborted
    14864                 )
    14865              /* ignore PoweredOff->Saving->PoweredOff transition when taking a
    14866               * snapshot */
    14867              && (   mConsoleTaskData.mSnapshot.isNull()
    14868                  || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
    14869                 )
    14870             )
    14871     {
    14872         /* The EMT thread has just stopped, unlock attached media. Note that as
    14873          * opposed to locking that is done from Console, we do unlocking here
    14874          * because the VM process may have aborted before having a chance to
    14875          * properly unlock all media it locked. */
    14876 
    14877         unlockMedia();
    14878     }
    14879 
    14880     if (oldMachineState == MachineState_Restoring)
    14881     {
    14882         if (aMachineState != MachineState_Saved)
    14883         {
    14884             /*
    14885              *  delete the saved state file once the machine has finished
    14886              *  restoring from it (note that Console sets the state from
    14887              *  Restoring to Saved if the VM couldn't restore successfully,
    14888              *  to give the user an ability to fix an error and retry --
    14889              *  we keep the saved state file in this case)
    14890              */
    14891             deleteSavedState = true;
    14892         }
    14893     }
    14894     else if (   oldMachineState == MachineState_Saved
    14895              && (   aMachineState == MachineState_PoweredOff
    14896                  || aMachineState == MachineState_Aborted
    14897                  || aMachineState == MachineState_Teleported
    14898                 )
    14899             )
    14900     {
    14901         /*
    14902          *  delete the saved state after Console::ForgetSavedState() is called
    14903          *  or if the VM process (owning a direct VM session) crashed while the
    14904          *  VM was Saved
    14905          */
    14906 
    14907         /// @todo (dmik)
    14908         //      Not sure that deleting the saved state file just because of the
    14909         //      client death before it attempted to restore the VM is a good
    14910         //      thing. But when it crashes we need to go to the Aborted state
    14911         //      which cannot have the saved state file associated... The only
    14912         //      way to fix this is to make the Aborted condition not a VM state
    14913         //      but a bool flag: i.e., when a crash occurs, set it to true and
    14914         //      change the state to PoweredOff or Saved depending on the
    14915         //      saved state presence.
    14916 
    14917         deleteSavedState = true;
    14918         mData->mCurrentStateModified = TRUE;
    14919         stsFlags |= SaveSTS_CurStateModified;
    14920     }
    14921 
    14922     if (   aMachineState == MachineState_Starting
    14923         || aMachineState == MachineState_Restoring
    14924         || aMachineState == MachineState_TeleportingIn
    14925        )
    14926     {
    14927         /* set the current state modified flag to indicate that the current
    14928          * state is no more identical to the state in the
    14929          * current snapshot */
    14930         if (!mData->mCurrentSnapshot.isNull())
    14931         {
    14932             mData->mCurrentStateModified = TRUE;
    14933             stsFlags |= SaveSTS_CurStateModified;
    14934         }
    14935     }
    14936 
    14937     if (deleteSavedState)
    14938     {
    14939         if (mRemoveSavedState)
    14940         {
    14941             Assert(!mSSData->strStateFilePath.isEmpty());
    14942 
    14943             // it is safe to delete the saved state file if ...
    14944             if (    !mData->mFirstSnapshot      // ... we have no snapshots or
    14945                  || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
    14946                                                 // ... none of the snapshots share the saved state file
    14947                )
    14948                 RTFileDelete(mSSData->strStateFilePath.c_str());
    14949         }
    14950 
    14951         mSSData->strStateFilePath.setNull();
    14952         stsFlags |= SaveSTS_StateFilePath;
    14953     }
    14954 
    14955     /* redirect to the underlying peer machine */
    14956     mPeer->setMachineState(aMachineState);
    14957 
    14958     if (   aMachineState == MachineState_PoweredOff
    14959         || aMachineState == MachineState_Teleported
    14960         || aMachineState == MachineState_Aborted
    14961         || aMachineState == MachineState_Saved)
    14962     {
    14963         /* the machine has stopped execution
    14964          * (or the saved state file was adopted) */
    14965         stsFlags |= SaveSTS_StateTimeStamp;
    14966     }
    14967 
    14968     if (   (   oldMachineState == MachineState_PoweredOff
    14969             || oldMachineState == MachineState_Aborted
    14970             || oldMachineState == MachineState_Teleported
    14971            )
    14972         && aMachineState == MachineState_Saved)
    14973     {
    14974         /* the saved state file was adopted */
    14975         Assert(!mSSData->strStateFilePath.isEmpty());
    14976         stsFlags |= SaveSTS_StateFilePath;
    14977     }
    14978 
    14979 #ifdef VBOX_WITH_GUEST_PROPS
    14980     if (   aMachineState == MachineState_PoweredOff
    14981         || aMachineState == MachineState_Aborted
    14982         || aMachineState == MachineState_Teleported)
    14983     {
    14984         /* Make sure any transient guest properties get removed from the
    14985          * property store on shutdown. */
    14986 
    14987         HWData::GuestPropertyMap::const_iterator it;
    14988         BOOL fNeedsSaving = mData->mGuestPropertiesModified;
    14989         if (!fNeedsSaving)
    14990             for (it = mHWData->mGuestProperties.begin();
    14991                  it != mHWData->mGuestProperties.end(); ++it)
    14992                 if (   (it->second.mFlags & guestProp::TRANSIENT)
    14993                     || (it->second.mFlags & guestProp::TRANSRESET))
    14994                 {
    14995                     fNeedsSaving = true;
    14996                     break;
    14997                 }
    14998         if (fNeedsSaving)
    14999         {
    15000             mData->mCurrentStateModified = TRUE;
    15001             stsFlags |= SaveSTS_CurStateModified;
    15002         }
    15003     }
    15004 #endif
    15005 
    15006     rc = saveStateSettings(stsFlags);
    15007 
    15008     if (   (   oldMachineState != MachineState_PoweredOff
    15009             && oldMachineState != MachineState_Aborted
    15010             && oldMachineState != MachineState_Teleported
    15011            )
    15012         && (   aMachineState == MachineState_PoweredOff
    15013             || aMachineState == MachineState_Aborted
    15014             || aMachineState == MachineState_Teleported
    15015            )
    15016        )
    15017     {
    15018         /* we've been shut down for any reason */
    15019         /* no special action so far */
    15020     }
    15021 
    15022     LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
    15023     LogFlowThisFuncLeave();
    15024     return rc;
    15025 }
    15026 
    15027 /**
    15028  *  Sends the current machine state value to the VM process.
    15029  *
    15030  *  @note Locks this object for reading, then calls a client process.
    15031  */
    15032 HRESULT SessionMachine::updateMachineStateOnClient()
    15033 {
    15034     AutoCaller autoCaller(this);
    15035     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    15036 
    15037     ComPtr<IInternalSessionControl> directControl;
    15038     {
    15039         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    15040         AssertReturn(!!mData, E_FAIL);
    15041         directControl = mData->mSession.mDirectControl;
    15042 
    15043         /* directControl may be already set to NULL here in #OnSessionEnd()
    15044          * called too early by the direct session process while there is still
    15045          * some operation (like deleting the snapshot) in progress. The client
    15046          * process in this case is waiting inside Session::close() for the
    15047          * "end session" process object to complete, while #uninit() called by
    15048          * #checkForDeath() on the Watcher thread is waiting for the pending
    15049          * operation to complete. For now, we accept this inconsistent behavior
    15050          * and simply do nothing here. */
    15051 
    15052         if (mData->mSession.mState == SessionState_Unlocking)
    15053             return S_OK;
    15054 
    15055         AssertReturn(!directControl.isNull(), E_FAIL);
    15056     }
    15057 
    15058     return directControl->UpdateMachineState(mData->mMachineState);
    15059 }
     210/* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/src-server/ClientWatcher.cpp

    r47555 r47561  
    1 /* $Id$ */
    21/** @file
    3  * Implementation of IVirtualBox in VBoxSVC.
     2 *
     3 * VirtualBox API client crash watcher
    44 */
    55
     
    1717
    1818#include <iprt/asm.h>
    19 #include <iprt/base64.h>
    20 #include <iprt/buildconfig.h>
    21 #include <iprt/cpp/utils.h>
    22 #include <iprt/dir.h>
    23 #include <iprt/env.h>
    24 #include <iprt/file.h>
    25 #include <iprt/path.h>
     19#include <iprt/assert.h>
     20#include <iprt/log.h>
     21#include <iprt/semaphore.h>
    2622#include <iprt/process.h>
    27 #include <iprt/rand.h>
    28 #include <iprt/sha.h>
    29 #include <iprt/string.h>
    30 #include <iprt/stream.h>
    31 #include <iprt/thread.h>
    32 #include <iprt/uuid.h>
    33 #include <iprt/cpp/xml.h>
    34 
    35 #include <VBox/com/com.h>
    36 #include <VBox/com/array.h>
    37 #include "VBox/com/EventQueue.h"
    38 
    39 #include <VBox/err.h>
    40 #include <VBox/param.h>
    41 #include <VBox/settings.h>
    42 #include <VBox/version.h>
    43 
    44 #include <package-generated.h>
    45 
    46 #include <algorithm>
    47 #include <set>
     23
     24#include <VBox/com/defs.h>
     25
    4826#include <vector>
    49 #include <memory> // for auto_ptr
    50 
     27
     28#include "VirtualBoxBase.h"
     29#include "AutoCaller.h"
     30#include "ClientWatcher.h"
     31#include "ClientToken.h"
    5132#include "VirtualBoxImpl.h"
    52 
    53 #include "Global.h"
    5433#include "MachineImpl.h"
    55 #include "MediumImpl.h"
    56 #include "SharedFolderImpl.h"
    57 #include "ProgressImpl.h"
    58 #include "ProgressProxyImpl.h"
    59 #include "HostImpl.h"
    60 #include "USBControllerImpl.h"
    61 #include "SystemPropertiesImpl.h"
    62 #include "GuestOSTypeImpl.h"
    63 #include "NetworkServiceRunner.h"
    64 #include "DHCPServerImpl.h"
    65 #include "NATNetworkImpl.h"
    66 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    67 # include "PerformanceImpl.h"
    68 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    69 #include "EventImpl.h"
    70 #include "VBoxEvents.h"
    71 #ifdef VBOX_WITH_EXTPACK
    72 # include "ExtPackManagerImpl.h"
    73 #endif
    74 #include "AutostartDb.h"
    75 
    76 #include "AutoCaller.h"
    77 #include "Logging.h"
    78 #include "objectslist.h"
    79 
    80 #ifdef RT_OS_WINDOWS
    81 # include "win/svchlp.h"
    82 # include "win/VBoxComEvents.h"
    83 #endif
    84 
    85 ////////////////////////////////////////////////////////////////////////////////
    86 //
    87 // Definitions
    88 //
    89 ////////////////////////////////////////////////////////////////////////////////
    90 
    91 #define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
    92 
    93 ////////////////////////////////////////////////////////////////////////////////
    94 //
    95 // Global variables
    96 //
    97 ////////////////////////////////////////////////////////////////////////////////
    98 
    99 // static
    100 Bstr VirtualBox::sVersion;
    101 
    102 // static
    103 Bstr VirtualBox::sVersionNormalized;
    104 
    105 // static
    106 ULONG VirtualBox::sRevision;
    107 
    108 // static
    109 Bstr VirtualBox::sPackageType;
    110 
    111 // static
    112 Bstr VirtualBox::sAPIVersion;
    11334
    11435#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    115 /** Table for adaptive timeouts in the client watcher. The counter starts at
    116  * the maximum value and decreases to 0. */
    117 static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 };
    118 #endif
    119 
    120 ////////////////////////////////////////////////////////////////////////////////
    121 //
    122 // CallbackEvent class
    123 //
    124 ////////////////////////////////////////////////////////////////////////////////
    125 
    126 /**
    127  *  Abstract callback event class to asynchronously call VirtualBox callbacks
    128  *  on a dedicated event thread. Subclasses reimplement #handleCallback()
    129  *  to call appropriate IVirtualBoxCallback methods depending on the event
    130  *  to be dispatched.
    131  *
    132  *  @note The VirtualBox instance passed to the constructor is strongly
    133  *  referenced, so that the VirtualBox singleton won't be released until the
    134  *  event gets handled by the event thread.
    135  */
    136 class VirtualBox::CallbackEvent : public Event
     36/** Table for adaptive timeouts. After an update the counter starts at the
     37 * maximum value and decreases to 0, i.e. first the short timeouts are used
     38 * and then the longer ones. This minimizes the detection latency in the
     39 * cases where a change is expected, for crashes. */
     40static const RTMSINTERVAL s_aUpdateTimeoutSteps[] = { 500, 200, 100, 50, 20, 10, 5 };
     41#endif
     42
     43
     44
     45VirtualBox::ClientWatcher::ClientWatcher() :
     46    mLock(LOCKCLASS_OBJECTSTATE)
    13747{
    138 public:
    139 
    140     CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat)
    141         : mVirtualBox(aVirtualBox), mWhat(aWhat)
     48    AssertReleaseFailed();
     49}
     50
     51VirtualBox::ClientWatcher::~ClientWatcher()
     52{
     53    if (mThread != NIL_RTTHREAD)
    14254    {
    143         Assert(aVirtualBox);
     55        /* signal the client watcher thread, should be exiting now */
     56        update();
     57        /* wait for termination */
     58        RTThreadWait(mThread, RT_INDEFINITE_WAIT, NULL);
     59        mThread = NIL_RTTHREAD;
    14460    }
    145 
    146     void *handler();
    147 
    148     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
    149 
    150 private:
    151 
    152     /**
    153      *  Note that this is a weak ref -- the CallbackEvent handler thread
    154      *  is bound to the lifetime of the VirtualBox instance, so it's safe.
    155      */
    156     VirtualBox         *mVirtualBox;
    157 protected:
    158     VBoxEventType_T     mWhat;
    159 };
    160 
    161 ////////////////////////////////////////////////////////////////////////////////
    162 //
    163 // VirtualBox private member data definition
    164 //
    165 ////////////////////////////////////////////////////////////////////////////////
    166 
     61    mProcesses.clear();
    16762#if defined(RT_OS_WINDOWS)
    168     #define UPDATEREQARG NULL
    169     #define UPDATEREQTYPE HANDLE
    170 #elif defined(RT_OS_OS2)
    171     #define UPDATEREQARG NIL_RTSEMEVENT
    172     #define UPDATEREQTYPE RTSEMEVENT
    173 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    174     #define UPDATEREQARG
    175     #define UPDATEREQTYPE RTSEMEVENT
    176 #else
    177 # error "Port me!"
    178 #endif
    179 
    180 typedef ObjectsList<Machine> MachinesOList;
    181 typedef ObjectsList<Medium> MediaOList;
    182 typedef ObjectsList<GuestOSType> GuestOSTypesOList;
    183 typedef ObjectsList<SharedFolder> SharedFoldersOList;
    184 typedef ObjectsList<DHCPServer> DHCPServersOList;
    185 typedef ObjectsList<NATNetwork> NATNetworksOList;
    186 
    187 typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
    188 typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
    189 
    190 /**
    191  *  Main VirtualBox data structure.
    192  *  @note |const| members are persistent during lifetime so can be accessed
    193  *  without locking.
    194  */
    195 struct VirtualBox::Data
    196 {
    197     Data()
    198         : pMainConfigFile(NULL),
    199           uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"),
    200           uRegistryNeedsSaving(0),
    201           lockMachines(LOCKCLASS_LISTOFMACHINES),
    202           allMachines(lockMachines),
    203           lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
    204           allGuestOSTypes(lockGuestOSTypes),
    205           lockMedia(LOCKCLASS_LISTOFMEDIA),
    206           allHardDisks(lockMedia),
    207           allDVDImages(lockMedia),
    208           allFloppyImages(lockMedia),
    209           lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
    210           allSharedFolders(lockSharedFolders),
    211           lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
    212           allDHCPServers(lockDHCPServers),
    213           lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS),
    214           allNATNetworks(lockNATNetworks),
    215           mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
    216           updateReq(UPDATEREQARG),
    217           threadClientWatcher(NIL_RTTHREAD),
    218           threadAsyncEvent(NIL_RTTHREAD),
    219           pAsyncEventQ(NULL),
    220           pAutostartDb(NULL),
    221           fSettingsCipherKeySet(false)
     63    if (mUpdateReq != NULL)
    22264    {
     65        ::CloseHandle(mUpdateReq);
     66        mUpdateReq = NULL;
    22367    }
    224 
    225     ~Data()
     68#elif defined(RT_OS_OS2) || defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
     69    if (mUpdateReq != NIL_RTSEMEVENT)
    22670    {
    227         if (pMainConfigFile)
    228         {
    229             delete pMainConfigFile;
    230             pMainConfigFile = NULL;
    231         }
    232     };
    233 
    234     // const data members not requiring locking
    235     const Utf8Str                       strHomeDir;
    236 
    237     // VirtualBox main settings file
    238     const Utf8Str                       strSettingsFilePath;
    239     settings::MainConfigFile            *pMainConfigFile;
    240 
    241     // constant pseudo-machine ID for global media registry
    242     const Guid                          uuidMediaRegistry;
    243 
    244     // counter if global media registry needs saving, updated using atomic
    245     // operations, without requiring any locks
    246     uint64_t                            uRegistryNeedsSaving;
    247 
    248     // const objects not requiring locking
    249     const ComObjPtr<Host>               pHost;
    250     const ComObjPtr<SystemProperties>   pSystemProperties;
    251 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    252     const ComObjPtr<PerformanceCollector> pPerformanceCollector;
    253 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    254 
    255     // Each of the following lists use a particular lock handle that protects the
    256     // list as a whole. As opposed to version 3.1 and earlier, these lists no
    257     // longer need the main VirtualBox object lock, but only the respective list
    258     // lock. In each case, the locking order is defined that the list must be
    259     // requested before object locks of members of the lists (see the order definitions
    260     // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
    261     RWLockHandle                        lockMachines;
    262     MachinesOList                       allMachines;
    263 
    264     RWLockHandle                        lockGuestOSTypes;
    265     GuestOSTypesOList                   allGuestOSTypes;
    266 
    267     // All the media lists are protected by the following locking handle:
    268     RWLockHandle                        lockMedia;
    269     MediaOList                          allHardDisks,           // base images only!
    270                                         allDVDImages,
    271                                         allFloppyImages;
    272     // the hard disks map is an additional map sorted by UUID for quick lookup
    273     // and contains ALL hard disks (base and differencing); it is protected by
    274     // the same lock as the other media lists above
    275     HardDiskMap                         mapHardDisks;
    276 
    277     // list of pending machine renames (also protected by media tree lock;
    278     // see VirtualBox::rememberMachineNameChangeForMedia())
    279     struct PendingMachineRename
    280     {
    281         Utf8Str     strConfigDirOld;
    282         Utf8Str     strConfigDirNew;
    283     };
    284     typedef std::list<PendingMachineRename> PendingMachineRenamesList;
    285     PendingMachineRenamesList           llPendingMachineRenames;
    286 
    287     RWLockHandle                        lockSharedFolders;
    288     SharedFoldersOList                  allSharedFolders;
    289 
    290     RWLockHandle                        lockDHCPServers;
    291     DHCPServersOList                    allDHCPServers;
    292    
    293     RWLockHandle                         lockNATNetworks;
    294     NATNetworksOList                     allNATNetworks;
    295 
    296     RWLockHandle                        mtxProgressOperations;
    297     ProgressMap                         mapProgressOperations;
    298 
    299     // the following are data for the client watcher thread
    300     const UPDATEREQTYPE                 updateReq;
    301 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    302     uint8_t                             updateAdaptCtr;
    303 #endif
    304     const RTTHREAD                      threadClientWatcher;
    305     typedef std::list<RTPROCESS> ProcessList;
    306     ProcessList                         llProcesses;
    307 
    308     // the following are data for the async event thread
    309     const RTTHREAD                      threadAsyncEvent;
    310     EventQueue * const                  pAsyncEventQ;
    311     const ComObjPtr<EventSource>        pEventSource;
    312 
    313 #ifdef VBOX_WITH_EXTPACK
    314     /** The extension pack manager object lives here. */
    315     const ComObjPtr<ExtPackManager>     ptrExtPackManager;
    316 #endif
    317 
    318     /** The global autostart database for the user. */
    319     AutostartDb * const                 pAutostartDb;
    320 
    321     /** Settings secret */
    322     bool                                fSettingsCipherKeySet;
    323     uint8_t                             SettingsCipherKey[RTSHA512_HASH_SIZE];
    324 };
    325 
    326 
    327 // constructor / destructor
    328 /////////////////////////////////////////////////////////////////////////////
    329 
    330 VirtualBox::VirtualBox()
    331 {}
    332 
    333 VirtualBox::~VirtualBox()
    334 {}
    335 
    336 HRESULT VirtualBox::FinalConstruct()
    337 {
    338     LogFlowThisFunc(("\n"));
    339 
    340     HRESULT rc = init();
    341 
    342     BaseFinalConstruct();
    343 
    344     return rc;
    345 }
    346 
    347 void VirtualBox::FinalRelease()
    348 {
    349     LogFlowThisFunc(("\n"));
    350 
    351     uninit();
    352 
    353     BaseFinalRelease();
    354 }
    355 
    356 // public initializer/uninitializer for internal purposes only
    357 /////////////////////////////////////////////////////////////////////////////
    358 
    359 /**
    360  *  Initializes the VirtualBox object.
    361  *
    362  *  @return COM result code
    363  */
    364 HRESULT VirtualBox::init()
    365 {
    366     /* Enclose the state transition NotReady->InInit->Ready */
    367     AutoInitSpan autoInitSpan(this);
    368     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    369 
    370     /* Locking this object for writing during init sounds a bit paradoxical,
    371      * but in the current locking mess this avoids that some code gets a
    372      * read lock and later calls code which wants the same write lock. */
    373     AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
    374 
    375     // allocate our instance data
    376     m = new Data;
    377 
    378     LogFlow(("===========================================================\n"));
    379     LogFlowThisFuncEnter();
    380 
    381     if (sVersion.isEmpty())
    382         sVersion = RTBldCfgVersion();
    383     if (sVersionNormalized.isEmpty())
    384     {
    385         Utf8Str tmp(RTBldCfgVersion());
    386         if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
    387             tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
    388         sVersionNormalized = tmp;
    389     }
    390     sRevision = RTBldCfgRevision();
    391     if (sPackageType.isEmpty())
    392         sPackageType = VBOX_PACKAGE_STRING;
    393     if (sAPIVersion.isEmpty())
    394         sAPIVersion = VBOX_API_VERSION_STRING;
    395     LogFlowThisFunc(("Version: %ls, Package: %ls, API Version: %ls\n", sVersion.raw(), sPackageType.raw(), sAPIVersion.raw()));
    396 
    397     /* Get the VirtualBox home directory. */
    398     {
    399         char szHomeDir[RTPATH_MAX];
    400         int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
    401         if (RT_FAILURE(vrc))
    402             return setError(E_FAIL,
    403                             tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
    404                             szHomeDir, vrc);
    405 
    406         unconst(m->strHomeDir) = szHomeDir;
    407     }
    408 
    409     /* compose the VirtualBox.xml file name */
    410     unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
    411                                                  m->strHomeDir.c_str(),
    412                                                  RTPATH_DELIMITER,
    413                                                  VBOX_GLOBAL_SETTINGS_FILE);
    414     HRESULT rc = S_OK;
    415     bool fCreate = false;
    416     try
    417     {
    418         // load and parse VirtualBox.xml; this will throw on XML or logic errors
    419         try
    420         {
    421             m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
    422         }
    423         catch (xml::EIPRTFailure &e)
    424         {
    425             // this is thrown by the XML backend if the RTOpen() call fails;
    426             // only if the main settings file does not exist, create it,
    427             // if there's something more serious, then do fail!
    428             if (e.rc() == VERR_FILE_NOT_FOUND)
    429                 fCreate = true;
    430             else
    431                 throw;
    432         }
    433 
    434         if (fCreate)
    435             m->pMainConfigFile = new settings::MainConfigFile(NULL);
    436 
    437 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    438         /* create the performance collector object BEFORE host */
    439         unconst(m->pPerformanceCollector).createObject();
    440         rc = m->pPerformanceCollector->init();
    441         ComAssertComRCThrowRC(rc);
    442 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    443 
    444         /* create the host object early, machines will need it */
    445         unconst(m->pHost).createObject();
    446         rc = m->pHost->init(this);
    447         ComAssertComRCThrowRC(rc);
    448 
    449         rc = m->pHost->loadSettings(m->pMainConfigFile->host);
    450         if (FAILED(rc)) throw rc;
    451 
    452         /*
    453          * Create autostart database object early, because the system properties
    454          * might need it.
    455          */
    456         unconst(m->pAutostartDb) = new AutostartDb;
    457 
    458         /* create the system properties object, someone may need it too */
    459         unconst(m->pSystemProperties).createObject();
    460         rc = m->pSystemProperties->init(this);
    461         ComAssertComRCThrowRC(rc);
    462 
    463         rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
    464         if (FAILED(rc)) throw rc;
    465 
    466         /* guest OS type objects, needed by machines */
    467         for (size_t i = 0; i < Global::cOSTypes; ++i)
    468         {
    469             ComObjPtr<GuestOSType> guestOSTypeObj;
    470             rc = guestOSTypeObj.createObject();
    471             if (SUCCEEDED(rc))
    472             {
    473                 rc = guestOSTypeObj->init(Global::sOSTypes[i]);
    474                 if (SUCCEEDED(rc))
    475                     m->allGuestOSTypes.addChild(guestOSTypeObj);
    476             }
    477             ComAssertComRCThrowRC(rc);
    478         }
    479 
    480         /* all registered media, needed by machines */
    481         if (FAILED(rc = initMedia(m->uuidMediaRegistry,
    482                                   m->pMainConfigFile->mediaRegistry,
    483                                   Utf8Str::Empty)))     // const Utf8Str &machineFolder
    484             throw rc;
    485 
    486         /* machines */
    487         if (FAILED(rc = initMachines()))
    488             throw rc;
    489 
    490 #ifdef DEBUG
    491         LogFlowThisFunc(("Dumping media backreferences\n"));
    492         dumpAllBackRefs();
    493 #endif
    494 
    495         /* net services - dhcp services */
    496         for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
    497              it != m->pMainConfigFile->llDhcpServers.end();
    498              ++it)
    499         {
    500             const settings::DHCPServer &data = *it;
    501 
    502             ComObjPtr<DHCPServer> pDhcpServer;
    503             if (SUCCEEDED(rc = pDhcpServer.createObject()))
    504                 rc = pDhcpServer->init(this, data);
    505             if (FAILED(rc)) throw rc;
    506 
    507             rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
    508             if (FAILED(rc)) throw rc;
    509         }
    510 
    511         /* net services - nat networks */
    512         for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
    513              it != m->pMainConfigFile->llNATNetworks.end();
    514              ++it)
    515         {
    516             const settings::NATNetwork &net = *it;
    517 
    518             ComObjPtr<NATNetwork> pNATNetwork;
    519             if (SUCCEEDED(rc = pNATNetwork.createObject()))
    520                 rc = pNATNetwork->init(this, net);
    521             if (FAILED(rc)) throw rc;
    522 
    523             rc = registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
    524             if (FAILED(rc)) throw rc;
    525         }
    526 
    527         /* events */
    528         if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
    529             rc = m->pEventSource->init(static_cast<IVirtualBox*>(this));
    530         if (FAILED(rc)) throw rc;
    531 
    532 #ifdef VBOX_WITH_EXTPACK
    533         /* extension manager */
    534         rc = unconst(m->ptrExtPackManager).createObject();
    535         if (SUCCEEDED(rc))
    536             rc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
    537         if (FAILED(rc))
    538             throw rc;
    539 #endif
    540     }
    541     catch (HRESULT err)
    542     {
    543         /* we assume that error info is set by the thrower */
    544         rc = err;
    545     }
    546     catch (...)
    547     {
    548         rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    549     }
    550 
    551     if (SUCCEEDED(rc))
    552     {
    553         /* start the client watcher thread */
    554 #if defined(RT_OS_WINDOWS)
    555         unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    556 #elif defined(RT_OS_OS2)
    557         RTSemEventCreate(&unconst(m->updateReq));
    558 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    559         RTSemEventCreate(&unconst(m->updateReq));
    560         ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0);
    561 #else
    562 # error "Port me!"
    563 #endif
    564         int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
    565                                  ClientWatcher,
    566                                  (void *)this,
    567                                  0,
    568                                  RTTHREADTYPE_MAIN_WORKER,
    569                                  RTTHREADFLAGS_WAITABLE,
    570                                  "Watcher");
    571         ComAssertRC(vrc);
    572         if (RT_FAILURE(vrc))
    573             rc = E_FAIL;
    574     }
    575 
    576     if (SUCCEEDED(rc))
    577     {
    578         try
    579         {
    580             /* start the async event handler thread */
    581             int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
    582                                      AsyncEventHandler,
    583                                      &unconst(m->pAsyncEventQ),
    584                                      0,
    585                                      RTTHREADTYPE_MAIN_WORKER,
    586                                      RTTHREADFLAGS_WAITABLE,
    587                                      "EventHandler");
    588             ComAssertRCThrow(vrc, E_FAIL);
    589 
    590             /* wait until the thread sets m->pAsyncEventQ */
    591             RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
    592             ComAssertThrow(m->pAsyncEventQ, E_FAIL);
    593         }
    594         catch (HRESULT aRC)
    595         {
    596             rc = aRC;
    597         }
    598     }
    599 
    600     /* Confirm a successful initialization when it's the case */
    601     if (SUCCEEDED(rc))
    602         autoInitSpan.setSucceeded();
    603 
    604 #ifdef VBOX_WITH_EXTPACK
    605     /* Let the extension packs have a go at things. */
    606     if (SUCCEEDED(rc))
    607     {
    608         lock.release();
    609         m->ptrExtPackManager->callAllVirtualBoxReadyHooks();
    610     }
    611 #endif
    612 
    613     LogFlowThisFunc(("rc=%08X\n", rc));
    614     LogFlowThisFuncLeave();
    615     LogFlow(("===========================================================\n"));
    616     return rc;
    617 }
    618 
    619 HRESULT VirtualBox::initMachines()
    620 {
    621     for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
    622          it != m->pMainConfigFile->llMachines.end();
    623          ++it)
    624     {
    625         HRESULT rc = S_OK;
    626         const settings::MachineRegistryEntry &xmlMachine = *it;
    627         Guid uuid = xmlMachine.uuid;
    628 
    629         ComObjPtr<Machine> pMachine;
    630         if (SUCCEEDED(rc = pMachine.createObject()))
    631         {
    632             rc = pMachine->initFromSettings(this,
    633                                             xmlMachine.strSettingsFile,
    634                                             &uuid);
    635             if (SUCCEEDED(rc))
    636                 rc = registerMachine(pMachine);
    637             if (FAILED(rc))
    638                 return rc;
    639         }
    640     }
    641 
    642     return S_OK;
    643 }
    644 
    645 /**
    646  * Loads a media registry from XML and adds the media contained therein to
    647  * the global lists of known media.
    648  *
    649  * This now (4.0) gets called from two locations:
    650  *
    651  *  --  VirtualBox::init(), to load the global media registry from VirtualBox.xml;
    652  *
    653  *  --  Machine::loadMachineDataFromSettings(), to load the per-machine registry
    654  *      from machine XML, for machines created with VirtualBox 4.0 or later.
    655  *
    656  * In both cases, the media found are added to the global lists so the
    657  * global arrays of media (including the GUI's virtual media manager)
    658  * continue to work as before.
    659  *
    660  * @param uuidMachineRegistry The UUID of the media registry. This is either the
    661  *       transient UUID created at VirtualBox startup for the global registry or
    662  *       a machine ID.
    663  * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
    664  *       or a machine XML.
    665  * @return
    666  */
    667 HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
    668                               const settings::MediaRegistry mediaRegistry,
    669                               const Utf8Str &strMachineFolder)
    670 {
    671     LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
    672              uuidRegistry.toString().c_str(),
    673              strMachineFolder.c_str()));
    674 
    675     AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    676 
    677     HRESULT rc = S_OK;
    678     settings::MediaList::const_iterator it;
    679     for (it = mediaRegistry.llHardDisks.begin();
    680          it != mediaRegistry.llHardDisks.end();
    681          ++it)
    682     {
    683         const settings::Medium &xmlHD = *it;
    684 
    685         ComObjPtr<Medium> pHardDisk;
    686         if (SUCCEEDED(rc = pHardDisk.createObject()))
    687             rc = pHardDisk->init(this,
    688                                  NULL,          // parent
    689                                  DeviceType_HardDisk,
    690                                  uuidRegistry,
    691                                  xmlHD,         // XML data; this recurses to processes the children
    692                                  strMachineFolder);
    693         if (FAILED(rc)) return rc;
    694 
    695         rc = registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk);
    696         if (FAILED(rc)) return rc;
    697     }
    698 
    699     for (it = mediaRegistry.llDvdImages.begin();
    700          it != mediaRegistry.llDvdImages.end();
    701          ++it)
    702     {
    703         const settings::Medium &xmlDvd = *it;
    704 
    705         ComObjPtr<Medium> pImage;
    706         if (SUCCEEDED(pImage.createObject()))
    707             rc = pImage->init(this,
    708                               NULL,
    709                               DeviceType_DVD,
    710                               uuidRegistry,
    711                               xmlDvd,
    712                               strMachineFolder);
    713         if (FAILED(rc)) return rc;
    714 
    715         rc = registerMedium(pImage, &pImage, DeviceType_DVD);
    716         if (FAILED(rc)) return rc;
    717     }
    718 
    719     for (it = mediaRegistry.llFloppyImages.begin();
    720          it != mediaRegistry.llFloppyImages.end();
    721          ++it)
    722     {
    723         const settings::Medium &xmlFloppy = *it;
    724 
    725         ComObjPtr<Medium> pImage;
    726         if (SUCCEEDED(pImage.createObject()))
    727             rc = pImage->init(this,
    728                               NULL,
    729                               DeviceType_Floppy,
    730                               uuidRegistry,
    731                               xmlFloppy,
    732                               strMachineFolder);
    733         if (FAILED(rc)) return rc;
    734 
    735         rc = registerMedium(pImage, &pImage, DeviceType_Floppy);
    736         if (FAILED(rc)) return rc;
    737     }
    738 
    739     LogFlow(("VirtualBox::initMedia LEAVING\n"));
    740 
    741     return S_OK;
    742 }
    743 
    744 void VirtualBox::uninit()
    745 {
    746     Assert(!m->uRegistryNeedsSaving);
    747     if (m->uRegistryNeedsSaving)
    748         saveSettings();
    749 
    750     /* Enclose the state transition Ready->InUninit->NotReady */
    751     AutoUninitSpan autoUninitSpan(this);
    752     if (autoUninitSpan.uninitDone())
    753         return;
    754 
    755     LogFlow(("===========================================================\n"));
    756     LogFlowThisFuncEnter();
    757     LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
    758 
    759     /* tell all our child objects we've been uninitialized */
    760 
    761     LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
    762     if (m->pHost)
    763     {
    764         /* It is necessary to hold the VirtualBox and Host locks here because
    765            we may have to uninitialize SessionMachines. */
    766         AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
    767         m->allMachines.uninitAll();
    768     }
    769     else
    770         m->allMachines.uninitAll();
    771     m->allFloppyImages.uninitAll();
    772     m->allDVDImages.uninitAll();
    773     m->allHardDisks.uninitAll();
    774     m->allDHCPServers.uninitAll();
    775 
    776     m->mapProgressOperations.clear();
    777 
    778     m->allGuestOSTypes.uninitAll();
    779 
    780     /* Note that we release singleton children after we've all other children.
    781      * In some cases this is important because these other children may use
    782      * some resources of the singletons which would prevent them from
    783      * uninitializing (as for example, mSystemProperties which owns
    784      * MediumFormat objects which Medium objects refer to) */
    785     if (m->pSystemProperties)
    786     {
    787         m->pSystemProperties->uninit();
    788         unconst(m->pSystemProperties).setNull();
    789     }
    790 
    791     if (m->pHost)
    792     {
    793         m->pHost->uninit();
    794         unconst(m->pHost).setNull();
    795     }
    796 
    797 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    798     if (m->pPerformanceCollector)
    799     {
    800         m->pPerformanceCollector->uninit();
    801         unconst(m->pPerformanceCollector).setNull();
    802     }
    803 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    804 
    805     LogFlowThisFunc(("Terminating the async event handler...\n"));
    806     if (m->threadAsyncEvent != NIL_RTTHREAD)
    807     {
    808         /* signal to exit the event loop */
    809         if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
    810         {
    811             /*
    812              *  Wait for thread termination (only after we've successfully
    813              *  interrupted the event queue processing!)
    814              */
    815             int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
    816             if (RT_FAILURE(vrc))
    817                 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
    818                                 m->threadAsyncEvent, vrc));
    819         }
    820         else
    821         {
    822             AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
    823             RTThreadWait(m->threadAsyncEvent, 0, NULL);
    824         }
    825 
    826         unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
    827         unconst(m->pAsyncEventQ) = NULL;
    828     }
    829 
    830     LogFlowThisFunc(("Releasing event source...\n"));
    831     if (m->pEventSource)
    832     {
    833         // we don't perform uninit() as it's possible that some pending event refers to this source
    834         unconst(m->pEventSource).setNull();
    835     }
    836 
    837     LogFlowThisFunc(("Terminating the client watcher...\n"));
    838     if (m->threadClientWatcher != NIL_RTTHREAD)
    839     {
    840         /* signal the client watcher thread */
    841         updateClientWatcher();
    842         /* wait for the termination */
    843         RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
    844         unconst(m->threadClientWatcher) = NIL_RTTHREAD;
    845     }
    846     m->llProcesses.clear();
    847 #if defined(RT_OS_WINDOWS)
    848     if (m->updateReq != NULL)
    849     {
    850         ::CloseHandle(m->updateReq);
    851         unconst(m->updateReq) = NULL;
    852     }
    853 #elif defined(RT_OS_OS2)
    854     if (m->updateReq != NIL_RTSEMEVENT)
    855     {
    856         RTSemEventDestroy(m->updateReq);
    857         unconst(m->updateReq) = NIL_RTSEMEVENT;
    858     }
    859 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    860     if (m->updateReq != NIL_RTSEMEVENT)
    861     {
    862         RTSemEventDestroy(m->updateReq);
    863         unconst(m->updateReq) = NIL_RTSEMEVENT;
     71        RTSemEventDestroy(mUpdateReq);
     72        mUpdateReq = NIL_RTSEMEVENT;
    86473    }
    86574#else
    86675# error "Port me!"
    86776#endif
    868 
    869     delete m->pAutostartDb;
    870 
    871     // clean up our instance data
    872     delete m;
    873 
    874     /* Unload hard disk plugin backends. */
    875     VDShutdown();
    876 
    877     LogFlowThisFuncLeave();
    878     LogFlow(("===========================================================\n"));
    87977}
    88078
    881 // IVirtualBox properties
    882 /////////////////////////////////////////////////////////////////////////////
    883 
    884 STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
     79VirtualBox::ClientWatcher::ClientWatcher(const ComObjPtr<VirtualBox> &pVirtualBox) :
     80    mVirtualBox(pVirtualBox),
     81    mThread(NIL_RTTHREAD),
     82    mUpdateReq(CWUPDATEREQARG),
     83    mLock(LOCKCLASS_OBJECTSTATE)
    88584{
    886     CheckComArgNotNull(aVersion);
    887 
    888     AutoCaller autoCaller(this);
    889     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    890 
    891     sVersion.cloneTo(aVersion);
    892     return S_OK;
     85#if defined(RT_OS_WINDOWS)
     86    mUpdateReq = ::CreateEvent(NULL, FALSE, FALSE, NULL);
     87#elif defined(RT_OS_OS2)
     88    RTSemEventCreate(&mUpdateReq);
     89#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
     90    RTSemEventCreate(&mUpdateReq);
     91    /* start with high timeouts, nothing to do */
     92    ASMAtomicUoWriteU8(&mUpdateAdaptCtr, 0);
     93#else
     94# error "Port me!"
     95#endif
     96
     97    int vrc = RTThreadCreate(&mThread,
     98                             worker,
     99                             (void *)this,
     100                             0,
     101                             RTTHREADTYPE_MAIN_WORKER,
     102                             RTTHREADFLAGS_WAITABLE,
     103                             "Watcher");
     104    AssertRC(vrc);
    893105}
    894106
    895 STDMETHODIMP VirtualBox::COMGETTER(VersionNormalized)(BSTR *aVersionNormalized)
     107bool VirtualBox::ClientWatcher::isReady()
    896108{
    897     CheckComArgNotNull(aVersionNormalized);
    898 
    899     AutoCaller autoCaller(this);
    900     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    901 
    902     sVersionNormalized.cloneTo(aVersionNormalized);
    903     return S_OK;
     109    return mThread != NIL_RTTHREAD;
    904110}
    905111
    906 STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
     112/**
     113 * Sends a signal to the thread to rescan the clients/VMs having open sessions.
     114 */
     115void VirtualBox::ClientWatcher::update()
    907116{
    908     CheckComArgNotNull(aRevision);
    909 
    910     AutoCaller autoCaller(this);
    911     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    912 
    913     *aRevision = sRevision;
    914     return S_OK;
     117    AssertReturnVoid(mThread != NIL_RTTHREAD);
     118
     119    /* sent an update request */
     120#if defined(RT_OS_WINDOWS)
     121    ::SetEvent(mUpdateReq);
     122#elif defined(RT_OS_OS2)
     123    RTSemEventSignal(mUpdateReq);
     124#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
     125    /* use short timeouts, as we expect changes */
     126    ASMAtomicUoWriteU8(&mUpdateAdaptCtr, RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
     127    RTSemEventSignal(mUpdateReq);
     128#else
     129# error "Port me!"
     130#endif
    915131}
    916132
    917 STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
     133/**
     134 * Adds a process to the list of processes to be reaped. This call should be
     135 * followed by a call to update() to cause the necessary actions immediately,
     136 * in case the process crashes straight away.
     137 */
     138void VirtualBox::ClientWatcher::addProcess(RTPROCESS pid)
    918139{
    919     CheckComArgNotNull(aPackageType);
    920 
    921     AutoCaller autoCaller(this);
    922     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    923 
    924     sPackageType.cloneTo(aPackageType);
    925     return S_OK;
     140    AssertReturnVoid(mThread != NIL_RTTHREAD);
     141    /* @todo r=klaus, do the reaping on all platforms! */
     142#ifndef RT_OS_WINDOWS
     143    AutoWriteLock alock(mLock COMMA_LOCKVAL_SRC_POS);
     144    mProcesses.push_back(pid);
     145#endif
    926146}
    927147
    928 STDMETHODIMP VirtualBox::COMGETTER(APIVersion)(BSTR *aAPIVersion)
    929 {
    930     CheckComArgNotNull(aAPIVersion);
    931 
    932     AutoCaller autoCaller(this);
    933     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    934 
    935     sAPIVersion.cloneTo(aAPIVersion);
    936     return S_OK;
    937 }
    938 
    939 STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
    940 {
    941     CheckComArgNotNull(aHomeFolder);
    942 
    943     AutoCaller autoCaller(this);
    944     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    945 
    946     /* mHomeDir is const and doesn't need a lock */
    947     m->strHomeDir.cloneTo(aHomeFolder);
    948     return S_OK;
    949 }
    950 
    951 STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
    952 {
    953     CheckComArgNotNull(aSettingsFilePath);
    954 
    955     AutoCaller autoCaller(this);
    956     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    957 
    958     /* mCfgFile.mName is const and doesn't need a lock */
    959     m->strSettingsFilePath.cloneTo(aSettingsFilePath);
    960     return S_OK;
    961 }
    962 
    963 STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
    964 {
    965     CheckComArgOutPointerValid(aHost);
    966 
    967     AutoCaller autoCaller(this);
    968     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    969 
    970     /* mHost is const, no need to lock */
    971     m->pHost.queryInterfaceTo(aHost);
    972     return S_OK;
    973 }
    974 
    975 STDMETHODIMP
    976 VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
    977 {
    978     CheckComArgOutPointerValid(aSystemProperties);
    979 
    980     AutoCaller autoCaller(this);
    981     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    982 
    983     /* mSystemProperties is const, no need to lock */
    984     m->pSystemProperties.queryInterfaceTo(aSystemProperties);
    985     return S_OK;
    986 }
    987 
    988 STDMETHODIMP
    989 VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
    990 {
    991     CheckComArgOutSafeArrayPointerValid(aMachines);
    992 
    993     AutoCaller autoCaller(this);
    994     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    995 
    996     AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    997     SafeIfaceArray<IMachine> machines(m->allMachines.getList());
    998     machines.detachTo(ComSafeArrayOutArg(aMachines));
    999 
    1000     return S_OK;
    1001 }
    1002 
    1003 STDMETHODIMP
    1004 VirtualBox::COMGETTER(MachineGroups)(ComSafeArrayOut(BSTR, aMachineGroups))
    1005 {
    1006     CheckComArgOutSafeArrayPointerValid(aMachineGroups);
    1007 
    1008     AutoCaller autoCaller(this);
    1009     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1010 
    1011     std::list<Bstr> allGroups;
    1012 
    1013     /* get copy of all machine references, to avoid holding the list lock */
    1014     MachinesOList::MyList allMachines;
    1015     {
    1016         AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1017         allMachines = m->allMachines.getList();
    1018     }
    1019     for (MachinesOList::MyList::const_iterator it = allMachines.begin();
    1020          it != allMachines.end();
    1021          ++it)
    1022     {
    1023         const ComObjPtr<Machine> &pMachine = *it;
    1024         AutoCaller autoMachineCaller(pMachine);
    1025         if (FAILED(autoMachineCaller.rc()))
    1026             continue;
    1027         AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
    1028 
    1029         if (pMachine->isAccessible())
    1030         {
    1031             const StringsList &thisGroups = pMachine->getGroups();
    1032             for (StringsList::const_iterator it2 = thisGroups.begin();
    1033                  it2 != thisGroups.end();
    1034                  ++it2)
    1035                 allGroups.push_back(*it2);
    1036         }
    1037     }
    1038 
    1039     /* throw out any duplicates */
    1040     allGroups.sort();
    1041     allGroups.unique();
    1042     com::SafeArray<BSTR> machineGroups(allGroups.size());
    1043     size_t i = 0;
    1044     for (std::list<Bstr>::const_iterator it = allGroups.begin();
    1045          it != allGroups.end();
    1046          ++it, i++)
    1047     {
    1048         const Bstr &tmp = *it;
    1049         tmp.cloneTo(&machineGroups[i]);
    1050     }
    1051     machineGroups.detachTo(ComSafeArrayOutArg(aMachineGroups));
    1052 
    1053     return S_OK;
    1054 }
    1055 
    1056 STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
    1057 {
    1058     CheckComArgOutSafeArrayPointerValid(aHardDisks);
    1059 
    1060     AutoCaller autoCaller(this);
    1061     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1062 
    1063     AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1064     SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());
    1065     hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
    1066 
    1067     return S_OK;
    1068 }
    1069 
    1070 STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
    1071 {
    1072     CheckComArgOutSafeArrayPointerValid(aDVDImages);
    1073 
    1074     AutoCaller autoCaller(this);
    1075     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1076 
    1077     AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1078     SafeIfaceArray<IMedium> images(m->allDVDImages.getList());
    1079     images.detachTo(ComSafeArrayOutArg(aDVDImages));
    1080 
    1081     return S_OK;
    1082 }
    1083 
    1084 STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
    1085 {
    1086     CheckComArgOutSafeArrayPointerValid(aFloppyImages);
    1087 
    1088     AutoCaller autoCaller(this);
    1089     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1090 
    1091     AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1092     SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());
    1093     images.detachTo(ComSafeArrayOutArg(aFloppyImages));
    1094 
    1095     return S_OK;
    1096 }
    1097 
    1098 STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
    1099 {
    1100     CheckComArgOutPointerValid(aOperations);
    1101 
    1102     AutoCaller autoCaller(this);
    1103     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1104 
    1105     /* protect mProgressOperations */
    1106     AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
    1107     SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
    1108     progress.detachTo(ComSafeArrayOutArg(aOperations));
    1109 
    1110     return S_OK;
    1111 }
    1112 
    1113 STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
    1114 {
    1115     CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
    1116 
    1117     AutoCaller autoCaller(this);
    1118     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1119 
    1120     AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1121     SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());
    1122     ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
    1123 
    1124     return S_OK;
    1125 }
    1126 
    1127 STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
    1128 {
    1129 #ifndef RT_OS_WINDOWS
    1130     NOREF(aSharedFoldersSize);
    1131 #endif /* RT_OS_WINDOWS */
    1132 
    1133     CheckComArgOutSafeArrayPointerValid(aSharedFolders);
    1134 
    1135     AutoCaller autoCaller(this);
    1136     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1137 
    1138     return setError(E_NOTIMPL, "Not yet implemented");
    1139 }
    1140 
    1141 STDMETHODIMP
    1142 VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
    1143 {
    1144 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    1145     CheckComArgOutPointerValid(aPerformanceCollector);
    1146 
    1147     AutoCaller autoCaller(this);
    1148     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1149 
    1150     /* mPerformanceCollector is const, no need to lock */
    1151     m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
    1152 
    1153     return S_OK;
    1154 #else /* !VBOX_WITH_RESOURCE_USAGE_API */
    1155     ReturnComNotImplemented();
    1156 #endif /* !VBOX_WITH_RESOURCE_USAGE_API */
    1157 }
    1158 
    1159 STDMETHODIMP
    1160 VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
    1161 {
    1162     CheckComArgOutSafeArrayPointerValid(aDHCPServers);
    1163 
    1164     AutoCaller autoCaller(this);
    1165     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1166 
    1167     AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1168     SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());
    1169     svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
    1170 
    1171     return S_OK;
    1172 }
    1173 
    1174 
    1175 STDMETHODIMP
    1176 VirtualBox::COMGETTER(NATNetworks)(ComSafeArrayOut(INATNetwork *, aNATNetworks))
    1177 {
    1178 #ifdef VBOX_WITH_NAT_SERVICE
    1179     CheckComArgOutSafeArrayPointerValid(aNATNetworks);
    1180 
    1181     AutoCaller autoCaller(this);
    1182     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1183 
    1184     AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1185     SafeIfaceArray<INATNetwork> nets(m->allNATNetworks.getList());
    1186     nets.detachTo(ComSafeArrayOutArg(aNATNetworks));
    1187 
    1188     return S_OK;
    1189 #else
    1190     NOREF(aNATNetworks);
    1191 # ifndef RT_OS_WINDOWS
    1192     NOREF(aNATNetworksSize);
    1193 # endif
    1194     return E_NOTIMPL;
    1195 #endif
    1196 }
    1197 
    1198 
    1199 STDMETHODIMP
    1200 VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
    1201 {
    1202     CheckComArgOutPointerValid(aEventSource);
    1203 
    1204     AutoCaller autoCaller(this);
    1205     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1206 
    1207     /* event source is const, no need to lock */
    1208     m->pEventSource.queryInterfaceTo(aEventSource);
    1209 
    1210     return S_OK;
    1211 }
    1212 
    1213 STDMETHODIMP
    1214 VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)
    1215 {
    1216     CheckComArgOutPointerValid(aExtPackManager);
    1217 
    1218     AutoCaller autoCaller(this);
    1219     HRESULT hrc = autoCaller.rc();
    1220     if (SUCCEEDED(hrc))
    1221     {
    1222 #ifdef VBOX_WITH_EXTPACK
    1223         /* The extension pack manager is const, no need to lock. */
    1224         hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);
    1225 #else
    1226         hrc = E_NOTIMPL;
    1227 #endif
    1228     }
    1229 
    1230     return hrc;
    1231 }
    1232 
    1233 STDMETHODIMP VirtualBox::COMGETTER(InternalNetworks)(ComSafeArrayOut(BSTR, aInternalNetworks))
    1234 {
    1235     CheckComArgOutSafeArrayPointerValid(aInternalNetworks);
    1236 
    1237     AutoCaller autoCaller(this);
    1238     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1239 
    1240     std::list<Bstr> allInternalNetworks;
    1241 
    1242     /* get copy of all machine references, to avoid holding the list lock */
    1243     MachinesOList::MyList allMachines;
    1244     {
    1245         AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1246         allMachines = m->allMachines.getList();
    1247     }
    1248     for (MachinesOList::MyList::const_iterator it = allMachines.begin();
    1249          it != allMachines.end();
    1250          ++it)
    1251     {
    1252         const ComObjPtr<Machine> &pMachine = *it;
    1253         AutoCaller autoMachineCaller(pMachine);
    1254         if (FAILED(autoMachineCaller.rc()))
    1255             continue;
    1256         AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
    1257 
    1258         if (pMachine->isAccessible())
    1259         {
    1260             uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
    1261             for (ULONG i = 0; i < cNetworkAdapters; i++)
    1262             {
    1263                 ComPtr<INetworkAdapter> pNet;
    1264                 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
    1265                 if (FAILED(rc) || pNet.isNull())
    1266                     continue;
    1267                 Bstr strInternalNetwork;
    1268                 rc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
    1269                 if (FAILED(rc) || strInternalNetwork.isEmpty())
    1270                     continue;
    1271 
    1272                 allInternalNetworks.push_back(strInternalNetwork);
    1273             }
    1274         }
    1275     }
    1276 
    1277     /* throw out any duplicates */
    1278     allInternalNetworks.sort();
    1279     allInternalNetworks.unique();
    1280     com::SafeArray<BSTR> internalNetworks(allInternalNetworks.size());
    1281     size_t i = 0;
    1282     for (std::list<Bstr>::const_iterator it = allInternalNetworks.begin();
    1283          it != allInternalNetworks.end();
    1284          ++it, i++)
    1285     {
    1286         const Bstr &tmp = *it;
    1287         tmp.cloneTo(&internalNetworks[i]);
    1288     }
    1289     internalNetworks.detachTo(ComSafeArrayOutArg(aInternalNetworks));
    1290 
    1291     return S_OK;
    1292 }
    1293 
    1294 STDMETHODIMP VirtualBox::COMGETTER(GenericNetworkDrivers)(ComSafeArrayOut(BSTR, aGenericNetworkDrivers))
    1295 {
    1296     CheckComArgOutSafeArrayPointerValid(aGenericNetworkDrivers);
    1297 
    1298     AutoCaller autoCaller(this);
    1299     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1300 
    1301     std::list<Bstr> allGenericNetworkDrivers;
    1302 
    1303     /* get copy of all machine references, to avoid holding the list lock */
    1304     MachinesOList::MyList allMachines;
    1305     {
    1306         AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1307         allMachines = m->allMachines.getList();
    1308     }
    1309     for (MachinesOList::MyList::const_iterator it = allMachines.begin();
    1310          it != allMachines.end();
    1311          ++it)
    1312     {
    1313         const ComObjPtr<Machine> &pMachine = *it;
    1314         AutoCaller autoMachineCaller(pMachine);
    1315         if (FAILED(autoMachineCaller.rc()))
    1316             continue;
    1317         AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
    1318 
    1319         if (pMachine->isAccessible())
    1320         {
    1321             uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
    1322             for (ULONG i = 0; i < cNetworkAdapters; i++)
    1323             {
    1324                 ComPtr<INetworkAdapter> pNet;
    1325                 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
    1326                 if (FAILED(rc) || pNet.isNull())
    1327                     continue;
    1328                 Bstr strGenericNetworkDriver;
    1329                 rc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
    1330                 if (FAILED(rc) || strGenericNetworkDriver.isEmpty())
    1331                     continue;
    1332 
    1333                 allGenericNetworkDrivers.push_back(strGenericNetworkDriver);
    1334             }
    1335         }
    1336     }
    1337 
    1338     /* throw out any duplicates */
    1339     allGenericNetworkDrivers.sort();
    1340     allGenericNetworkDrivers.unique();
    1341     com::SafeArray<BSTR> genericNetworks(allGenericNetworkDrivers.size());
    1342     size_t i = 0;
    1343     for (std::list<Bstr>::const_iterator it = allGenericNetworkDrivers.begin();
    1344          it != allGenericNetworkDrivers.end();
    1345          ++it, i++)
    1346     {
    1347         const Bstr &tmp = *it;
    1348         tmp.cloneTo(&genericNetworks[i]);
    1349     }
    1350     genericNetworks.detachTo(ComSafeArrayOutArg(aGenericNetworkDrivers));
    1351 
    1352     return S_OK;
    1353 }
    1354 
    1355 STDMETHODIMP
    1356 VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
    1357                                  IN_BSTR        aVersion,
    1358                                  BSTR           *aUrl,
    1359                                  BSTR           *aFile,
    1360                                  BOOL           *aResult)
    1361 {
    1362     CheckComArgNotNull(aResult);
    1363 
    1364     AutoCaller autoCaller(this);
    1365     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1366 
    1367     NOREF(aVersion);
    1368 
    1369     static const struct
    1370     {
    1371         FirmwareType_T type;
    1372         const char*    fileName;
    1373         const char*    url;
    1374     }
    1375     firmwareDesc[] =
    1376     {
    1377         {
    1378             /* compiled-in firmware */
    1379             FirmwareType_BIOS,    NULL,             NULL
    1380         },
    1381         {
    1382             FirmwareType_EFI32,   "VBoxEFI32.fd",   "http://virtualbox.org/firmware/VBoxEFI32.fd"
    1383         },
    1384         {
    1385             FirmwareType_EFI64,   "VBoxEFI64.fd",   "http://virtualbox.org/firmware/VBoxEFI64.fd"
    1386         },
    1387         {
    1388             FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
    1389         }
    1390     };
    1391 
    1392     for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
    1393     {
    1394         if (aFirmwareType != firmwareDesc[i].type)
    1395             continue;
    1396 
    1397         /* compiled-in firmware */
    1398         if (firmwareDesc[i].fileName == NULL)
    1399         {
    1400             *aResult = TRUE;
    1401             break;
    1402         }
    1403 
    1404         Utf8Str shortName, fullName;
    1405 
    1406         shortName = Utf8StrFmt("Firmware%c%s",
    1407                                RTPATH_DELIMITER,
    1408                                firmwareDesc[i].fileName);
    1409         int rc = calculateFullPath(shortName, fullName);
    1410         AssertRCReturn(rc, rc);
    1411         if (RTFileExists(fullName.c_str()))
    1412         {
    1413             *aResult = TRUE;
    1414             if (aFile)
    1415                 Utf8Str(fullName).cloneTo(aFile);
    1416             break;
    1417         }
    1418 
    1419         char pszVBoxPath[RTPATH_MAX];
    1420         rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX);
    1421         AssertRCReturn(rc, rc);
    1422         fullName = Utf8StrFmt("%s%c%s",
    1423                               pszVBoxPath,
    1424                               RTPATH_DELIMITER,
    1425                               firmwareDesc[i].fileName);
    1426         if (RTFileExists(fullName.c_str()))
    1427         {
    1428             *aResult = TRUE;
    1429             if (aFile)
    1430                 Utf8Str(fullName).cloneTo(aFile);
    1431             break;
    1432         }
    1433 
    1434         /** @todo: account for version in the URL */
    1435         if (aUrl != NULL)
    1436         {
    1437             Utf8Str strUrl(firmwareDesc[i].url);
    1438             strUrl.cloneTo(aUrl);
    1439         }
    1440         *aResult = FALSE;
    1441 
    1442         /* Assume single record per firmware type */
    1443         break;
    1444     }
    1445 
    1446     return S_OK;
    1447 }
    1448 // IVirtualBox methods
    1449 /////////////////////////////////////////////////////////////////////////////
    1450 
    1451 /* Helper for VirtualBox::ComposeMachineFilename */
    1452 static void sanitiseMachineFilename(Utf8Str &aName);
    1453 
    1454 STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,
    1455                                                 IN_BSTR aGroup,
    1456                                                 IN_BSTR aCreateFlags,
    1457                                                 IN_BSTR aBaseFolder,
    1458                                                 BSTR *aFilename)
    1459 {
    1460     LogFlowThisFuncEnter();
    1461     LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));
    1462 
    1463     CheckComArgStrNotEmptyOrNull(aName);
    1464     CheckComArgOutPointerValid(aFilename);
    1465 
    1466     AutoCaller autoCaller(this);
    1467     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1468 
    1469     Utf8Str strCreateFlags(aCreateFlags);
    1470     Guid id;
    1471     bool fDirectoryIncludesUUID = false;
    1472     if (!strCreateFlags.isEmpty())
    1473     {
    1474         const char *pcszNext = strCreateFlags.c_str();
    1475         while (*pcszNext != '\0')
    1476         {
    1477             Utf8Str strFlag;
    1478             const char *pcszComma = RTStrStr(pcszNext, ",");
    1479             if (!pcszComma)
    1480                 strFlag = pcszNext;
    1481             else
    1482                 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
    1483 
    1484             const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
    1485             /* skip over everything which doesn't contain '=' */
    1486             if (pcszEqual && pcszEqual != strFlag.c_str())
    1487             {
    1488                 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
    1489                 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
    1490 
    1491                 if (strKey == "UUID")
    1492                     id = strValue.c_str();
    1493                 else if (strKey == "directoryIncludesUUID")
    1494                     fDirectoryIncludesUUID = (strValue == "1");
    1495             }
    1496 
    1497             if (!pcszComma)
    1498                 pcszNext += strFlag.length();
    1499             else
    1500                 pcszNext += strFlag.length() + 1;
    1501         }
    1502     }
    1503 
    1504     if (id.isZero())
    1505         fDirectoryIncludesUUID = false;
    1506     else if (!id.isValid())
    1507     {
    1508         /* do something else */
    1509         return setError(E_INVALIDARG,
    1510                  tr("'%ls' is not a valid Guid"),
    1511                  id.toStringCurly().c_str());
    1512     }
    1513 
    1514     Utf8Str strGroup(aGroup);
    1515     if (strGroup.isEmpty())
    1516         strGroup = "/";
    1517     HRESULT rc = validateMachineGroup(strGroup, true);
    1518     if (FAILED(rc))
    1519         return rc;
    1520 
    1521     /* Compose the settings file name using the following scheme:
    1522      *
    1523      *     <base_folder><group>/<machine_name>/<machine_name>.xml
    1524      *
    1525      * If a non-null and non-empty base folder is specified, the default
    1526      * machine folder will be used as a base folder.
    1527      * We sanitise the machine name to a safe white list of characters before
    1528      * using it.
    1529      */
    1530     Utf8Str strBase = aBaseFolder;
    1531     Utf8Str strName = aName;
    1532     Utf8Str strDirName(strName);
    1533     if (fDirectoryIncludesUUID)
    1534         strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
    1535     sanitiseMachineFilename(strName);
    1536     sanitiseMachineFilename(strDirName);
    1537 
    1538     if (strBase.isEmpty())
    1539         /* we use the non-full folder value below to keep the path relative */
    1540         getDefaultMachineFolder(strBase);
    1541 
    1542     calculateFullPath(strBase, strBase);
    1543 
    1544     /* eliminate toplevel group to avoid // in the result */
    1545     if (strGroup == "/")
    1546         strGroup.setNull();
    1547     Bstr bstrSettingsFile = BstrFmt("%s%s%c%s%c%s.vbox",
    1548                                     strBase.c_str(),
    1549                                     strGroup.c_str(),
    1550                                     RTPATH_DELIMITER,
    1551                                     strDirName.c_str(),
    1552                                     RTPATH_DELIMITER,
    1553                                     strName.c_str());
    1554 
    1555     bstrSettingsFile.detachTo(aFilename);
    1556 
    1557     return S_OK;
    1558 }
    1559 
    1560148/**
    1561  * Remove characters from a machine file name which can be problematic on
    1562  * particular systems.
    1563  * @param  strName  The file name to sanitise.
     149 * Thread worker function that watches the termination of all client processes
     150 * that have open sessions using IMachine::LockMachine()
    1564151 */
    1565 void sanitiseMachineFilename(Utf8Str &strName)
    1566 {
    1567     /** Set of characters which should be safe for use in filenames: some basic
    1568      * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul.  We try to
    1569      * skip anything that could count as a control character in Windows or
    1570      * *nix, or be otherwise difficult for shells to handle (I would have
    1571      * preferred to remove the space and brackets too).  We also remove all
    1572      * characters which need UTF-16 surrogate pairs for Windows's benefit. */
    1573     RTUNICP aCpSet[] =
    1574         { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_',
    1575           0xa0, 0xd7af, '\0' };
    1576     char *pszName = strName.mutableRaw();
    1577     int cReplacements = RTStrPurgeComplementSet(pszName, aCpSet, '_');
    1578     Assert(cReplacements >= 0);
    1579     NOREF(cReplacements);
    1580     /* No leading dot or dash. */
    1581     if (pszName[0] == '.' || pszName[0] == '-')
    1582         pszName[0] = '_';
    1583     /* No trailing dot. */
    1584     if (pszName[strName.length() - 1] == '.')
    1585         pszName[strName.length() - 1] = '_';
    1586     /* Mangle leading and trailing spaces. */
    1587     for (size_t i = 0; pszName[i] == ' '; ++i)
    1588        pszName[i] = '_';
    1589     for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
    1590        pszName[i] = '_';
    1591 }
    1592 
    1593 #ifdef DEBUG
    1594 /** Simple unit test/operation examples for sanitiseMachineFilename(). */
    1595 static unsigned testSanitiseMachineFilename(void (*pfnPrintf)(const char *, ...))
    1596 {
    1597     unsigned cErrors = 0;
    1598 
    1599     /** Expected results of sanitising given file names. */
    1600     static struct
    1601     {
    1602         /** The test file name to be sanitised (Utf-8). */
    1603         const char *pcszIn;
    1604         /** The expected sanitised output (Utf-8). */
    1605         const char *pcszOutExpected;
    1606     } aTest[] =
    1607     {
    1608         { "OS/2 2.1", "OS_2 2.1" },
    1609         { "-!My VM!-", "__My VM_-" },
    1610         { "\xF0\x90\x8C\xB0", "____" },
    1611         { "  My VM  ", "__My VM__" },
    1612         { ".My VM.", "_My VM_" },
    1613         { "My VM", "My VM" }
    1614     };
    1615     for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
    1616     {
    1617         Utf8Str str(aTest[i].pcszIn);
    1618         sanitiseMachineFilename(str);
    1619         if (str.compare(aTest[i].pcszOutExpected))
    1620         {
    1621             ++cErrors;
    1622             pfnPrintf("%s: line %d, expected %s, actual %s\n",
    1623                       __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
    1624                       str.c_str());
    1625         }
    1626     }
    1627     return cErrors;
    1628 }
    1629 
    1630 /** @todo Proper testcase. */
    1631 /** @todo Do we have a better method of doing init functions? */
    1632 namespace
    1633 {
    1634     class TestSanitiseMachineFilename
    1635     {
    1636     public:
    1637         TestSanitiseMachineFilename(void)
    1638         {
    1639             Assert(!testSanitiseMachineFilename(RTAssertMsg2));
    1640         }
    1641     };
    1642     TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
    1643 }
    1644 #endif
    1645 
    1646 /** @note Locks mSystemProperties object for reading. */
    1647 STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile,
    1648                                        IN_BSTR aName,
    1649                                        ComSafeArrayIn(IN_BSTR, aGroups),
    1650                                        IN_BSTR aOsTypeId,
    1651                                        IN_BSTR aCreateFlags,
    1652                                        IMachine **aMachine)
    1653 {
    1654     LogFlowThisFuncEnter();
    1655     LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\", aCreateFlags=\"%ls\"\n", aSettingsFile, aName, aOsTypeId, aCreateFlags));
    1656 
    1657     CheckComArgStrNotEmptyOrNull(aName);
    1658     /** @todo tighten checks on aId? */
    1659     CheckComArgOutPointerValid(aMachine);
    1660 
    1661     AutoCaller autoCaller(this);
    1662     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1663 
    1664     StringsList llGroups;
    1665     HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
    1666     if (FAILED(rc))
    1667         return rc;
    1668 
    1669     Utf8Str strCreateFlags(aCreateFlags);
    1670     Guid id;
    1671     bool fForceOverwrite = false;
    1672     bool fDirectoryIncludesUUID = false;
    1673     if (!strCreateFlags.isEmpty())
    1674     {
    1675         const char *pcszNext = strCreateFlags.c_str();
    1676         while (*pcszNext != '\0')
    1677         {
    1678             Utf8Str strFlag;
    1679             const char *pcszComma = RTStrStr(pcszNext, ",");
    1680             if (!pcszComma)
    1681                 strFlag = pcszNext;
    1682             else
    1683                 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
    1684 
    1685             const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
    1686             /* skip over everything which doesn't contain '=' */
    1687             if (pcszEqual && pcszEqual != strFlag.c_str())
    1688             {
    1689                 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
    1690                 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
    1691 
    1692                 if (strKey == "UUID")
    1693                     id = strValue.c_str();
    1694                 else if (strKey == "forceOverwrite")
    1695                     fForceOverwrite = (strValue == "1");
    1696                 else if (strKey == "directoryIncludesUUID")
    1697                     fDirectoryIncludesUUID = (strValue == "1");
    1698             }
    1699 
    1700             if (!pcszComma)
    1701                 pcszNext += strFlag.length();
    1702             else
    1703                 pcszNext += strFlag.length() + 1;
    1704         }
    1705     }
    1706     /* Create UUID if none was specified. */
    1707     if (id.isZero())
    1708         id.create();
    1709     else if (!id.isValid())
    1710     {
    1711         /* do something else */
    1712         return setError(E_INVALIDARG,
    1713                  tr("'%ls' is not a valid Guid"),
    1714                  id.toStringCurly().c_str());
    1715     }
    1716 
    1717     /* NULL settings file means compose automatically */
    1718     Bstr bstrSettingsFile(aSettingsFile);
    1719     if (bstrSettingsFile.isEmpty())
    1720     {
    1721         Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
    1722         if (fDirectoryIncludesUUID)
    1723             strNewCreateFlags += ",directoryIncludesUUID=1";
    1724 
    1725         rc = ComposeMachineFilename(aName,
    1726                                     Bstr(llGroups.front()).raw(),
    1727                                     Bstr(strNewCreateFlags).raw(),
    1728                                     NULL /* aBaseFolder */,
    1729                                     bstrSettingsFile.asOutParam());
    1730         if (FAILED(rc)) return rc;
    1731     }
    1732 
    1733     /* create a new object */
    1734     ComObjPtr<Machine> machine;
    1735     rc = machine.createObject();
    1736     if (FAILED(rc)) return rc;
    1737 
    1738     GuestOSType *osType = NULL;
    1739     rc = findGuestOSType(Bstr(aOsTypeId), osType);
    1740     if (FAILED(rc)) return rc;
    1741 
    1742     /* initialize the machine object */
    1743     rc = machine->init(this,
    1744                        Utf8Str(bstrSettingsFile),
    1745                        Utf8Str(aName),
    1746                        llGroups,
    1747                        osType,
    1748                        id,
    1749                        fForceOverwrite,
    1750                        fDirectoryIncludesUUID);
    1751     if (SUCCEEDED(rc))
    1752     {
    1753         /* set the return value */
    1754         rc = machine.queryInterfaceTo(aMachine);
    1755         AssertComRC(rc);
    1756 
    1757 #ifdef VBOX_WITH_EXTPACK
    1758         /* call the extension pack hooks */
    1759         m->ptrExtPackManager->callAllVmCreatedHooks(machine);
    1760 #endif
    1761     }
    1762 
    1763     LogFlowThisFuncLeave();
    1764 
    1765     return rc;
    1766 }
    1767 
    1768 STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
    1769                                      IMachine **aMachine)
    1770 {
    1771     CheckComArgStrNotEmptyOrNull(aSettingsFile);
    1772     CheckComArgOutPointerValid(aMachine);
    1773 
    1774     AutoCaller autoCaller(this);
    1775     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1776 
    1777     HRESULT rc = E_FAIL;
    1778 
    1779     /* create a new object */
    1780     ComObjPtr<Machine> machine;
    1781     rc = machine.createObject();
    1782     if (SUCCEEDED(rc))
    1783     {
    1784         /* initialize the machine object */
    1785         rc = machine->initFromSettings(this,
    1786                                        aSettingsFile,
    1787                                        NULL);       /* const Guid *aId */
    1788         if (SUCCEEDED(rc))
    1789         {
    1790             /* set the return value */
    1791             rc = machine.queryInterfaceTo(aMachine);
    1792             ComAssertComRC(rc);
    1793         }
    1794     }
    1795 
    1796     return rc;
    1797 }
    1798 
    1799 /** @note Locks objects! */
    1800 STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
    1801 {
    1802     CheckComArgNotNull(aMachine);
    1803 
    1804     AutoCaller autoCaller(this);
    1805     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1806 
    1807     HRESULT rc;
    1808 
    1809     Bstr name;
    1810     rc = aMachine->COMGETTER(Name)(name.asOutParam());
    1811     if (FAILED(rc)) return rc;
    1812 
    1813     /* We can safely cast child to Machine * here because only Machine
    1814      * implementations of IMachine can be among our children. */
    1815     Machine *pMachine = static_cast<Machine*>(aMachine);
    1816 
    1817     AutoCaller machCaller(pMachine);
    1818     ComAssertComRCRetRC(machCaller.rc());
    1819 
    1820     rc = registerMachine(pMachine);
    1821     /* fire an event */
    1822     if (SUCCEEDED(rc))
    1823         onMachineRegistered(pMachine->getId(), TRUE);
    1824 
    1825     return rc;
    1826 }
    1827 
    1828 /** @note Locks this object for reading, then some machine objects for reading. */
    1829 STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
    1830 {
    1831     LogFlowThisFuncEnter();
    1832     LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
    1833 
    1834     CheckComArgStrNotEmptyOrNull(aNameOrId);
    1835     CheckComArgOutPointerValid(aMachine);
    1836 
    1837     AutoCaller autoCaller(this);
    1838     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1839 
    1840     /* start with not found */
    1841     HRESULT rc = S_OK;
    1842     ComObjPtr<Machine> pMachineFound;
    1843 
    1844     Guid id(aNameOrId);
    1845     if (id.isValid() && !id.isZero())
    1846 
    1847         rc = findMachine(id,
    1848                          true /* fPermitInaccessible */,
    1849                          true /* setError */,
    1850                          &pMachineFound);
    1851                 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
    1852     else
    1853     {
    1854         Utf8Str strName(aNameOrId);
    1855         rc = findMachineByName(aNameOrId,
    1856                                true /* setError */,
    1857                                &pMachineFound);
    1858                 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
    1859     }
    1860 
    1861     /* this will set (*machine) to NULL if machineObj is null */
    1862     pMachineFound.queryInterfaceTo(aMachine);
    1863 
    1864     LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
    1865     LogFlowThisFuncLeave();
    1866 
    1867     return rc;
    1868 }
    1869 
    1870 STDMETHODIMP VirtualBox::GetMachinesByGroups(ComSafeArrayIn(IN_BSTR, aGroups), ComSafeArrayOut(IMachine *, aMachines))
    1871 {
    1872     CheckComArgSafeArrayNotNull(aGroups);
    1873     CheckComArgOutSafeArrayPointerValid(aMachines);
    1874 
    1875     AutoCaller autoCaller(this);
    1876     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1877 
    1878     StringsList llGroups;
    1879     HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
    1880     if (FAILED(rc))
    1881         return rc;
    1882     /* we want to rely on sorted groups during compare, to save time */
    1883     llGroups.sort();
    1884 
    1885     /* get copy of all machine references, to avoid holding the list lock */
    1886     MachinesOList::MyList allMachines;
    1887     {
    1888         AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    1889         allMachines = m->allMachines.getList();
    1890     }
    1891 
    1892     com::SafeIfaceArray<IMachine> saMachines;
    1893     for (MachinesOList::MyList::const_iterator it = allMachines.begin();
    1894          it != allMachines.end();
    1895          ++it)
    1896     {
    1897         const ComObjPtr<Machine> &pMachine = *it;
    1898         AutoCaller autoMachineCaller(pMachine);
    1899         if (FAILED(autoMachineCaller.rc()))
    1900             continue;
    1901         AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
    1902 
    1903         if (pMachine->isAccessible())
    1904         {
    1905             const StringsList &thisGroups = pMachine->getGroups();
    1906             for (StringsList::const_iterator it2 = thisGroups.begin();
    1907                  it2 != thisGroups.end();
    1908                  ++it2)
    1909             {
    1910                 const Utf8Str &group = *it2;
    1911                 bool fAppended = false;
    1912                 for (StringsList::const_iterator it3 = llGroups.begin();
    1913                      it3 != llGroups.end();
    1914                      ++it3)
    1915                 {
    1916                     int order = it3->compare(group);
    1917                     if (order == 0)
    1918                     {
    1919                         saMachines.push_back(pMachine);
    1920                         fAppended = true;
    1921                         break;
    1922                     }
    1923                     else if (order > 0)
    1924                         break;
    1925                     else
    1926                         continue;
    1927                 }
    1928                 /* avoid duplicates and save time */
    1929                 if (fAppended)
    1930                     break;
    1931             }
    1932         }
    1933     }
    1934 
    1935     saMachines.detachTo(ComSafeArrayOutArg(aMachines));
    1936 
    1937     return S_OK;
    1938 }
    1939 
    1940 STDMETHODIMP VirtualBox::GetMachineStates(ComSafeArrayIn(IMachine *, aMachines), ComSafeArrayOut(MachineState_T, aStates))
    1941 {
    1942     CheckComArgSafeArrayNotNull(aMachines);
    1943     CheckComArgOutSafeArrayPointerValid(aStates);
    1944 
    1945     com::SafeIfaceArray<IMachine> saMachines(ComSafeArrayInArg(aMachines));
    1946     com::SafeArray<MachineState_T> saStates(saMachines.size());
    1947     for (size_t i = 0; i < saMachines.size(); i++)
    1948     {
    1949         ComPtr<IMachine> pMachine = saMachines[i];
    1950         MachineState_T state = MachineState_Null;
    1951         if (!pMachine.isNull())
    1952         {
    1953             HRESULT rc = pMachine->COMGETTER(State)(&state);
    1954             if (rc == E_ACCESSDENIED)
    1955                 rc = S_OK;
    1956             AssertComRC(rc);
    1957         }
    1958         saStates[i] = state;
    1959     }
    1960     saStates.detachTo(ComSafeArrayOutArg(aStates));
    1961 
    1962     return S_OK;
    1963 }
    1964 
    1965 STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
    1966                                         IN_BSTR aLocation,
    1967                                         IMedium **aHardDisk)
    1968 {
    1969     CheckComArgOutPointerValid(aHardDisk);
    1970 
    1971     AutoCaller autoCaller(this);
    1972     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1973 
    1974     /* we don't access non-const data members so no need to lock */
    1975 
    1976     Utf8Str format(aFormat);
    1977     if (format.isEmpty())
    1978         getDefaultHardDiskFormat(format);
    1979 
    1980     ComObjPtr<Medium> hardDisk;
    1981     hardDisk.createObject();
    1982     HRESULT rc = hardDisk->init(this,
    1983                                 format,
    1984                                 aLocation,
    1985                                 Guid::Empty /* media registry: none yet */);
    1986 
    1987     if (SUCCEEDED(rc))
    1988         hardDisk.queryInterfaceTo(aHardDisk);
    1989 
    1990     return rc;
    1991 }
    1992 
    1993 STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
    1994                                     DeviceType_T deviceType,
    1995                                     AccessMode_T accessMode,
    1996                                     BOOL fForceNewUuid,
    1997                                     IMedium **aMedium)
    1998 {
    1999     HRESULT rc = S_OK;
    2000     CheckComArgStrNotEmptyOrNull(aLocation);
    2001     CheckComArgOutPointerValid(aMedium);
    2002 
    2003     AutoCaller autoCaller(this);
    2004     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2005 
    2006     Guid id(aLocation);
    2007     ComObjPtr<Medium> pMedium;
    2008 
    2009     // have to get write lock as the whole find/update sequence must be done
    2010     // in one critical section, otherwise there are races which can lead to
    2011     // multiple Medium objects with the same content
    2012     AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    2013 
    2014     // check if the device type is correct, and see if a medium for the
    2015     // given path has already initialized; if so, return that
    2016     switch (deviceType)
    2017     {
    2018         case DeviceType_HardDisk:
    2019             if (id.isValid() && !id.isZero())
    2020                 rc = findHardDiskById(id, false /* setError */, &pMedium);
    2021             else
    2022                 rc = findHardDiskByLocation(aLocation,
    2023                                             false, /* aSetError */
    2024                                             &pMedium);
    2025         break;
    2026 
    2027         case DeviceType_Floppy:
    2028         case DeviceType_DVD:
    2029             if (id.isValid() && !id.isZero())
    2030                 rc = findDVDOrFloppyImage(deviceType, &id, Utf8Str::Empty,
    2031                                           false /* setError */, &pMedium);
    2032             else
    2033                 rc = findDVDOrFloppyImage(deviceType, NULL, aLocation,
    2034                                           false /* setError */, &pMedium);
    2035 
    2036             // enforce read-only for DVDs even if caller specified ReadWrite
    2037             if (deviceType == DeviceType_DVD)
    2038                 accessMode = AccessMode_ReadOnly;
    2039         break;
    2040 
    2041         default:
    2042             return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy %d", deviceType);
    2043     }
    2044 
    2045     if (pMedium.isNull())
    2046     {
    2047         pMedium.createObject();
    2048         treeLock.release();
    2049         rc = pMedium->init(this,
    2050                            aLocation,
    2051                            (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
    2052                            !!fForceNewUuid,
    2053                            deviceType);
    2054         treeLock.acquire();
    2055 
    2056         if (SUCCEEDED(rc))
    2057         {
    2058             rc = registerMedium(pMedium, &pMedium, deviceType);
    2059 
    2060             treeLock.release();
    2061 
    2062             /* Note that it's important to call uninit() on failure to register
    2063              * because the differencing hard disk would have been already associated
    2064              * with the parent and this association needs to be broken. */
    2065 
    2066             if (FAILED(rc))
    2067             {
    2068                 pMedium->uninit();
    2069                 rc = VBOX_E_OBJECT_NOT_FOUND;
    2070             }
    2071         }
    2072         else
    2073             rc = VBOX_E_OBJECT_NOT_FOUND;
    2074     }
    2075 
    2076     if (SUCCEEDED(rc))
    2077         pMedium.queryInterfaceTo(aMedium);
    2078 
    2079     return rc;
    2080 }
    2081 
    2082 
    2083 /** @note Locks this object for reading. */
    2084 STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
    2085 {
    2086     CheckComArgNotNull(aType);
    2087 
    2088     AutoCaller autoCaller(this);
    2089     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2090 
    2091     *aType = NULL;
    2092 
    2093     AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    2094     for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
    2095          it != m->allGuestOSTypes.end();
    2096          ++it)
    2097     {
    2098         const Bstr &typeId = (*it)->id();
    2099         AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
    2100         if (typeId.compare(aId, Bstr::CaseInsensitive) == 0)
    2101         {
    2102             (*it).queryInterfaceTo(aType);
    2103             break;
    2104         }
    2105     }
    2106 
    2107     return (*aType) ? S_OK :
    2108         setError(E_INVALIDARG,
    2109                  tr("'%ls' is not a valid Guest OS type"),
    2110                  aId);
    2111 }
    2112 
    2113 STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName,        IN_BSTR aHostPath,
    2114                                             BOOL /* aWritable */, BOOL /* aAutoMount */)
    2115 {
    2116     CheckComArgStrNotEmptyOrNull(aName);
    2117     CheckComArgStrNotEmptyOrNull(aHostPath);
    2118 
    2119     AutoCaller autoCaller(this);
    2120     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2121 
    2122     return setError(E_NOTIMPL, "Not yet implemented");
    2123 }
    2124 
    2125 STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
    2126 {
    2127     CheckComArgStrNotEmptyOrNull(aName);
    2128 
    2129     AutoCaller autoCaller(this);
    2130     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2131 
    2132     return setError(E_NOTIMPL, "Not yet implemented");
    2133 }
    2134 
    2135 /**
    2136  *  @note Locks this object for reading.
    2137  */
    2138 STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
    2139 {
    2140     using namespace settings;
    2141 
    2142     CheckComArgOutSafeArrayPointerValid(aKeys);
    2143 
    2144     AutoCaller autoCaller(this);
    2145     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2146 
    2147     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2148 
    2149     com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
    2150     int i = 0;
    2151     for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
    2152          it != m->pMainConfigFile->mapExtraDataItems.end();
    2153          ++it, ++i)
    2154     {
    2155         const Utf8Str &strName = it->first;     // the key
    2156         strName.cloneTo(&saKeys[i]);
    2157     }
    2158     saKeys.detachTo(ComSafeArrayOutArg(aKeys));
    2159 
    2160     return S_OK;
    2161 }
    2162 
    2163 /**
    2164  *  @note Locks this object for reading.
    2165  */
    2166 STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
    2167                                       BSTR *aValue)
    2168 {
    2169     CheckComArgStrNotEmptyOrNull(aKey);
    2170     CheckComArgNotNull(aValue);
    2171 
    2172     AutoCaller autoCaller(this);
    2173     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2174 
    2175     /* start with nothing found */
    2176     Utf8Str strKey(aKey);
    2177     Bstr bstrResult;
    2178 
    2179     settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
    2180     if (it != m->pMainConfigFile->mapExtraDataItems.end())
    2181         // found:
    2182         bstrResult = it->second; // source is a Utf8Str
    2183 
    2184     /* return the result to caller (may be empty) */
    2185     bstrResult.cloneTo(aValue);
    2186 
    2187     return S_OK;
    2188 }
    2189 
    2190 /**
    2191  *  @note Locks this object for writing.
    2192  */
    2193 STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
    2194                                       IN_BSTR aValue)
    2195 {
    2196     CheckComArgStrNotEmptyOrNull(aKey);
    2197 
    2198     AutoCaller autoCaller(this);
    2199     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2200 
    2201     Utf8Str strKey(aKey);
    2202     Utf8Str strValue(aValue);
    2203     Utf8Str strOldValue;            // empty
    2204 
    2205     // locking note: we only hold the read lock briefly to look up the old value,
    2206     // then release it and call the onExtraCanChange callbacks. There is a small
    2207     // chance of a race insofar as the callback might be called twice if two callers
    2208     // change the same key at the same time, but that's a much better solution
    2209     // than the deadlock we had here before. The actual changing of the extradata
    2210     // is then performed under the write lock and race-free.
    2211 
    2212     // look up the old value first; if nothing has changed then we need not do anything
    2213     {
    2214         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
    2215         settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
    2216         if (it != m->pMainConfigFile->mapExtraDataItems.end())
    2217             strOldValue = it->second;
    2218     }
    2219 
    2220     bool fChanged;
    2221     if ((fChanged = (strOldValue != strValue)))
    2222     {
    2223         // ask for permission from all listeners outside the locks;
    2224         // onExtraDataCanChange() only briefly requests the VirtualBox
    2225         // lock to copy the list of callbacks to invoke
    2226         Bstr error;
    2227         Bstr bstrValue(aValue);
    2228 
    2229         if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
    2230         {
    2231             const char *sep = error.isEmpty() ? "" : ": ";
    2232             CBSTR err = error.raw();
    2233             LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
    2234                             sep, err));
    2235             return setError(E_ACCESSDENIED,
    2236                             tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
    2237                             aKey,
    2238                             bstrValue.raw(),
    2239                             sep,
    2240                             err);
    2241         }
    2242 
    2243         // data is changing and change not vetoed: then write it out under the lock
    2244 
    2245         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2246 
    2247         if (strValue.isEmpty())
    2248             m->pMainConfigFile->mapExtraDataItems.erase(strKey);
    2249         else
    2250             m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
    2251                 // creates a new key if needed
    2252 
    2253         /* save settings on success */
    2254         HRESULT rc = saveSettings();
    2255         if (FAILED(rc)) return rc;
    2256     }
    2257 
    2258     // fire notification outside the lock
    2259     if (fChanged)
    2260         onExtraDataChange(Guid::Empty, aKey, aValue);
    2261 
    2262     return S_OK;
    2263 }
    2264 
    2265 /**
    2266  *
    2267  */
    2268 STDMETHODIMP VirtualBox::SetSettingsSecret(IN_BSTR aValue)
    2269 {
    2270     storeSettingsKey(aValue);
    2271     decryptSettings();
    2272     return S_OK;
    2273 }
    2274 
    2275 int VirtualBox::decryptMediumSettings(Medium *pMedium)
    2276 {
    2277     Bstr bstrCipher;
    2278     HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
    2279                                        bstrCipher.asOutParam());
    2280     if (SUCCEEDED(hrc))
    2281     {
    2282         Utf8Str strPlaintext;
    2283         int rc = decryptSetting(&strPlaintext, bstrCipher);
    2284         if (RT_SUCCESS(rc))
    2285             pMedium->setPropertyDirect("InitiatorSecret", strPlaintext);
    2286         else
    2287             return rc;
    2288     }
    2289     return VINF_SUCCESS;
    2290 }
    2291 
    2292 /**
    2293  * Decrypt all encrypted settings.
    2294  *
    2295  * So far we only have encrypted iSCSI initiator secrets so we just go through
    2296  * all hard disk mediums and determine the plain 'InitiatorSecret' from
    2297  * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
    2298  * properties need to be null-terminated strings.
    2299  */
    2300 int VirtualBox::decryptSettings()
    2301 {
    2302     bool fFailure = false;
    2303     AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    2304     for (MediaList::const_iterator mt = m->allHardDisks.begin();
    2305          mt != m->allHardDisks.end();
    2306          ++mt)
    2307     {
    2308         ComObjPtr<Medium> pMedium = *mt;
    2309         AutoCaller medCaller(pMedium);
    2310         if (FAILED(medCaller.rc()))
    2311             continue;
    2312         AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    2313         int vrc = decryptMediumSettings(pMedium);
    2314         if (RT_FAILURE(vrc))
    2315             fFailure = true;
    2316     }
    2317     return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
    2318 }
    2319 
    2320 /**
    2321  * Encode.
    2322  *
    2323  * @param aPlaintext      plaintext to be encrypted
    2324  * @param aCiphertext     resulting ciphertext (base64-encoded)
    2325  */
    2326 int VirtualBox::encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
    2327 {
    2328     uint8_t abCiphertext[32];
    2329     char    szCipherBase64[128];
    2330     size_t  cchCipherBase64;
    2331     int rc = encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext,
    2332                                  aPlaintext.length()+1, sizeof(abCiphertext));
    2333     if (RT_SUCCESS(rc))
    2334     {
    2335         rc = RTBase64Encode(abCiphertext, sizeof(abCiphertext),
    2336                             szCipherBase64, sizeof(szCipherBase64),
    2337                             &cchCipherBase64);
    2338         if (RT_SUCCESS(rc))
    2339             *aCiphertext = szCipherBase64;
    2340     }
    2341     return rc;
    2342 }
    2343 
    2344 /**
    2345  * Decode.
    2346  *
    2347  * @param aPlaintext      resulting plaintext
    2348  * @param aCiphertext     ciphertext (base64-encoded) to decrypt
    2349  */
    2350 int VirtualBox::decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
    2351 {
    2352     uint8_t abPlaintext[64];
    2353     uint8_t abCiphertext[64];
    2354     size_t  cbCiphertext;
    2355     int rc = RTBase64Decode(aCiphertext.c_str(),
    2356                             abCiphertext, sizeof(abCiphertext),
    2357                             &cbCiphertext, NULL);
    2358     if (RT_SUCCESS(rc))
    2359     {
    2360         rc = decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
    2361         if (RT_SUCCESS(rc))
    2362         {
    2363             for (unsigned i = 0; i < cbCiphertext; i++)
    2364             {
    2365                 /* sanity check: null-terminated string? */
    2366                 if (abPlaintext[i] == '\0')
    2367                 {
    2368                     /* sanity check: valid UTF8 string? */
    2369                     if (RTStrIsValidEncoding((const char*)abPlaintext))
    2370                     {
    2371                         *aPlaintext = Utf8Str((const char*)abPlaintext);
    2372                         return VINF_SUCCESS;
    2373                     }
    2374                 }
    2375             }
    2376             rc = VERR_INVALID_MAGIC;
    2377         }
    2378     }
    2379     return rc;
    2380 }
    2381 
    2382 /**
    2383  * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
    2384  *
    2385  * @param aPlaintext      clear text to be encrypted
    2386  * @param aCiphertext     resulting encrypted text
    2387  * @param aPlaintextSize  size of the plaintext
    2388  * @param aCiphertextSize size of the ciphertext
    2389  */
    2390 int VirtualBox::encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
    2391                                     size_t aPlaintextSize, size_t aCiphertextSize) const
    2392 {
    2393     unsigned i, j;
    2394     uint8_t aBytes[64];
    2395 
    2396     if (!m->fSettingsCipherKeySet)
    2397         return VERR_INVALID_STATE;
    2398 
    2399     if (aCiphertextSize > sizeof(aBytes))
    2400         return VERR_BUFFER_OVERFLOW;
    2401 
    2402     if (aCiphertextSize < 32)
    2403         return VERR_INVALID_PARAMETER;
    2404 
    2405     AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
    2406 
    2407     /* store the first 8 bytes of the cipherkey for verification */
    2408     for (i = 0, j = 0; i < 8; i++, j++)
    2409         aCiphertext[i] = m->SettingsCipherKey[j];
    2410 
    2411     for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
    2412     {
    2413         aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
    2414         if (++j >= sizeof(m->SettingsCipherKey))
    2415             j = 0;
    2416     }
    2417 
    2418     /* fill with random data to have a minimal length (salt) */
    2419     if (i < aCiphertextSize)
    2420     {
    2421         RTRandBytes(aBytes, aCiphertextSize - i);
    2422         for (int k = 0; i < aCiphertextSize; i++, k++)
    2423         {
    2424             aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
    2425             if (++j >= sizeof(m->SettingsCipherKey))
    2426                 j = 0;
    2427         }
    2428     }
    2429 
    2430     return VINF_SUCCESS;
    2431 }
    2432 
    2433 /**
    2434  * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
    2435  *
    2436  * @param aPlaintext      resulting plaintext
    2437  * @param aCiphertext     ciphertext to be decrypted
    2438  * @param aCiphertextSize size of the ciphertext == size of the plaintext
    2439  */
    2440 int VirtualBox::decryptSettingBytes(uint8_t *aPlaintext,
    2441                                     const uint8_t *aCiphertext, size_t aCiphertextSize) const
    2442 {
    2443     unsigned i, j;
    2444 
    2445     if (!m->fSettingsCipherKeySet)
    2446         return VERR_INVALID_STATE;
    2447 
    2448     if (aCiphertextSize < 32)
    2449         return VERR_INVALID_PARAMETER;
    2450 
    2451     /* key verification */
    2452     for (i = 0, j = 0; i < 8; i++, j++)
    2453         if (aCiphertext[i] != m->SettingsCipherKey[j])
    2454             return VERR_INVALID_MAGIC;
    2455 
    2456     /* poison */
    2457     memset(aPlaintext, 0xff, aCiphertextSize);
    2458     for (int k = 0; i < aCiphertextSize; i++, k++)
    2459     {
    2460         aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
    2461         if (++j >= sizeof(m->SettingsCipherKey))
    2462             j = 0;
    2463     }
    2464 
    2465     return VINF_SUCCESS;
    2466 }
    2467 
    2468 /**
    2469  * Store a settings key.
    2470  *
    2471  * @param aKey          the key to store
    2472  */
    2473 void VirtualBox::storeSettingsKey(const Utf8Str &aKey)
    2474 {
    2475     RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
    2476     m->fSettingsCipherKeySet = true;
    2477 }
    2478 
    2479 // public methods only for internal purposes
    2480 /////////////////////////////////////////////////////////////////////////////
    2481 
    2482 #ifdef DEBUG
    2483 void VirtualBox::dumpAllBackRefs()
    2484 {
    2485     {
    2486         AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    2487         for (MediaList::const_iterator mt = m->allHardDisks.begin();
    2488              mt != m->allHardDisks.end();
    2489              ++mt)
    2490         {
    2491             ComObjPtr<Medium> pMedium = *mt;
    2492             pMedium->dumpBackRefs();
    2493         }
    2494     }
    2495     {
    2496         AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    2497         for (MediaList::const_iterator mt = m->allDVDImages.begin();
    2498              mt != m->allDVDImages.end();
    2499              ++mt)
    2500         {
    2501             ComObjPtr<Medium> pMedium = *mt;
    2502             pMedium->dumpBackRefs();
    2503         }
    2504     }
    2505 }
    2506 #endif
    2507 
    2508 /**
    2509  *  Posts an event to the event queue that is processed asynchronously
    2510  *  on a dedicated thread.
    2511  *
    2512  *  Posting events to the dedicated event queue is useful to perform secondary
    2513  *  actions outside any object locks -- for example, to iterate over a list
    2514  *  of callbacks and inform them about some change caused by some object's
    2515  *  method call.
    2516  *
    2517  *  @param event    event to post; must have been allocated using |new|, will
    2518  *                  be deleted automatically by the event thread after processing
    2519  *
    2520  *  @note Doesn't lock any object.
    2521  */
    2522 HRESULT VirtualBox::postEvent(Event *event)
    2523 {
    2524     AssertReturn(event, E_FAIL);
    2525 
    2526     HRESULT rc;
    2527     AutoCaller autoCaller(this);
    2528     if (SUCCEEDED((rc = autoCaller.rc())))
    2529     {
    2530         if (autoCaller.state() != Ready)
    2531             LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
    2532                             autoCaller.state()));
    2533             // return S_OK
    2534         else if (    (m->pAsyncEventQ)
    2535                   && (m->pAsyncEventQ->postEvent(event))
    2536                 )
    2537             return S_OK;
    2538         else
    2539             rc = E_FAIL;
    2540     }
    2541 
    2542     // in any event of failure, we must clean up here, or we'll leak;
    2543     // the caller has allocated the object using new()
    2544     delete event;
    2545     return rc;
    2546 }
    2547 
    2548 /**
    2549  * Adds a progress to the global collection of pending operations.
    2550  * Usually gets called upon progress object initialization.
    2551  *
    2552  * @param aProgress Operation to add to the collection.
    2553  *
    2554  * @note Doesn't lock objects.
    2555  */
    2556 HRESULT VirtualBox::addProgress(IProgress *aProgress)
    2557 {
    2558     CheckComArgNotNull(aProgress);
    2559 
    2560     AutoCaller autoCaller(this);
    2561     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2562 
    2563     Bstr id;
    2564     HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
    2565     AssertComRCReturnRC(rc);
    2566 
    2567     /* protect mProgressOperations */
    2568     AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
    2569 
    2570     m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
    2571     return S_OK;
    2572 }
    2573 
    2574 /**
    2575  * Removes the progress from the global collection of pending operations.
    2576  * Usually gets called upon progress completion.
    2577  *
    2578  * @param aId   UUID of the progress operation to remove
    2579  *
    2580  * @note Doesn't lock objects.
    2581  */
    2582 HRESULT VirtualBox::removeProgress(IN_GUID aId)
    2583 {
    2584     AutoCaller autoCaller(this);
    2585     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2586 
    2587     ComPtr<IProgress> progress;
    2588 
    2589     /* protect mProgressOperations */
    2590     AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
    2591 
    2592     size_t cnt = m->mapProgressOperations.erase(aId);
    2593     Assert(cnt == 1);
    2594     NOREF(cnt);
    2595 
    2596     return S_OK;
    2597 }
    2598 
    2599 #ifdef RT_OS_WINDOWS
    2600 
    2601 struct StartSVCHelperClientData
    2602 {
    2603     ComObjPtr<VirtualBox> that;
    2604     ComObjPtr<Progress> progress;
    2605     bool privileged;
    2606     VirtualBox::SVCHelperClientFunc func;
    2607     void *user;
    2608 };
    2609 
    2610 /**
    2611  *  Helper method that starts a worker thread that:
    2612  *  - creates a pipe communication channel using SVCHlpClient;
    2613  *  - starts an SVC Helper process that will inherit this channel;
    2614  *  - executes the supplied function by passing it the created SVCHlpClient
    2615  *    and opened instance to communicate to the Helper process and the given
    2616  *    Progress object.
    2617  *
    2618  *  The user function is supposed to communicate to the helper process
    2619  *  using the \a aClient argument to do the requested job and optionally expose
    2620  *  the progress through the \a aProgress object. The user function should never
    2621  *  call notifyComplete() on it: this will be done automatically using the
    2622  *  result code returned by the function.
    2623  *
    2624  *  Before the user function is started, the communication channel passed to
    2625  *  the \a aClient argument is fully set up, the function should start using
    2626  *  its write() and read() methods directly.
    2627  *
    2628  *  The \a aVrc parameter of the user function may be used to return an error
    2629  *  code if it is related to communication errors (for example, returned by
    2630  *  the SVCHlpClient members when they fail). In this case, the correct error
    2631  *  message using this value will be reported to the caller. Note that the
    2632  *  value of \a aVrc is inspected only if the user function itself returns
    2633  *  success.
    2634  *
    2635  *  If a failure happens anywhere before the user function would be normally
    2636  *  called, it will be called anyway in special "cleanup only" mode indicated
    2637  *  by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
    2638  *  all the function is supposed to do is to cleanup its aUser argument if
    2639  *  necessary (it's assumed that the ownership of this argument is passed to
    2640  *  the user function once #startSVCHelperClient() returns a success, thus
    2641  *  making it responsible for the cleanup).
    2642  *
    2643  *  After the user function returns, the thread will send the SVCHlpMsg::Null
    2644  *  message to indicate a process termination.
    2645  *
    2646  *  @param  aPrivileged |true| to start the SVC Helper process as a privileged
    2647  *                      user that can perform administrative tasks
    2648  *  @param  aFunc       user function to run
    2649  *  @param  aUser       argument to the user function
    2650  *  @param  aProgress   progress object that will track operation completion
    2651  *
    2652  *  @note aPrivileged is currently ignored (due to some unsolved problems in
    2653  *        Vista) and the process will be started as a normal (unprivileged)
    2654  *        process.
    2655  *
    2656  *  @note Doesn't lock anything.
    2657  */
    2658 HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
    2659                                          SVCHelperClientFunc aFunc,
    2660                                          void *aUser, Progress *aProgress)
    2661 {
    2662     AssertReturn(aFunc, E_POINTER);
    2663     AssertReturn(aProgress, E_POINTER);
    2664 
    2665     AutoCaller autoCaller(this);
    2666     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2667 
    2668     /* create the SVCHelperClientThread() argument */
    2669     std::auto_ptr <StartSVCHelperClientData>
    2670         d(new StartSVCHelperClientData());
    2671     AssertReturn(d.get(), E_OUTOFMEMORY);
    2672 
    2673     d->that = this;
    2674     d->progress = aProgress;
    2675     d->privileged = aPrivileged;
    2676     d->func = aFunc;
    2677     d->user = aUser;
    2678 
    2679     RTTHREAD tid = NIL_RTTHREAD;
    2680     int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
    2681                              static_cast <void *>(d.get()),
    2682                              0, RTTHREADTYPE_MAIN_WORKER,
    2683                              RTTHREADFLAGS_WAITABLE, "SVCHelper");
    2684     if (RT_FAILURE(vrc))
    2685         return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
    2686 
    2687     /* d is now owned by SVCHelperClientThread(), so release it */
    2688     d.release();
    2689 
    2690     return S_OK;
    2691 }
    2692 
    2693 /**
    2694  *  Worker thread for startSVCHelperClient().
    2695  */
    2696 /* static */
    2697 DECLCALLBACK(int)
    2698 VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
     152/*static*/
     153DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD /* thread */, void *pvUser)
    2699154{
    2700155    LogFlowFuncEnter();
    2701156
    2702     std::auto_ptr<StartSVCHelperClientData>
    2703         d(static_cast<StartSVCHelperClientData*>(aUser));
    2704 
    2705     HRESULT rc = S_OK;
    2706     bool userFuncCalled = false;
     157    VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser;
     158    Assert(that);
     159
     160    typedef std::vector<ComObjPtr<Machine> > MachineVector;
     161    typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector;
     162
     163    SessionMachineVector machines;
     164    MachineVector spawnedMachines;
     165
     166    size_t cnt = 0;
     167    size_t cntSpawned = 0;
     168
     169    VirtualBoxBase::initializeComForThread();
     170
     171#if defined(RT_OS_WINDOWS)
     172
     173    /// @todo (dmik) processes reaping!
     174
     175    HANDLE handles[MAXIMUM_WAIT_OBJECTS];
     176    handles[0] = that->mUpdateReq;
    2707177
    2708178    do
    2709179    {
    2710         AssertBreakStmt(d.get(), rc = E_POINTER);
    2711         AssertReturn(!d->progress.isNull(), E_POINTER);
    2712 
    2713         /* protect VirtualBox from uninitialization */
    2714         AutoCaller autoCaller(d->that);
    2715         if (!autoCaller.isOk())
    2716         {
    2717             /* it's too late */
    2718             rc = autoCaller.rc();
    2719             break;
    2720         }
    2721 
    2722         int vrc = VINF_SUCCESS;
    2723 
    2724         Guid id;
    2725         id.create();
    2726         SVCHlpClient client;
    2727         vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
    2728                                        id.raw()).c_str());
    2729         if (RT_FAILURE(vrc))
    2730         {
    2731             rc = d->that->setError(E_FAIL,
    2732                                    tr("Could not create the communication channel (%Rrc)"), vrc);
    2733             break;
    2734         }
    2735 
    2736         /* get the path to the executable */
    2737         char exePathBuf[RTPATH_MAX];
    2738         char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
    2739         if (!exePath)
    2740         {
    2741             rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
    2742             break;
    2743         }
    2744 
    2745         Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
    2746 
    2747         LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
    2748 
    2749         RTPROCESS pid = NIL_RTPROCESS;
    2750 
    2751         if (d->privileged)
    2752         {
    2753             /* Attempt to start a privileged process using the Run As dialog */
    2754 
    2755             Bstr file = exePath;
    2756             Bstr parameters = argsStr;
    2757 
    2758             SHELLEXECUTEINFO shExecInfo;
    2759 
    2760             shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    2761 
    2762             shExecInfo.fMask = NULL;
    2763             shExecInfo.hwnd = NULL;
    2764             shExecInfo.lpVerb = L"runas";
    2765             shExecInfo.lpFile = file.raw();
    2766             shExecInfo.lpParameters = parameters.raw();
    2767             shExecInfo.lpDirectory = NULL;
    2768             shExecInfo.nShow = SW_NORMAL;
    2769             shExecInfo.hInstApp = NULL;
    2770 
    2771             if (!ShellExecuteEx(&shExecInfo))
    2772             {
    2773                 int vrc2 = RTErrConvertFromWin32(GetLastError());
    2774                 /* hide excessive details in case of a frequent error
    2775                  * (pressing the Cancel button to close the Run As dialog) */
    2776                 if (vrc2 == VERR_CANCELLED)
    2777                     rc = d->that->setError(E_FAIL,
    2778                                            tr("Operation canceled by the user"));
    2779                 else
    2780                     rc = d->that->setError(E_FAIL,
    2781                                            tr("Could not launch a privileged process '%s' (%Rrc)"),
    2782                                            exePath, vrc2);
    2783                 break;
    2784             }
    2785         }
    2786         else
    2787         {
    2788             const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
    2789             vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
    2790             if (RT_FAILURE(vrc))
    2791             {
    2792                 rc = d->that->setError(E_FAIL,
    2793                                        tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
    2794                 break;
    2795             }
    2796         }
    2797 
    2798         /* wait for the client to connect */
    2799         vrc = client.connect();
    2800         if (RT_SUCCESS(vrc))
    2801         {
    2802             /* start the user supplied function */
    2803             rc = d->func(&client, d->progress, d->user, &vrc);
    2804             userFuncCalled = true;
    2805         }
    2806 
    2807         /* send the termination signal to the process anyway */
    2808         {
    2809             int vrc2 = client.write(SVCHlpMsg::Null);
    2810             if (RT_SUCCESS(vrc))
    2811                 vrc = vrc2;
    2812         }
    2813 
    2814         if (SUCCEEDED(rc) && RT_FAILURE(vrc))
    2815         {
    2816             rc = d->that->setError(E_FAIL,
    2817                                    tr("Could not operate the communication channel (%Rrc)"), vrc);
    2818             break;
    2819         }
    2820     }
    2821     while (0);
    2822 
    2823     if (FAILED(rc) && !userFuncCalled)
    2824     {
    2825         /* call the user function in the "cleanup only" mode
    2826          * to let it free resources passed to in aUser */
    2827         d->func(NULL, NULL, d->user, NULL);
    2828     }
    2829 
    2830     d->progress->notifyComplete(rc);
    2831 
    2832     LogFlowFuncLeave();
    2833     return 0;
    2834 }
    2835 
    2836 #endif /* RT_OS_WINDOWS */
    2837 
    2838 /**
    2839  *  Sends a signal to the client watcher thread to rescan the set of machines
    2840  *  that have open sessions.
    2841  *
    2842  *  @note Doesn't lock anything.
    2843  */
    2844 void VirtualBox::updateClientWatcher()
    2845 {
    2846     AutoCaller autoCaller(this);
    2847     AssertComRCReturnVoid(autoCaller.rc());
    2848 
    2849     AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
    2850 
    2851     /* sent an update request */
    2852 #if defined(RT_OS_WINDOWS)
    2853     ::SetEvent(m->updateReq);
    2854 #elif defined(RT_OS_OS2)
    2855     RTSemEventSignal(m->updateReq);
    2856 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    2857     ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
    2858     RTSemEventSignal(m->updateReq);
    2859 #else
    2860 # error "Port me!"
    2861 #endif
    2862 }
    2863 
    2864 /**
    2865  *  Adds the given child process ID to the list of processes to be reaped.
    2866  *  This call should be followed by #updateClientWatcher() to take the effect.
    2867  */
    2868 void VirtualBox::addProcessToReap(RTPROCESS pid)
    2869 {
    2870     AutoCaller autoCaller(this);
    2871     AssertComRCReturnVoid(autoCaller.rc());
    2872 
    2873     /// @todo (dmik) Win32?
    2874 #ifndef RT_OS_WINDOWS
    2875     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2876     m->llProcesses.push_back(pid);
    2877 #endif
    2878 }
    2879 
    2880 /** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
    2881 struct MachineEvent : public VirtualBox::CallbackEvent
    2882 {
    2883     MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, BOOL aBool)
    2884         : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
    2885         , mBool(aBool)
    2886         { }
    2887 
    2888     MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, MachineState_T aState)
    2889         : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
    2890         , mState(aState)
    2891         {}
    2892 
    2893     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
    2894     {
    2895         switch (mWhat)
    2896         {
    2897             case VBoxEventType_OnMachineDataChanged:
    2898                 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
    2899                 break;
    2900 
    2901             case VBoxEventType_OnMachineStateChanged:
    2902                 aEvDesc.init(aSource, mWhat, id.raw(), mState);
    2903                 break;
    2904 
    2905             case VBoxEventType_OnMachineRegistered:
    2906                 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
    2907                 break;
    2908 
    2909             default:
    2910                 AssertFailedReturn(S_OK);
    2911          }
    2912          return S_OK;
    2913     }
    2914 
    2915     Bstr id;
    2916     MachineState_T mState;
    2917     BOOL mBool;
    2918 };
    2919 
    2920 /**
    2921  *  @note Doesn't lock any object.
    2922  */
    2923 void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
    2924 {
    2925     postEvent(new MachineEvent(this, VBoxEventType_OnMachineStateChanged, aId, aState));
    2926 }
    2927 
    2928 /**
    2929  *  @note Doesn't lock any object.
    2930  */
    2931 void VirtualBox::onMachineDataChange(const Guid &aId, BOOL aTemporary)
    2932 {
    2933     postEvent(new MachineEvent(this, VBoxEventType_OnMachineDataChanged, aId, aTemporary));
    2934 }
    2935 
    2936 /**
    2937  *  @note Locks this object for reading.
    2938  */
    2939 BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
    2940                                        Bstr &aError)
    2941 {
    2942     LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
    2943                       aId.toString().c_str(), aKey, aValue));
    2944 
    2945     AutoCaller autoCaller(this);
    2946     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    2947 
    2948     BOOL allowChange = TRUE;
    2949     Bstr id = aId.toUtf16();
    2950 
    2951     VBoxEventDesc evDesc;
    2952     evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
    2953     BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
    2954     //Assert(fDelivered);
    2955     if (fDelivered)
    2956     {
    2957         ComPtr<IEvent> aEvent;
    2958         evDesc.getEvent(aEvent.asOutParam());
    2959         ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
    2960         Assert(aCanChangeEvent);
    2961         BOOL fVetoed = FALSE;
    2962         aCanChangeEvent->IsVetoed(&fVetoed);
    2963         allowChange = !fVetoed;
    2964 
    2965         if (!allowChange)
    2966         {
    2967             SafeArray<BSTR> aVetos;
    2968             aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
    2969             if (aVetos.size() > 0)
    2970                 aError = aVetos[0];
    2971         }
    2972     }
    2973     else
    2974         allowChange = TRUE;
    2975 
    2976     LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
    2977     return allowChange;
    2978 }
    2979 
    2980 /** Event for onExtraDataChange() */
    2981 struct ExtraDataEvent : public VirtualBox::CallbackEvent
    2982 {
    2983     ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
    2984                    IN_BSTR aKey, IN_BSTR aVal)
    2985         : CallbackEvent(aVB, VBoxEventType_OnExtraDataChanged)
    2986         , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
    2987     {}
    2988 
    2989     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
    2990     {
    2991         return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
    2992     }
    2993 
    2994     Bstr machineId, key, val;
    2995 };
    2996 
    2997 /**
    2998  *  @note Doesn't lock any object.
    2999  */
    3000 void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
    3001 {
    3002     postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
    3003 }
    3004 
    3005 /**
    3006  *  @note Doesn't lock any object.
    3007  */
    3008 void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
    3009 {
    3010     postEvent(new MachineEvent(this, VBoxEventType_OnMachineRegistered, aId, aRegistered));
    3011 }
    3012 
    3013 /** Event for onSessionStateChange() */
    3014 struct SessionEvent : public VirtualBox::CallbackEvent
    3015 {
    3016     SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
    3017         : CallbackEvent(aVB, VBoxEventType_OnSessionStateChanged)
    3018         , machineId(aMachineId.toUtf16()), sessionState(aState)
    3019     {}
    3020 
    3021     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
    3022     {
    3023         return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
    3024     }
    3025     Bstr machineId;
    3026     SessionState_T sessionState;
    3027 };
    3028 
    3029 /**
    3030  *  @note Doesn't lock any object.
    3031  */
    3032 void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
    3033 {
    3034     postEvent(new SessionEvent(this, aId, aState));
    3035 }
    3036 
    3037 /** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
    3038 struct SnapshotEvent : public VirtualBox::CallbackEvent
    3039 {
    3040     SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
    3041                   VBoxEventType_T aWhat)
    3042         : CallbackEvent(aVB, aWhat)
    3043         , machineId(aMachineId), snapshotId(aSnapshotId)
    3044         {}
    3045 
    3046     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
    3047     {
    3048         return aEvDesc.init(aSource, mWhat, machineId.toUtf16().raw(),
    3049                             snapshotId.toUtf16().raw());
    3050     }
    3051 
    3052     Guid machineId;
    3053     Guid snapshotId;
    3054 };
    3055 
    3056 /**
    3057  *  @note Doesn't lock any object.
    3058  */
    3059 void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
    3060 {
    3061     postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
    3062                                 VBoxEventType_OnSnapshotTaken));
    3063 }
    3064 
    3065 /**
    3066  *  @note Doesn't lock any object.
    3067  */
    3068 void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
    3069 {
    3070     postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
    3071                                 VBoxEventType_OnSnapshotDeleted));
    3072 }
    3073 
    3074 /**
    3075  *  @note Doesn't lock any object.
    3076  */
    3077 void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
    3078 {
    3079     postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
    3080                                 VBoxEventType_OnSnapshotChanged));
    3081 }
    3082 
    3083 /** Event for onGuestPropertyChange() */
    3084 struct GuestPropertyEvent : public VirtualBox::CallbackEvent
    3085 {
    3086     GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
    3087                        IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
    3088         : CallbackEvent(aVBox, VBoxEventType_OnGuestPropertyChanged),
    3089           machineId(aMachineId),
    3090           name(aName),
    3091           value(aValue),
    3092           flags(aFlags)
    3093     {}
    3094 
    3095     virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
    3096     {
    3097         return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
    3098                             machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
    3099     }
    3100 
    3101     Guid machineId;
    3102     Bstr name, value, flags;
    3103 };
    3104 
    3105 /**
    3106  *  @note Doesn't lock any object.
    3107  */
    3108 void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
    3109                                        IN_BSTR aValue, IN_BSTR aFlags)
    3110 {
    3111     postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
    3112 }
    3113 
    3114 /** Event for onMachineUninit(), this is not a CallbackEvent */
    3115 class MachineUninitEvent : public Event
    3116 {
    3117 public:
    3118 
    3119     MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
    3120         : mVirtualBox(aVirtualBox), mMachine(aMachine)
    3121     {
    3122         Assert(aVirtualBox);
    3123         Assert(aMachine);
    3124     }
    3125 
    3126     void *handler()
    3127     {
    3128 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    3129         /* Handle unregistering metrics here, as it is not vital to get
    3130          * it done immediately. It reduces the number of locks needed and
    3131          * the lock contention in SessionMachine::uninit. */
    3132         {
    3133             AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
    3134             mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
    3135         }
    3136 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    3137 
    3138         return NULL;
    3139     }
    3140 
    3141 private:
    3142 
    3143     /**
    3144      *  Note that this is a weak ref -- the CallbackEvent handler thread
    3145      *  is bound to the lifetime of the VirtualBox instance, so it's safe.
    3146      */
    3147     VirtualBox        *mVirtualBox;
    3148 
    3149     /** Reference to the machine object. */
    3150     ComObjPtr<Machine> mMachine;
    3151 };
    3152 
    3153 /**
    3154  *  Trigger internal event. This isn't meant to be signalled to clients.
    3155  *  @note Doesn't lock any object.
    3156  */
    3157 void VirtualBox::onMachineUninit(Machine *aMachine)
    3158 {
    3159     postEvent(new MachineUninitEvent(this, aMachine));
    3160 }
    3161 
    3162 /**
    3163  *  @note Doesn't lock any object.
    3164  */
    3165 void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
    3166                                NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
    3167                                IN_BSTR aGuestIp, uint16_t aGuestPort)
    3168 {
    3169     fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
    3170                          aHostPort, aGuestIp, aGuestPort);
    3171 }
    3172 
    3173 void VirtualBox::onNATNetworkChange(IN_BSTR aName)
    3174 {
    3175     fireNATNetworkChangedEvent(m->pEventSource, aName);
    3176 }
    3177 
    3178 void VirtualBox::onNATNetworkStartStop(IN_BSTR aName, BOOL fStart)
    3179 {
    3180     fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
    3181 }
    3182 void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled,
    3183                                      IN_BSTR aNetwork, IN_BSTR aGateway,
    3184                                      BOOL aAdvertiseDefaultIpv6RouteEnabled,
    3185                                      BOOL fNeedDhcpServer)
    3186 {
    3187     fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled,
    3188                                aNetwork, aGateway,
    3189                                aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
    3190 }
    3191 
    3192 void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6,
    3193                                          IN_BSTR aRuleName, NATProtocol_T proto,
    3194                                          IN_BSTR aHostIp, LONG aHostPort,
    3195                                          IN_BSTR aGuestIp, LONG aGuestPort)
    3196 {
    3197     fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create,
    3198                                    fIpv6, aRuleName, proto,
    3199                                    aHostIp, aHostPort,
    3200                                    aGuestIp, aGuestPort);
    3201 }
    3202 
    3203 /**
    3204  *  @note Locks this object for reading.
    3205  */
    3206 ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
    3207 {
    3208     ComObjPtr<GuestOSType> type;
    3209     AutoCaller autoCaller(this);
    3210     AssertComRCReturn(autoCaller.rc(), type);
    3211 
    3212     /* unknown type must always be the first */
    3213     ComAssertRet(m->allGuestOSTypes.size() > 0, type);
    3214 
    3215     return m->allGuestOSTypes.front();
    3216 }
    3217 
    3218 /**
    3219  * Returns the list of opened machines (machines having direct sessions opened
    3220  * by client processes) and optionally the list of direct session controls.
    3221  *
    3222  * @param aMachines     Where to put opened machines (will be empty if none).
    3223  * @param aControls     Where to put direct session controls (optional).
    3224  *
    3225  * @note The returned lists contain smart pointers. So, clear it as soon as
    3226  * it becomes no more necessary to release instances.
    3227  *
    3228  * @note It can be possible that a session machine from the list has been
    3229  * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
    3230  * when accessing unprotected data directly.
    3231  *
    3232  * @note Locks objects for reading.
    3233  */
    3234 void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
    3235                                    InternalControlList *aControls /*= NULL*/)
    3236 {
    3237     AutoCaller autoCaller(this);
    3238     AssertComRCReturnVoid(autoCaller.rc());
    3239 
    3240     aMachines.clear();
    3241     if (aControls)
    3242         aControls->clear();
    3243 
    3244     AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3245 
    3246     for (MachinesOList::iterator it = m->allMachines.begin();
    3247          it != m->allMachines.end();
    3248          ++it)
    3249     {
    3250         ComObjPtr<SessionMachine> sm;
    3251         ComPtr<IInternalSessionControl> ctl;
    3252         if ((*it)->isSessionOpen(sm, &ctl))
    3253         {
    3254             aMachines.push_back(sm);
    3255             if (aControls)
    3256                 aControls->push_back(ctl);
    3257         }
    3258     }
    3259 }
    3260 
    3261 /**
    3262  *  Searches for a machine object with the given ID in the collection
    3263  *  of registered machines.
    3264  *
    3265  * @param aId Machine UUID to look for.
    3266  * @param aPermitInaccessible If true, inaccessible machines will be found;
    3267  *                  if false, this will fail if the given machine is inaccessible.
    3268  * @param aSetError If true, set errorinfo if the machine is not found.
    3269  * @param aMachine Returned machine, if found.
    3270  * @return
    3271  */
    3272 HRESULT VirtualBox::findMachine(const Guid &aId,
    3273                                 bool fPermitInaccessible,
    3274                                 bool aSetError,
    3275                                 ComObjPtr<Machine> *aMachine /* = NULL */)
    3276 {
    3277     HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
    3278 
    3279     AutoCaller autoCaller(this);
    3280     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    3281 
    3282     {
    3283         AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3284 
    3285         for (MachinesOList::iterator it = m->allMachines.begin();
    3286              it != m->allMachines.end();
    3287              ++it)
    3288         {
    3289             ComObjPtr<Machine> pMachine = *it;
    3290 
    3291             if (!fPermitInaccessible)
    3292             {
    3293                 // skip inaccessible machines
    3294                 AutoCaller machCaller(pMachine);
    3295                 if (FAILED(machCaller.rc()))
    3296                     continue;
    3297             }
    3298 
    3299             if (pMachine->getId() == aId)
    3300             {
    3301                 rc = S_OK;
    3302                 if (aMachine)
    3303                     *aMachine = pMachine;
    3304                 break;
    3305             }
    3306         }
    3307     }
    3308 
    3309     if (aSetError && FAILED(rc))
    3310         rc = setError(rc,
    3311                       tr("Could not find a registered machine with UUID {%RTuuid}"),
    3312                       aId.raw());
    3313 
    3314     return rc;
    3315 }
    3316 
    3317 /**
    3318  * Searches for a machine object with the given name or location in the
    3319  * collection of registered machines.
    3320  *
    3321  * @param aName Machine name or location to look for.
    3322  * @param aSetError If true, set errorinfo if the machine is not found.
    3323  * @param aMachine Returned machine, if found.
    3324  * @return
    3325  */
    3326 HRESULT VirtualBox::findMachineByName(const Utf8Str &aName, bool aSetError,
    3327                                       ComObjPtr<Machine> *aMachine /* = NULL */)
    3328 {
    3329     HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
    3330 
    3331     AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3332     for (MachinesOList::iterator it = m->allMachines.begin();
    3333          it != m->allMachines.end();
    3334          ++it)
    3335     {
    3336         ComObjPtr<Machine> &pMachine = *it;
    3337         AutoCaller machCaller(pMachine);
    3338         if (machCaller.rc())
    3339             continue;       // we can't ask inaccessible machines for their names
    3340 
    3341         AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
    3342         if (pMachine->getName() == aName)
    3343         {
    3344             rc = S_OK;
    3345             if (aMachine)
    3346                 *aMachine = pMachine;
    3347             break;
    3348         }
    3349         if (!RTPathCompare(pMachine->getSettingsFileFull().c_str(), aName.c_str()))
    3350         {
    3351             rc = S_OK;
    3352             if (aMachine)
    3353                 *aMachine = pMachine;
    3354             break;
    3355         }
    3356     }
    3357 
    3358     if (aSetError && FAILED(rc))
    3359         rc = setError(rc,
    3360                       tr("Could not find a registered machine named '%s'"), aName.c_str());
    3361 
    3362     return rc;
    3363 }
    3364 
    3365 static HRESULT validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
    3366 {
    3367     /* empty strings are invalid */
    3368     if (aGroup.isEmpty())
    3369         return E_INVALIDARG;
    3370     /* the toplevel group is valid */
    3371     if (aGroup == "/")
    3372         return S_OK;
    3373     /* any other strings of length 1 are invalid */
    3374     if (aGroup.length() == 1)
    3375         return E_INVALIDARG;
    3376     /* must start with a slash */
    3377     if (aGroup.c_str()[0] != '/')
    3378         return E_INVALIDARG;
    3379     /* must not end with a slash */
    3380     if (aGroup.c_str()[aGroup.length() - 1] == '/')
    3381         return E_INVALIDARG;
    3382     /* check the group components */
    3383     const char *pStr = aGroup.c_str() + 1;  /* first char is /, skip it */
    3384     while (pStr)
    3385     {
    3386         char *pSlash = RTStrStr(pStr, "/");
    3387         if (pSlash)
    3388         {
    3389             /* no empty components (or // sequences in other words) */
    3390             if (pSlash == pStr)
    3391                 return E_INVALIDARG;
    3392             /* check if the machine name rules are violated, because that means
    3393              * the group components are too close to the limits. */
    3394             Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
    3395             Utf8Str tmp2(tmp);
    3396             sanitiseMachineFilename(tmp);
    3397             if (tmp != tmp2)
    3398                 return E_INVALIDARG;
    3399             if (fPrimary)
    3400             {
    3401                 HRESULT rc = pVirtualBox->findMachineByName(tmp,
    3402                                                             false /* aSetError */);
    3403                 if (SUCCEEDED(rc))
    3404                     return VBOX_E_VM_ERROR;
    3405             }
    3406             pStr = pSlash + 1;
    3407         }
    3408         else
    3409         {
    3410             /* check if the machine name rules are violated, because that means
    3411              * the group components is too close to the limits. */
    3412             Utf8Str tmp(pStr);
    3413             Utf8Str tmp2(tmp);
    3414             sanitiseMachineFilename(tmp);
    3415             if (tmp != tmp2)
    3416                 return E_INVALIDARG;
    3417             pStr = NULL;
    3418         }
    3419     }
    3420     return S_OK;
    3421 }
    3422 
    3423 /**
    3424  * Validates a machine group.
    3425  *
    3426  * @param aMachineGroup     Machine group.
    3427  * @param fPrimary          Set if this is the primary group.
    3428  *
    3429  * @return S_OK or E_INVALIDARG
    3430  */
    3431 HRESULT VirtualBox::validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
    3432 {
    3433     HRESULT rc = validateMachineGroupHelper(aGroup, fPrimary, this);
    3434     if (FAILED(rc))
    3435     {
    3436         if (rc == VBOX_E_VM_ERROR)
    3437             rc = setError(E_INVALIDARG,
    3438                           tr("Machine group '%s' conflicts with a virtual machine name"),
    3439                           aGroup.c_str());
    3440         else
    3441             rc = setError(rc,
    3442                           tr("Invalid machine group '%s'"),
    3443                           aGroup.c_str());
    3444     }
    3445     return rc;
    3446 }
    3447 
    3448 /**
    3449  * Takes a list of machine groups, and sanitizes/validates it.
    3450  *
    3451  * @param aMachineGroups    Safearray with the machine groups.
    3452  * @param pllMachineGroups  Pointer to list of strings for the result.
    3453  *
    3454  * @return S_OK or E_INVALIDARG
    3455  */
    3456 HRESULT VirtualBox::convertMachineGroups(ComSafeArrayIn(IN_BSTR, aMachineGroups), StringsList *pllMachineGroups)
    3457 {
    3458     pllMachineGroups->clear();
    3459     if (aMachineGroups)
    3460     {
    3461         com::SafeArray<IN_BSTR> machineGroups(ComSafeArrayInArg(aMachineGroups));
    3462         for (size_t i = 0; i < machineGroups.size(); i++)
    3463         {
    3464             Utf8Str group(machineGroups[i]);
    3465             if (group.length() == 0)
    3466                 group = "/";
    3467 
    3468             HRESULT rc = validateMachineGroup(group, i == 0);
    3469             if (FAILED(rc))
    3470                 return rc;
    3471 
    3472             /* no duplicates please */
    3473             if (   find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
    3474                 == pllMachineGroups->end())
    3475                 pllMachineGroups->push_back(group);
    3476         }
    3477         if (pllMachineGroups->size() == 0)
    3478             pllMachineGroups->push_back("/");
    3479     }
    3480     else
    3481         pllMachineGroups->push_back("/");
    3482 
    3483     return S_OK;
    3484 }
    3485 
    3486 /**
    3487  * Searches for a Medium object with the given ID in the list of registered
    3488  * hard disks.
    3489  *
    3490  * @param aId           ID of the hard disk. Must not be empty.
    3491  * @param aSetError     If @c true , the appropriate error info is set in case
    3492  *                      when the hard disk is not found.
    3493  * @param aHardDisk     Where to store the found hard disk object (can be NULL).
    3494  *
    3495  * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
    3496  *
    3497  * @note Locks the media tree for reading.
    3498  */
    3499 HRESULT VirtualBox::findHardDiskById(const Guid &id,
    3500                                      bool aSetError,
    3501                                      ComObjPtr<Medium> *aHardDisk /*= NULL*/)
    3502 {
    3503     AssertReturn(!id.isZero(), E_INVALIDARG);
    3504 
    3505     // we use the hard disks map, but it is protected by the
    3506     // hard disk _list_ lock handle
    3507     AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3508 
    3509     HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
    3510     if (it != m->mapHardDisks.end())
    3511     {
    3512         if (aHardDisk)
    3513             *aHardDisk = (*it).second;
    3514         return S_OK;
    3515     }
    3516 
    3517     if (aSetError)
    3518         return setError(VBOX_E_OBJECT_NOT_FOUND,
    3519                         tr("Could not find an open hard disk with UUID {%RTuuid}"),
    3520                         id.raw());
    3521 
    3522     return VBOX_E_OBJECT_NOT_FOUND;
    3523 }
    3524 
    3525 /**
    3526  * Searches for a Medium object with the given ID or location in the list of
    3527  * registered hard disks. If both ID and location are specified, the first
    3528  * object that matches either of them (not necessarily both) is returned.
    3529  *
    3530  * @param aLocation     Full location specification. Must not be empty.
    3531  * @param aSetError     If @c true , the appropriate error info is set in case
    3532  *                      when the hard disk is not found.
    3533  * @param aHardDisk     Where to store the found hard disk object (can be NULL).
    3534  *
    3535  * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
    3536  *
    3537  * @note Locks the media tree for reading.
    3538  */
    3539 HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
    3540                                            bool aSetError,
    3541                                            ComObjPtr<Medium> *aHardDisk /*= NULL*/)
    3542 {
    3543     AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
    3544 
    3545     // we use the hard disks map, but it is protected by the
    3546     // hard disk _list_ lock handle
    3547     AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3548 
    3549     for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
    3550          it != m->mapHardDisks.end();
    3551          ++it)
    3552     {
    3553         const ComObjPtr<Medium> &pHD = (*it).second;
    3554 
    3555         AutoCaller autoCaller(pHD);
    3556         if (FAILED(autoCaller.rc())) return autoCaller.rc();
    3557         AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
    3558 
    3559         Utf8Str strLocationFull = pHD->getLocationFull();
    3560 
    3561         if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
    3562         {
    3563             if (aHardDisk)
    3564                 *aHardDisk = pHD;
    3565             return S_OK;
    3566         }
    3567     }
    3568 
    3569     if (aSetError)
    3570         return setError(VBOX_E_OBJECT_NOT_FOUND,
    3571                         tr("Could not find an open hard disk with location '%s'"),
    3572                         strLocation.c_str());
    3573 
    3574     return VBOX_E_OBJECT_NOT_FOUND;
    3575 }
    3576 
    3577 /**
    3578  * Searches for a Medium object with the given ID or location in the list of
    3579  * registered DVD or floppy images, depending on the @a mediumType argument.
    3580  * If both ID and file path are specified, the first object that matches either
    3581  * of them (not necessarily both) is returned.
    3582  *
    3583  * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
    3584  * @param aId       ID of the image file (unused when NULL).
    3585  * @param aLocation Full path to the image file (unused when NULL).
    3586  * @param aSetError If @c true, the appropriate error info is set in case when
    3587  *                  the image is not found.
    3588  * @param aImage    Where to store the found image object (can be NULL).
    3589  *
    3590  * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
    3591  *
    3592  * @note Locks the media tree for reading.
    3593  */
    3594 HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
    3595                                          const Guid *aId,
    3596                                          const Utf8Str &aLocation,
    3597                                          bool aSetError,
    3598                                          ComObjPtr<Medium> *aImage /* = NULL */)
    3599 {
    3600     AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
    3601 
    3602     Utf8Str location;
    3603     if (!aLocation.isEmpty())
    3604     {
    3605         int vrc = calculateFullPath(aLocation, location);
    3606         if (RT_FAILURE(vrc))
    3607             return setError(VBOX_E_FILE_ERROR,
    3608                             tr("Invalid image file location '%s' (%Rrc)"),
    3609                             aLocation.c_str(),
    3610                             vrc);
    3611     }
    3612 
    3613     MediaOList *pMediaList;
    3614 
    3615     switch (mediumType)
    3616     {
    3617         case DeviceType_DVD:
    3618             pMediaList = &m->allDVDImages;
    3619         break;
    3620 
    3621         case DeviceType_Floppy:
    3622             pMediaList = &m->allFloppyImages;
    3623         break;
    3624 
    3625         default:
    3626             return E_INVALIDARG;
    3627     }
    3628 
    3629     AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3630 
    3631     bool found = false;
    3632 
    3633     for (MediaList::const_iterator it = pMediaList->begin();
    3634          it != pMediaList->end();
    3635          ++it)
    3636     {
    3637         // no AutoCaller, registered image life time is bound to this
    3638         Medium *pMedium = *it;
    3639         AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
    3640         const Utf8Str &strLocationFull = pMedium->getLocationFull();
    3641 
    3642         found =     (    aId
    3643                       && pMedium->getId() == *aId)
    3644                  || (    !aLocation.isEmpty()
    3645                       && RTPathCompare(location.c_str(),
    3646                                        strLocationFull.c_str()) == 0);
    3647         if (found)
    3648         {
    3649             if (pMedium->getDeviceType() != mediumType)
    3650             {
    3651                 if (mediumType == DeviceType_DVD)
    3652                     return setError(E_INVALIDARG,
    3653                                     "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
    3654                 else
    3655                     return setError(E_INVALIDARG,
    3656                                     "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
    3657             }
    3658 
    3659             if (aImage)
    3660                 *aImage = pMedium;
    3661             break;
    3662         }
    3663     }
    3664 
    3665     HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
    3666 
    3667     if (aSetError && !found)
    3668     {
    3669         if (aId)
    3670             setError(rc,
    3671                      tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
    3672                      aId->raw(),
    3673                      m->strSettingsFilePath.c_str());
    3674         else
    3675             setError(rc,
    3676                      tr("Could not find an image file with location '%s' in the media registry ('%s')"),
    3677                      aLocation.c_str(),
    3678                      m->strSettingsFilePath.c_str());
    3679     }
    3680 
    3681     return rc;
    3682 }
    3683 
    3684 /**
    3685  * Searches for an IMedium object that represents the given UUID.
    3686  *
    3687  * If the UUID is empty (indicating an empty drive), this sets pMedium
    3688  * to NULL and returns S_OK.
    3689  *
    3690  * If the UUID refers to a host drive of the given device type, this
    3691  * sets pMedium to the object from the list in IHost and returns S_OK.
    3692  *
    3693  * If the UUID is an image file, this sets pMedium to the object that
    3694  * findDVDOrFloppyImage() returned.
    3695  *
    3696  * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
    3697  *
    3698  * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
    3699  * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
    3700  * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
    3701  * @param pMedium out: IMedium object found.
    3702  * @return
    3703  */
    3704 HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
    3705                                          const Guid &uuid,
    3706                                          bool fRefresh,
    3707                                          bool aSetError,
    3708                                          ComObjPtr<Medium> &pMedium)
    3709 {
    3710     if (uuid.isZero())
    3711     {
    3712         // that's easy
    3713         pMedium.setNull();
    3714         return S_OK;
    3715     }
    3716     else if (!uuid.isValid())
    3717     {
    3718         /* handling of case invalid GUID */
    3719         return setError(VBOX_E_OBJECT_NOT_FOUND,
    3720                             tr("Guid '%ls' is invalid"),
    3721                             uuid.toString().c_str());
    3722     }
    3723 
    3724     // first search for host drive with that UUID
    3725     HRESULT rc = m->pHost->findHostDriveById(mediumType,
    3726                                              uuid,
    3727                                              fRefresh,
    3728                                              pMedium);
    3729     if (rc == VBOX_E_OBJECT_NOT_FOUND)
    3730                 // then search for an image with that UUID
    3731         rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
    3732 
    3733     return rc;
    3734 }
    3735 
    3736 HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
    3737                                     GuestOSType*& pGuestOSType)
    3738 {
    3739     /* Look for a GuestOSType object */
    3740     AssertMsg(m->allGuestOSTypes.size() != 0,
    3741               ("Guest OS types array must be filled"));
    3742 
    3743     if (bstrOSType.isEmpty())
    3744     {
    3745         pGuestOSType = NULL;
    3746         return S_OK;
    3747     }
    3748 
    3749     AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    3750     for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
    3751          it != m->allGuestOSTypes.end();
    3752          ++it)
    3753     {
    3754         if ((*it)->id() == bstrOSType)
    3755         {
    3756             pGuestOSType = *it;
    3757             return S_OK;
    3758         }
    3759     }
    3760 
    3761     return setError(VBOX_E_OBJECT_NOT_FOUND,
    3762                     tr("Guest OS type '%ls' is invalid"),
    3763                     bstrOSType.raw());
    3764 }
    3765 
    3766 /**
    3767  * Returns the constant pseudo-machine UUID that is used to identify the
    3768  * global media registry.
    3769  *
    3770  * Starting with VirtualBox 4.0 each medium remembers in its instance data
    3771  * in which media registry it is saved (if any): this can either be a machine
    3772  * UUID, if it's in a per-machine media registry, or this global ID.
    3773  *
    3774  * This UUID is only used to identify the VirtualBox object while VirtualBox
    3775  * is running. It is a compile-time constant and not saved anywhere.
    3776  *
    3777  * @return
    3778  */
    3779 const Guid& VirtualBox::getGlobalRegistryId() const
    3780 {
    3781     return m->uuidMediaRegistry;
    3782 }
    3783 
    3784 const ComObjPtr<Host>& VirtualBox::host() const
    3785 {
    3786     return m->pHost;
    3787 }
    3788 
    3789 SystemProperties* VirtualBox::getSystemProperties() const
    3790 {
    3791     return m->pSystemProperties;
    3792 }
    3793 
    3794 #ifdef VBOX_WITH_EXTPACK
    3795 /**
    3796  * Getter that SystemProperties and others can use to talk to the extension
    3797  * pack manager.
    3798  */
    3799 ExtPackManager* VirtualBox::getExtPackManager() const
    3800 {
    3801     return m->ptrExtPackManager;
    3802 }
    3803 #endif
    3804 
    3805 /**
    3806  * Getter that machines can talk to the autostart database.
    3807  */
    3808 AutostartDb* VirtualBox::getAutostartDb() const
    3809 {
    3810     return m->pAutostartDb;
    3811 }
    3812 
    3813 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    3814 const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
    3815 {
    3816     return m->pPerformanceCollector;
    3817 }
    3818 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    3819 
    3820 /**
    3821  * Returns the default machine folder from the system properties
    3822  * with proper locking.
    3823  * @return
    3824  */
    3825 void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
    3826 {
    3827     AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
    3828     str = m->pSystemProperties->m->strDefaultMachineFolder;
    3829 }
    3830 
    3831 /**
    3832  * Returns the default hard disk format from the system properties
    3833  * with proper locking.
    3834  * @return
    3835  */
    3836 void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
    3837 {
    3838     AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
    3839     str = m->pSystemProperties->m->strDefaultHardDiskFormat;
    3840 }
    3841 
    3842 const Utf8Str& VirtualBox::homeDir() const
    3843 {
    3844     return m->strHomeDir;
    3845 }
    3846 
    3847 /**
    3848  * Calculates the absolute path of the given path taking the VirtualBox home
    3849  * directory as the current directory.
    3850  *
    3851  * @param  aPath    Path to calculate the absolute path for.
    3852  * @param  aResult  Where to put the result (used only on success, can be the
    3853  *                  same Utf8Str instance as passed in @a aPath).
    3854  * @return IPRT result.
    3855  *
    3856  * @note Doesn't lock any object.
    3857  */
    3858 int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
    3859 {
    3860     AutoCaller autoCaller(this);
    3861     AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
    3862 
    3863     /* no need to lock since mHomeDir is const */
    3864 
    3865     char folder[RTPATH_MAX];
    3866     int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
    3867                           strPath.c_str(),
    3868                           folder,
    3869                           sizeof(folder));
    3870     if (RT_SUCCESS(vrc))
    3871         aResult = folder;
    3872 
    3873     return vrc;
    3874 }
    3875 
    3876 /**
    3877  * Copies strSource to strTarget, making it relative to the VirtualBox config folder
    3878  * if it is a subdirectory thereof, or simply copying it otherwise.
    3879  *
    3880  * @param strSource Path to evalue and copy.
    3881  * @param strTarget Buffer to receive target path.
    3882  */
    3883 void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
    3884                                           Utf8Str &strTarget)
    3885 {
    3886     AutoCaller autoCaller(this);
    3887     AssertComRCReturnVoid(autoCaller.rc());
    3888 
    3889     // no need to lock since mHomeDir is const
    3890 
    3891     // use strTarget as a temporary buffer to hold the machine settings dir
    3892     strTarget = m->strHomeDir;
    3893     if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
    3894         // is relative: then append what's left
    3895         strTarget.append(strSource.c_str() + strTarget.length());     // include '/'
    3896     else
    3897         // is not relative: then overwrite
    3898         strTarget = strSource;
    3899 }
    3900 
    3901 // private methods
    3902 /////////////////////////////////////////////////////////////////////////////
    3903 
    3904 /**
    3905  * Checks if there is a hard disk, DVD or floppy image with the given ID or
    3906  * location already registered.
    3907  *
    3908  * On return, sets @a aConflict to the string describing the conflicting medium,
    3909  * or sets it to @c Null if no conflicting media is found. Returns S_OK in
    3910  * either case. A failure is unexpected.
    3911  *
    3912  * @param aId           UUID to check.
    3913  * @param aLocation     Location to check.
    3914  * @param aConflict     Where to return parameters of the conflicting medium.
    3915  * @param ppMedium      Medium reference in case this is simply a duplicate.
    3916  *
    3917  * @note Locks the media tree and media objects for reading.
    3918  */
    3919 HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
    3920                                            const Utf8Str &aLocation,
    3921                                            Utf8Str &aConflict,
    3922                                            ComObjPtr<Medium> *ppMedium)
    3923 {
    3924     AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
    3925     AssertReturn(ppMedium, E_INVALIDARG);
    3926 
    3927     aConflict.setNull();
    3928     ppMedium->setNull();
    3929 
    3930     AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3931 
    3932     HRESULT rc = S_OK;
    3933 
    3934     ComObjPtr<Medium> pMediumFound;
    3935     const char *pcszType = NULL;
    3936 
    3937     if (aId.isValid() && !aId.isZero())
    3938         rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
    3939     if (FAILED(rc) && !aLocation.isEmpty())
    3940         rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
    3941     if (SUCCEEDED(rc))
    3942         pcszType = tr("hard disk");
    3943 
    3944     if (!pcszType)
    3945     {
    3946         rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
    3947         if (SUCCEEDED(rc))
    3948             pcszType = tr("CD/DVD image");
    3949     }
    3950 
    3951     if (!pcszType)
    3952     {
    3953         rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
    3954         if (SUCCEEDED(rc))
    3955             pcszType = tr("floppy image");
    3956     }
    3957 
    3958     if (pcszType && pMediumFound)
    3959     {
    3960         /* Note: no AutoCaller since bound to this */
    3961         AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
    3962 
    3963         Utf8Str strLocFound = pMediumFound->getLocationFull();
    3964         Guid idFound = pMediumFound->getId();
    3965 
    3966         if (    (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
    3967              && (idFound == aId)
    3968            )
    3969             *ppMedium = pMediumFound;
    3970 
    3971         aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
    3972                                pcszType,
    3973                                strLocFound.c_str(),
    3974                                idFound.raw());
    3975     }
    3976 
    3977     return S_OK;
    3978 }
    3979 
    3980 /**
    3981  * Checks whether the given UUID is already in use by one medium for the
    3982  * given device type.
    3983  *
    3984  * @returns true if the UUID is already in use
    3985  *          fale otherwise
    3986  * @param   aId           The UUID to check.
    3987  * @param   deviceType    The device type the UUID is going to be checked for
    3988  *                        conflicts.
    3989  */
    3990 bool VirtualBox::isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
    3991 {
    3992     /* A zero UUID is invalid here, always claim that it is already used. */
    3993     AssertReturn(!aId.isZero(), true);
    3994 
    3995     AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3996 
    3997     HRESULT rc = S_OK;
    3998     bool fInUse = false;
    3999 
    4000     ComObjPtr<Medium> pMediumFound;
    4001 
    4002     switch (deviceType)
    4003     {
    4004         case DeviceType_HardDisk:
    4005             rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
    4006             break;
    4007         case DeviceType_DVD:
    4008             rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
    4009             break;
    4010         case DeviceType_Floppy:
    4011             rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
    4012             break;
    4013         default:
    4014             AssertMsgFailed(("Invalid device type %d\n", deviceType));
    4015     }
    4016 
    4017     if (SUCCEEDED(rc) && pMediumFound)
    4018         fInUse = true;
    4019 
    4020     return fInUse;
    4021 }
    4022 
    4023 /**
    4024  * Called from Machine::prepareSaveSettings() when it has detected
    4025  * that a machine has been renamed. Such renames will require
    4026  * updating the global media registry during the
    4027  * VirtualBox::saveSettings() that follows later.
    4028 *
    4029  * When a machine is renamed, there may well be media (in particular,
    4030  * diff images for snapshots) in the global registry that will need
    4031  * to have their paths updated. Before 3.2, Machine::saveSettings
    4032  * used to call VirtualBox::saveSettings implicitly, which was both
    4033  * unintuitive and caused locking order problems. Now, we remember
    4034  * such pending name changes with this method so that
    4035  * VirtualBox::saveSettings() can process them properly.
    4036  */
    4037 void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
    4038                                                    const Utf8Str &strNewConfigDir)
    4039 {
    4040     AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4041 
    4042     Data::PendingMachineRename pmr;
    4043     pmr.strConfigDirOld = strOldConfigDir;
    4044     pmr.strConfigDirNew = strNewConfigDir;
    4045     m->llPendingMachineRenames.push_back(pmr);
    4046 }
    4047 
    4048 struct SaveMediaRegistriesDesc
    4049 {
    4050     MediaList llMedia;
    4051     ComObjPtr<VirtualBox> pVirtualBox;
    4052 };
    4053 
    4054 static int fntSaveMediaRegistries(RTTHREAD ThreadSelf, void *pvUser)
    4055 {
    4056     NOREF(ThreadSelf);
    4057     SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
    4058     if (!pDesc)
    4059     {
    4060         LogRelFunc(("Thread for saving media registries lacks parameters\n"));
    4061         return VERR_INVALID_PARAMETER;
    4062     }
    4063 
    4064     for (MediaList::const_iterator it = pDesc->llMedia.begin();
    4065          it != pDesc->llMedia.end();
    4066          ++it)
    4067     {
    4068         Medium *pMedium = *it;
    4069         pMedium->markRegistriesModified();
    4070     }
    4071 
    4072     pDesc->pVirtualBox->saveModifiedRegistries();
    4073 
    4074     pDesc->llMedia.clear();
    4075     pDesc->pVirtualBox.setNull();
    4076     delete pDesc;
    4077 
    4078     return VINF_SUCCESS;
    4079 }
    4080 
    4081 /**
    4082  * Goes through all known media (hard disks, floppies and DVDs) and saves
    4083  * those into the given settings::MediaRegistry structures whose registry
    4084  * ID match the given UUID.
    4085  *
    4086  * Before actually writing to the structures, all media paths (not just the
    4087  * ones for the given registry) are updated if machines have been renamed
    4088  * since the last call.
    4089  *
    4090  * This gets called from two contexts:
    4091  *
    4092  *  -- VirtualBox::saveSettings() with the UUID of the global registry
    4093  *     (VirtualBox::Data.uuidRegistry); this will save those media
    4094  *     which had been loaded from the global registry or have been
    4095  *     attached to a "legacy" machine which can't save its own registry;
    4096  *
    4097  *  -- Machine::saveSettings() with the UUID of a machine, if a medium
    4098  *     has been attached to a machine created with VirtualBox 4.0 or later.
    4099  *
    4100  * Media which have only been temporarily opened without having been
    4101  * attached to a machine have a NULL registry UUID and therefore don't
    4102  * get saved.
    4103  *
    4104  * This locks the media tree. Throws HRESULT on errors!
    4105  *
    4106  * @param mediaRegistry Settings structure to fill.
    4107  * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
    4108  * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
    4109  */
    4110 void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
    4111                                    const Guid &uuidRegistry,
    4112                                    const Utf8Str &strMachineFolder)
    4113 {
    4114     // lock all media for the following; use a write lock because we're
    4115     // modifying the PendingMachineRenamesList, which is protected by this
    4116     AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4117 
    4118     // if a machine was renamed, then we'll need to refresh media paths
    4119     if (m->llPendingMachineRenames.size())
    4120     {
    4121         // make a single list from the three media lists so we don't need three loops
    4122         MediaList llAllMedia;
    4123         // with hard disks, we must use the map, not the list, because the list only has base images
    4124         for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
    4125             llAllMedia.push_back(it->second);
    4126         for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
    4127             llAllMedia.push_back(*it);
    4128         for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
    4129             llAllMedia.push_back(*it);
    4130 
    4131         SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
    4132         for (MediaList::iterator it = llAllMedia.begin();
    4133              it != llAllMedia.end();
    4134              ++it)
    4135         {
    4136             Medium *pMedium = *it;
    4137             for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
    4138                  it2 != m->llPendingMachineRenames.end();
    4139                  ++it2)
    4140             {
    4141                 const Data::PendingMachineRename &pmr = *it2;
    4142                 HRESULT rc = pMedium->updatePath(pmr.strConfigDirOld,
    4143                                                  pmr.strConfigDirNew);
    4144                 if (SUCCEEDED(rc))
    4145                 {
    4146                     // Remember which medium objects has been changed,
    4147                     // to trigger saving their registries later.
    4148                     pDesc->llMedia.push_back(pMedium);
    4149                 } else if (rc == VBOX_E_FILE_ERROR)
    4150                     /* nothing */;
    4151                 else
    4152                     AssertComRC(rc);
    4153             }
    4154         }
    4155         // done, don't do it again until we have more machine renames
    4156         m->llPendingMachineRenames.clear();
    4157 
    4158         if (pDesc->llMedia.size())
    4159         {
    4160             // Handle the media registry saving in a separate thread, to
    4161             // avoid giant locking problems and passing up the list many
    4162             // levels up to whoever triggered saveSettings, as there are
    4163             // lots of places which would need to handle saving more settings.
    4164             pDesc->pVirtualBox = this;
    4165             int vrc = RTThreadCreate(NULL,
    4166                                      fntSaveMediaRegistries,
    4167                                      (void *)pDesc,
    4168                                      0,     // cbStack (default)
    4169                                      RTTHREADTYPE_MAIN_WORKER,
    4170                                      0,     // flags
    4171                                      "SaveMediaReg");
    4172             ComAssertRC(vrc);
    4173             // failure means that settings aren't saved, but there isn't
    4174             // much we can do besides avoiding memory leaks
    4175             if (RT_FAILURE(vrc))
    4176             {
    4177                 LogRelFunc(("Failed to create thread for saving media registries (%Rrc)\n", vrc));
    4178                 delete pDesc;
    4179             }
    4180         }
    4181         else
    4182             delete pDesc;
    4183     }
    4184 
    4185     struct {
    4186         MediaOList &llSource;
    4187         settings::MediaList &llTarget;
    4188     } s[] =
    4189     {
    4190         // hard disks
    4191         { m->allHardDisks, mediaRegistry.llHardDisks },
    4192         // CD/DVD images
    4193         { m->allDVDImages, mediaRegistry.llDvdImages },
    4194         // floppy images
    4195         { m->allFloppyImages, mediaRegistry.llFloppyImages }
    4196     };
    4197 
    4198     HRESULT rc;
    4199 
    4200     for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
    4201     {
    4202         MediaOList &llSource = s[i].llSource;
    4203         settings::MediaList &llTarget = s[i].llTarget;
    4204         llTarget.clear();
    4205         for (MediaList::const_iterator it = llSource.begin();
    4206              it != llSource.end();
    4207              ++it)
    4208         {
    4209             Medium *pMedium = *it;
    4210             AutoCaller autoCaller(pMedium);
    4211             if (FAILED(autoCaller.rc())) throw autoCaller.rc();
    4212             AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    4213 
    4214             if (pMedium->isInRegistry(uuidRegistry))
    4215             {
    4216                 settings::Medium med;
    4217                 rc = pMedium->saveSettings(med, strMachineFolder);     // this recurses into child hard disks
    4218                 if (FAILED(rc)) throw rc;
    4219                 llTarget.push_back(med);
    4220             }
    4221         }
    4222     }
    4223 }
    4224 
    4225 /**
    4226  *  Helper function which actually writes out VirtualBox.xml, the main configuration file.
    4227  *  Gets called from the public VirtualBox::SaveSettings() as well as from various other
    4228  *  places internally when settings need saving.
    4229  *
    4230  *  @note Caller must have locked the VirtualBox object for writing and must not hold any
    4231  *    other locks since this locks all kinds of member objects and trees temporarily,
    4232  *    which could cause conflicts.
    4233  */
    4234 HRESULT VirtualBox::saveSettings()
    4235 {
    4236     AutoCaller autoCaller(this);
    4237     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    4238 
    4239     AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
    4240     AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
    4241 
    4242     HRESULT rc = S_OK;
    4243 
    4244     try
    4245     {
    4246         // machines
    4247         m->pMainConfigFile->llMachines.clear();
    4248         {
    4249             AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4250             for (MachinesOList::iterator it = m->allMachines.begin();
    4251                  it != m->allMachines.end();
    4252                  ++it)
    4253             {
    4254                 Machine *pMachine = *it;
    4255                 // save actual machine registry entry
    4256                 settings::MachineRegistryEntry mre;
    4257                 rc = pMachine->saveRegistryEntry(mre);
    4258                 m->pMainConfigFile->llMachines.push_back(mre);
    4259             }
    4260         }
    4261 
    4262         saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
    4263                           m->uuidMediaRegistry,         // global media registry ID
    4264                           Utf8Str::Empty);              // strMachineFolder
    4265 
    4266         m->pMainConfigFile->llDhcpServers.clear();
    4267         {
    4268             AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4269             for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
    4270                  it != m->allDHCPServers.end();
    4271                  ++it)
    4272             {
    4273                 settings::DHCPServer d;
    4274                 rc = (*it)->saveSettings(d);
    4275                 if (FAILED(rc)) throw rc;
    4276                 m->pMainConfigFile->llDhcpServers.push_back(d);
    4277             }
    4278         }
    4279 
    4280 #ifdef VBOX_WITH_NAT_SERVICE
    4281         /* Saving NAT Network configuration */
    4282         m->pMainConfigFile->llNATNetworks.clear();
    4283         {
    4284             AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4285             for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
    4286                  it != m->allNATNetworks.end();
    4287                  ++it)
    4288             {
    4289                 settings::NATNetwork n;
    4290                 rc = (*it)->saveSettings(n);
    4291                 if (FAILED(rc)) throw rc;
    4292                 m->pMainConfigFile->llNATNetworks.push_back(n);
    4293             }
    4294         }
    4295 #endif
    4296 
    4297         // leave extra data alone, it's still in the config file
    4298 
    4299         // host data (USB filters)
    4300         rc = m->pHost->saveSettings(m->pMainConfigFile->host);
    4301         if (FAILED(rc)) throw rc;
    4302 
    4303         rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
    4304         if (FAILED(rc)) throw rc;
    4305 
    4306         // and write out the XML, still under the lock
    4307         m->pMainConfigFile->write(m->strSettingsFilePath);
    4308     }
    4309     catch (HRESULT err)
    4310     {
    4311         /* we assume that error info is set by the thrower */
    4312         rc = err;
    4313     }
    4314     catch (...)
    4315     {
    4316         rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    4317     }
    4318 
    4319     return rc;
    4320 }
    4321 
    4322 /**
    4323  *  Helper to register the machine.
    4324  *
    4325  *  When called during VirtualBox startup, adds the given machine to the
    4326  *  collection of registered machines. Otherwise tries to mark the machine
    4327  *  as registered, and, if succeeded, adds it to the collection and
    4328  *  saves global settings.
    4329  *
    4330  *  @note The caller must have added itself as a caller of the @a aMachine
    4331  *  object if calls this method not on VirtualBox startup.
    4332  *
    4333  *  @param aMachine     machine to register
    4334  *
    4335  *  @note Locks objects!
    4336  */
    4337 HRESULT VirtualBox::registerMachine(Machine *aMachine)
    4338 {
    4339     ComAssertRet(aMachine, E_INVALIDARG);
    4340 
    4341     AutoCaller autoCaller(this);
    4342     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4343 
    4344     HRESULT rc = S_OK;
    4345 
    4346     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4347 
    4348     {
    4349         ComObjPtr<Machine> pMachine;
    4350         rc = findMachine(aMachine->getId(),
    4351                          true /* fPermitInaccessible */,
    4352                          false /* aDoSetError */,
    4353                          &pMachine);
    4354         if (SUCCEEDED(rc))
    4355         {
    4356             /* sanity */
    4357             AutoLimitedCaller machCaller(pMachine);
    4358             AssertComRC(machCaller.rc());
    4359 
    4360             return setError(E_INVALIDARG,
    4361                             tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
    4362                             aMachine->getId().raw(),
    4363                             pMachine->getSettingsFileFull().c_str());
    4364         }
    4365 
    4366         ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
    4367         rc = S_OK;
    4368     }
    4369 
    4370     if (autoCaller.state() != InInit)
    4371     {
    4372         rc = aMachine->prepareRegister();
    4373         if (FAILED(rc)) return rc;
    4374     }
    4375 
    4376     /* add to the collection of registered machines */
    4377     m->allMachines.addChild(aMachine);
    4378 
    4379     if (autoCaller.state() != InInit)
    4380         rc = saveSettings();
    4381 
    4382     return rc;
    4383 }
    4384 
    4385 /**
    4386  * Remembers the given medium object by storing it in either the global
    4387  * medium registry or a machine one.
    4388  *
    4389  * @note Caller must hold the media tree lock for writing; in addition, this
    4390  * locks @a pMedium for reading
    4391  *
    4392  * @param pMedium   Medium object to remember.
    4393  * @param ppMedium  Actually stored medium object. Can be different if due
    4394  *                  to an unavoidable race there was a duplicate Medium object
    4395  *                  created.
    4396  * @param argType   Either DeviceType_HardDisk, DeviceType_DVD or DeviceType_Floppy.
    4397  * @return
    4398  */
    4399 HRESULT VirtualBox::registerMedium(const ComObjPtr<Medium> &pMedium,
    4400                                    ComObjPtr<Medium> *ppMedium,
    4401                                    DeviceType_T argType)
    4402 {
    4403     AssertReturn(pMedium != NULL, E_INVALIDARG);
    4404     AssertReturn(ppMedium != NULL, E_INVALIDARG);
    4405 
    4406     AutoCaller autoCaller(this);
    4407     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    4408 
    4409     AutoCaller mediumCaller(pMedium);
    4410     AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
    4411 
    4412     const char *pszDevType = NULL;
    4413     ObjectsList<Medium> *pall = NULL;
    4414     switch (argType)
    4415     {
    4416         case DeviceType_HardDisk:
    4417             pall = &m->allHardDisks;
    4418             pszDevType = tr("hard disk");
    4419             break;
    4420         case DeviceType_DVD:
    4421             pszDevType = tr("DVD image");
    4422             pall = &m->allDVDImages;
    4423             break;
    4424         case DeviceType_Floppy:
    4425             pszDevType = tr("floppy image");
    4426             pall = &m->allFloppyImages;
    4427             break;
    4428         default:
    4429             AssertMsgFailedReturn(("invalid device type %d", argType), E_INVALIDARG);
    4430     }
    4431 
    4432     // caller must hold the media tree write lock
    4433     Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    4434 
    4435     Guid id;
    4436     Utf8Str strLocationFull;
    4437     ComObjPtr<Medium> pParent;
    4438     {
    4439         AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
    4440         id = pMedium->getId();
    4441         strLocationFull = pMedium->getLocationFull();
    4442         pParent = pMedium->getParent();
    4443     }
    4444 
    4445     HRESULT rc;
    4446 
    4447     Utf8Str strConflict;
    4448     ComObjPtr<Medium> pDupMedium;
    4449     rc = checkMediaForConflicts(id,
    4450                                 strLocationFull,
    4451                                 strConflict,
    4452                                 &pDupMedium);
    4453     if (FAILED(rc)) return rc;
    4454 
    4455     if (pDupMedium.isNull())
    4456     {
    4457         if (strConflict.length())
    4458             return setError(E_INVALIDARG,
    4459                             tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
    4460                             pszDevType,
    4461                             strLocationFull.c_str(),
    4462                             id.raw(),
    4463                             strConflict.c_str(),
    4464                             m->strSettingsFilePath.c_str());
    4465 
    4466         // add to the collection if it is a base medium
    4467         if (pParent.isNull())
    4468             pall->getList().push_back(pMedium);
    4469 
    4470         // store all hard disks (even differencing images) in the map
    4471         if (argType == DeviceType_HardDisk)
    4472             m->mapHardDisks[id] = pMedium;
    4473 
    4474         *ppMedium = pMedium;
    4475     }
    4476     else
    4477     {
    4478         // pMedium may be the last reference to the Medium object, and the
    4479         // caller may have specified the same ComObjPtr as the output parameter.
    4480         // In this case the assignment will uninit the object, and we must not
    4481         // have a caller pending.
    4482         mediumCaller.release();
    4483         *ppMedium = pDupMedium;
    4484     }
    4485 
    4486     return rc;
    4487 }
    4488 
    4489 /**
    4490  * Removes the given medium from the respective registry.
    4491  *
    4492  * @param pMedium    Hard disk object to remove.
    4493  *
    4494  * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
    4495  */
    4496 HRESULT VirtualBox::unregisterMedium(Medium *pMedium)
    4497 {
    4498     AssertReturn(pMedium != NULL, E_INVALIDARG);
    4499 
    4500     AutoCaller autoCaller(this);
    4501     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    4502 
    4503     AutoCaller mediumCaller(pMedium);
    4504     AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
    4505 
    4506     // caller must hold the media tree write lock
    4507     Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    4508 
    4509     Guid id;
    4510     ComObjPtr<Medium> pParent;
    4511     DeviceType_T devType;
    4512     {
    4513         AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
    4514         id = pMedium->getId();
    4515         pParent = pMedium->getParent();
    4516         devType = pMedium->getDeviceType();
    4517     }
    4518 
    4519     ObjectsList<Medium> *pall = NULL;
    4520     switch (devType)
    4521     {
    4522         case DeviceType_HardDisk:
    4523             pall = &m->allHardDisks;
    4524             break;
    4525         case DeviceType_DVD:
    4526             pall = &m->allDVDImages;
    4527             break;
    4528         case DeviceType_Floppy:
    4529             pall = &m->allFloppyImages;
    4530             break;
    4531         default:
    4532             AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
    4533     }
    4534 
    4535     // remove from the collection if it is a base medium
    4536     if (pParent.isNull())
    4537         pall->getList().remove(pMedium);
    4538 
    4539     // remove all hard disks (even differencing images) from map
    4540     if (devType == DeviceType_HardDisk)
    4541     {
    4542         size_t cnt = m->mapHardDisks.erase(id);
    4543         Assert(cnt == 1);
    4544         NOREF(cnt);
    4545     }
    4546 
    4547     return S_OK;
    4548 }
    4549 
    4550 /**
    4551  * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
    4552  * with children appearing before their parents.
    4553  * @param llMedia
    4554  * @param pMedium
    4555  */
    4556 void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
    4557 {
    4558     // recurse first, then add ourselves; this way children end up on the
    4559     // list before their parents
    4560 
    4561     const MediaList &llChildren = pMedium->getChildren();
    4562     for (MediaList::const_iterator it = llChildren.begin();
    4563          it != llChildren.end();
    4564          ++it)
    4565     {
    4566         Medium *pChild = *it;
    4567         pushMediumToListWithChildren(llMedia, pChild);
    4568     }
    4569 
    4570     Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
    4571     llMedia.push_back(pMedium);
    4572 }
    4573 
    4574 /**
    4575  * Unregisters all Medium objects which belong to the given machine registry.
    4576  * Gets called from Machine::uninit() just before the machine object dies
    4577  * and must only be called with a machine UUID as the registry ID.
    4578  *
    4579  * Locks the media tree.
    4580  *
    4581  * @param uuidMachine Medium registry ID (always a machine UUID)
    4582  * @return
    4583  */
    4584 HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
    4585 {
    4586     Assert(!uuidMachine.isZero() && uuidMachine.isValid());
    4587 
    4588     LogFlowFuncEnter();
    4589 
    4590     AutoCaller autoCaller(this);
    4591     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    4592 
    4593     MediaList llMedia2Close;
    4594 
    4595     {
    4596         AutoWriteLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4597 
    4598         for (MediaOList::iterator it = m->allHardDisks.getList().begin();
    4599              it != m->allHardDisks.getList().end();
    4600              ++it)
    4601         {
    4602             ComObjPtr<Medium> pMedium = *it;
    4603             AutoCaller medCaller(pMedium);
    4604             if (FAILED(medCaller.rc())) return medCaller.rc();
    4605             AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
    4606 
    4607             if (pMedium->isInRegistry(uuidMachine))
    4608                 // recursively with children first
    4609                 pushMediumToListWithChildren(llMedia2Close, pMedium);
    4610         }
    4611     }
    4612 
    4613     for (MediaList::iterator it = llMedia2Close.begin();
    4614          it != llMedia2Close.end();
    4615          ++it)
    4616     {
    4617         ComObjPtr<Medium> pMedium = *it;
    4618         Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
    4619         AutoCaller mac(pMedium);
    4620         pMedium->close(mac);
    4621     }
    4622 
    4623     LogFlowFuncLeave();
    4624 
    4625     return S_OK;
    4626 }
    4627 
    4628 /**
    4629  * Removes the given machine object from the internal list of registered machines.
    4630  * Called from Machine::Unregister().
    4631  * @param pMachine
    4632  * @param id  UUID of the machine. Must be passed by caller because machine may be dead by this time.
    4633  * @return
    4634  */
    4635 HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
    4636                                       const Guid &id)
    4637 {
    4638     // remove from the collection of registered machines
    4639     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4640     m->allMachines.removeChild(pMachine);
    4641     // save the global registry
    4642     HRESULT rc = saveSettings();
    4643     alock.release();
    4644 
    4645     /*
    4646      * Now go over all known media and checks if they were registered in the
    4647      * media registry of the given machine. Each such medium is then moved to
    4648      * a different media registry to make sure it doesn't get lost since its
    4649      * media registry is about to go away.
    4650      *
    4651      * This fixes the following use case: Image A.vdi of machine A is also used
    4652      * by machine B, but registered in the media registry of machine A. If machine
    4653      * A is deleted, A.vdi must be moved to the registry of B, or else B will
    4654      * become inaccessible.
    4655      */
    4656     {
    4657         AutoReadLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4658         // iterate over the list of *base* images
    4659         for (MediaOList::iterator it = m->allHardDisks.getList().begin();
    4660              it != m->allHardDisks.getList().end();
    4661              ++it)
    4662         {
    4663             ComObjPtr<Medium> &pMedium = *it;
    4664             AutoCaller medCaller(pMedium);
    4665             if (FAILED(medCaller.rc())) return medCaller.rc();
    4666             AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    4667 
    4668             if (pMedium->removeRegistry(id, true /* fRecurse */))
    4669             {
    4670                 // machine ID was found in base medium's registry list:
    4671                 // move this base image and all its children to another registry then
    4672                 // 1) first, find a better registry to add things to
    4673                 const Guid *puuidBetter = pMedium->getAnyMachineBackref();
    4674                 if (puuidBetter)
    4675                 {
    4676                     // 2) better registry found: then use that
    4677                     pMedium->addRegistry(*puuidBetter, true /* fRecurse */);
    4678                     // 3) and make sure the registry is saved below
    4679                     mlock.release();
    4680                     tlock.release();
    4681                     markRegistryModified(*puuidBetter);
    4682                     tlock.acquire();
    4683                     mlock.release();
    4684                 }
    4685             }
    4686         }
    4687     }
    4688 
    4689     saveModifiedRegistries();
    4690 
    4691     /* fire an event */
    4692     onMachineRegistered(id, FALSE);
    4693 
    4694     return rc;
    4695 }
    4696 
    4697 /**
    4698  * Marks the registry for @a uuid as modified, so that it's saved in a later
    4699  * call to saveModifiedRegistries().
    4700  *
    4701  * @param uuid
    4702  */
    4703 void VirtualBox::markRegistryModified(const Guid &uuid)
    4704 {
    4705     if (uuid == getGlobalRegistryId())
    4706         ASMAtomicIncU64(&m->uRegistryNeedsSaving);
    4707     else
    4708     {
    4709         ComObjPtr<Machine> pMachine;
    4710         HRESULT rc = findMachine(uuid,
    4711                                  false /* fPermitInaccessible */,
    4712                                  false /* aSetError */,
    4713                                  &pMachine);
    4714         if (SUCCEEDED(rc))
    4715         {
    4716             AutoCaller machineCaller(pMachine);
    4717             if (SUCCEEDED(machineCaller.rc()))
    4718                 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
    4719         }
    4720     }
    4721 }
    4722 
    4723 /**
    4724  * Saves all settings files according to the modified flags in the Machine
    4725  * objects and in the VirtualBox object.
    4726  *
    4727  * This locks machines and the VirtualBox object as necessary, so better not
    4728  * hold any locks before calling this.
    4729  *
    4730  * @return
    4731  */
    4732 void VirtualBox::saveModifiedRegistries()
    4733 {
    4734     HRESULT rc = S_OK;
    4735     bool fNeedsGlobalSettings = false;
    4736     uint64_t uOld;
    4737 
    4738     {
    4739         AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4740         for (MachinesOList::iterator it = m->allMachines.begin();
    4741              it != m->allMachines.end();
    4742              ++it)
    4743         {
    4744             const ComObjPtr<Machine> &pMachine = *it;
    4745 
    4746             for (;;)
    4747             {
    4748                 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
    4749                 if (!uOld)
    4750                     break;
    4751                 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
    4752                     break;
    4753                 ASMNopPause();
    4754             }
    4755             if (uOld)
    4756             {
    4757                 AutoCaller autoCaller(pMachine);
    4758                 if (FAILED(autoCaller.rc()))
    4759                     continue;
    4760                 /* object is already dead, no point in saving settings */
    4761                 if (autoCaller.state() != Ready)
    4762                     continue;
    4763                 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
    4764                 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
    4765                                             Machine::SaveS_Force);           // caller said save, so stop arguing
    4766             }
    4767         }
    4768     }
    4769 
    4770     for (;;)
    4771     {
    4772         uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
    4773         if (!uOld)
    4774             break;
    4775         if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
    4776             break;
    4777         ASMNopPause();
    4778     }
    4779     if (uOld || fNeedsGlobalSettings)
    4780     {
    4781         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4782         rc = saveSettings();
    4783     }
    4784     NOREF(rc); /* XXX */
    4785 }
    4786 
    4787 
    4788 /* static */
    4789 const Bstr &VirtualBox::getVersionNormalized()
    4790 {
    4791     return sVersionNormalized;
    4792 }
    4793 
    4794 /**
    4795  * Checks if the path to the specified file exists, according to the path
    4796  * information present in the file name. Optionally the path is created.
    4797  *
    4798  * Note that the given file name must contain the full path otherwise the
    4799  * extracted relative path will be created based on the current working
    4800  * directory which is normally unknown.
    4801  *
    4802  * @param aFileName     Full file name which path is checked/created.
    4803  * @param aCreate       Flag if the path should be created if it doesn't exist.
    4804  *
    4805  * @return Extended error information on failure to check/create the path.
    4806  */
    4807 /* static */
    4808 HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName, bool fCreate)
    4809 {
    4810     Utf8Str strDir(strFileName);
    4811     strDir.stripFilename();
    4812     if (!RTDirExists(strDir.c_str()))
    4813     {
    4814         if (fCreate)
    4815         {
    4816             int vrc = RTDirCreateFullPath(strDir.c_str(), 0700);
    4817             if (RT_FAILURE(vrc))
    4818                 return setErrorStatic(VBOX_E_IPRT_ERROR,
    4819                                       Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
    4820                                                  strDir.c_str(),
    4821                                                  vrc));
    4822         }
    4823         else
    4824             return setErrorStatic(VBOX_E_IPRT_ERROR,
    4825                                   Utf8StrFmt(tr("Directory '%s' does not exist"),
    4826                                              strDir.c_str()));
    4827     }
    4828 
    4829     return S_OK;
    4830 }
    4831 
    4832 const Utf8Str& VirtualBox::settingsFilePath()
    4833 {
    4834     return m->strSettingsFilePath;
    4835 }
    4836 
    4837 /**
    4838  * Returns the lock handle which protects the media trees (hard disks,
    4839  * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
    4840  * are no longer protected by the VirtualBox lock, but by this more
    4841  * specialized lock. Mind the locking order: always request this lock
    4842  * after the VirtualBox object lock but before the locks of the media
    4843  * objects contained in these lists. See AutoLock.h.
    4844  */
    4845 RWLockHandle& VirtualBox::getMediaTreeLockHandle()
    4846 {
    4847     return m->lockMedia;
    4848 }
    4849 
    4850 /**
    4851  *  Thread function that watches the termination of all client processes
    4852  *  that have opened sessions using IMachine::LockMachine()
    4853  */
    4854 // static
    4855 DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
    4856 {
    4857     LogFlowFuncEnter();
    4858 
    4859     VirtualBox *that = (VirtualBox*)pvUser;
    4860     Assert(that);
    4861 
    4862     typedef std::vector< ComObjPtr<Machine> > MachineVector;
    4863     typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
    4864 
    4865     SessionMachineVector machines;
    4866     MachineVector spawnedMachines;
    4867 
    4868     size_t cnt = 0;
    4869     size_t cntSpawned = 0;
    4870 
    4871     VirtualBoxBase::initializeComForThread();
    4872 
    4873 #if defined(RT_OS_WINDOWS)
    4874 
    4875     /// @todo (dmik) processes reaping!
    4876 
    4877     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
    4878     handles[0] = that->m->updateReq;
    4879 
    4880     do
    4881     {
    4882         AutoCaller autoCaller(that);
     180        AutoCaller autoCaller(that->mVirtualBox);
    4883181        /* VirtualBox has been early uninitialized, terminate */
    4884182        if (!autoCaller.isOk())
     
    4934232                    CloseHandle(handles[i]);
    4935233
     234                // get reference to the machines list in VirtualBox
     235                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList();
     236
    4936237                // lock the machines list for reading
    4937                 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
     238                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4938239
    4939240                /* obtain a new set of opened machines */
     
    4941242                machines.clear();
    4942243
    4943                 for (MachinesOList::iterator it = that->m->allMachines.begin();
    4944                      it != that->m->allMachines.end();
     244                for (MachinesOList::iterator it = allMachines.begin();
     245                     it != allMachines.end();
    4945246                     ++it)
    4946247                {
     
    4950251
    4951252                    ComObjPtr<SessionMachine> sm;
    4952                     HANDLE ipcSem;
    4953                     if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
    4954                     {
    4955                         machines.push_back(sm);
    4956                         handles[1 + cnt] = ipcSem;
    4957                         ++cnt;
     253                    if ((*it)->isSessionOpenOrClosing(sm))
     254                    {
     255                        AutoCaller smCaller(sm);
     256                        if (smCaller.isOk())
     257                        {
     258                            AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
     259                            Machine::ClientToken *ct = sm->getClientToken();
     260                            if (ct)
     261                            {
     262                                HANDLE ipcSem = ct->getToken();
     263                                machines.push_back(sm);
     264                                handles[1 + cnt] = ipcSem;
     265                                ++cnt;
     266                            }
     267                        }
    4958268                    }
    4959269                }
     
    4965275                spawnedMachines.clear();
    4966276
    4967                 for (MachinesOList::iterator it = that->m->allMachines.begin();
    4968                      it != that->m->allMachines.end();
     277                for (MachinesOList::iterator it = allMachines.begin();
     278                     it != allMachines.end();
    4969279                     ++it)
    4970280                {
     
    4973283                                   ("MAXIMUM_WAIT_OBJECTS reached"));
    4974284
    4975                     RTPROCESS pid;
    4976                     if ((*it)->isSessionSpawning(&pid))
    4977                     {
    4978                         HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
    4979                         AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
    4980                                                pid, GetLastError()));
    4981                         if (rc == 0)
     285                    if ((*it)->isSessionSpawning())
     286                    {
     287                        ULONG pid;
     288                        HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid);
     289                        if (SUCCEEDED(hrc))
    4982290                        {
    4983                             spawnedMachines.push_back(*it);
    4984                             handles[1 + cnt + cntSpawned] = ph;
    4985                             ++cntSpawned;
     291                            HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
     292                            AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
     293                                                   pid, GetLastError()));
     294                            if (ph != NULL)
     295                            {
     296                                spawnedMachines.push_back(*it);
     297                                handles[1 + cnt + cntSpawned] = ph;
     298                                ++cntSpawned;
     299                            }
    4986300                        }
    4987301                    }
     
    4998312
    4999313    /* close old process handles */
    5000     for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
     314    for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
    5001315        CloseHandle(handles[i]);
    5002316
     
    5018332    do
    5019333    {
    5020         AutoCaller autoCaller(that);
     334        AutoCaller autoCaller(that->mVirtualBox);
    5021335        /* VirtualBox has been early uninitialized, terminate */
    5022336        if (!autoCaller.isOk())
     
    5028342            autoCaller.release();
    5029343
    5030             int vrc = RTSemEventWait(that->m->updateReq, 500);
     344            int vrc = RTSemEventWait(that->mUpdateReq, 500);
    5031345
    5032346            /* Restore the caller before using VirtualBox. If it fails, this
     
    5081395                         * termination; find which mutex is in the Owner Died
    5082396                         * state */
    5083                         for (size_t i = 0; i < cnt; ++ i)
     397                        for (size_t i = 0; i < cnt; ++i)
    5084398                        {
    5085399                            PID pid; TID tid;
     
    5115429                if (cntSpawned > 0)
    5116430                {
    5117                     for (size_t i = 0; i < cntSpawned; ++ i)
     431                    for (size_t i = 0; i < cntSpawned; ++i)
    5118432                        updateSpawned |= (spawnedMachines[i])->
    5119433                            checkForSpawnFailure();
     
    5123437            if (update || updateSpawned)
    5124438            {
    5125                 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
     439                // get reference to the machines list in VirtualBox
     440                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList();
     441
     442                // lock the machines list for reading
     443                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    5126444
    5127445                if (update)
     
    5135453                    machines.clear();
    5136454
    5137                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5138                          it != that->m->allMachines.end(); ++ it)
     455                    for (MachinesOList::iterator it = allMachines.begin();
     456                         it != allMachines.end(); ++it)
    5139457                    {
    5140458                        /// @todo handle situations with more than 64 objects
     
    5144462
    5145463                        ComObjPtr<SessionMachine> sm;
    5146                         HMTX ipcSem;
    5147                         if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
     464                        if ((*it)->isSessionOpenOrClosing(sm))
    5148465                        {
    5149                             machines.push_back(sm);
    5150                             handles[cnt].hsemCur = (HSEM)ipcSem;
    5151                             handles[cnt].ulUser = cnt;
    5152                             ++ cnt;
     466                            AutoCaller smCaller(sm);
     467                            if (smCaller.isOk())
     468                            {
     469                                AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
     470                                ClientToken *ct = sm->getClientToken();
     471                                if (ct)
     472                                {
     473                                    HMTX ipcSem = ct->getToken();
     474                                    machines.push_back(sm);
     475                                    handles[cnt].hsemCur = (HSEM)ipcSem;
     476                                    handles[cnt].ulUser = cnt;
     477                                    ++cnt;
     478                                }
     479                            }
    5153480                        }
    5154481                    }
     
    5173500                    spawnedMachines.clear();
    5174501
    5175                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5176                          it != that->m->allMachines.end(); ++ it)
     502                    for (MachinesOList::iterator it = allMachines.begin();
     503                         it != allMachines.end(); ++it)
    5177504                    {
    5178505                        if ((*it)->isSessionSpawning())
     
    5204531    do
    5205532    {
    5206         AutoCaller autoCaller(that);
     533        AutoCaller autoCaller(that->mVirtualBox);
    5207534        if (!autoCaller.isOk())
    5208535            break;
     
    5223550                do
    5224551                {
    5225                     uOld = ASMAtomicUoReadU8(&that->m->updateAdaptCtr);
     552                    uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
    5226553                    uNew = uOld ? uOld - 1 : uOld;
    5227                 } while (!ASMAtomicCmpXchgU8(&that->m->updateAdaptCtr, uNew, uOld));
    5228                 Assert(uOld <= RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
    5229                 cMillies = s_updateAdaptTimeouts[uOld];
    5230             }
    5231 
    5232             int rc = RTSemEventWait(that->m->updateReq, cMillies);
     554                } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
     555                Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
     556                cMillies = s_aUpdateTimeoutSteps[uOld];
     557            }
     558
     559            int rc = RTSemEventWait(that->mUpdateReq, cMillies);
    5233560
    5234561            /*
     
    5244571                /* RT_SUCCESS(rc) means an update event is signaled */
    5245572
     573                // get reference to the machines list in VirtualBox
     574                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList();
     575
    5246576                // lock the machines list for reading
    5247                 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
     577                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    5248578
    5249579                if (RT_SUCCESS(rc) || update)
     
    5252582                    machines.clear();
    5253583
    5254                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5255                          it != that->m->allMachines.end();
     584                    for (MachinesOList::iterator it = allMachines.begin();
     585                         it != allMachines.end();
    5256586                         ++it)
    5257587                    {
     
    5270600                    spawnedMachines.clear();
    5271601
    5272                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5273                          it != that->m->allMachines.end();
     602                    for (MachinesOList::iterator it = allMachines.begin();
     603                         it != allMachines.end();
    5274604                         ++it)
    5275605                    {
     
    5286616
    5287617            update = false;
    5288             for (size_t i = 0; i < cnt; ++ i)
     618            for (size_t i = 0; i < cnt; ++i)
    5289619                update |= (machines[i])->checkForDeath();
    5290620
    5291621            updateSpawned = false;
    5292             for (size_t i = 0; i < cntSpawned; ++ i)
     622            for (size_t i = 0; i < cntSpawned; ++i)
    5293623                updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
    5294624
    5295625            /* reap child processes */
    5296626            {
    5297                 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
    5298                 if (that->m->llProcesses.size())
     627                AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS);
     628                if (that->mProcesses.size())
    5299629                {
    5300630                    LogFlowFunc(("UPDATE: child process count = %d\n",
    5301                                  that->m->llProcesses.size()));
    5302                     VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
    5303                     while (it != that->m->llProcesses.end())
     631                                 that->mProcesses.size()));
     632                    VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin();
     633                    while (it != that->mProcesses.end())
    5304634                    {
    5305635                        RTPROCESS pid = *it;
     
    5311641                                         pid, pid, status.iStatus,
    5312642                                         status.enmReason));
    5313                             it = that->m->llProcesses.erase(it);
     643                            it = that->mProcesses.erase(it);
    5314644                        }
    5315645                        else
     
    5320650                            {
    5321651                                /* remove the process if it is not already running */
    5322                                 it = that->m->llProcesses.erase(it);
     652                                it = that->mProcesses.erase(it);
    5323653                            }
    5324654                            else
    5325                                 ++ it;
     655                                ++it;
    5326656                        }
    5327657                    }
     
    5342672
    5343673    VirtualBoxBase::uninitializeComForThread();
     674
    5344675    LogFlowFuncLeave();
    5345676    return 0;
    5346677}
    5347 
    5348 /**
    5349  *  Thread function that handles custom events posted using #postEvent().
    5350  */
    5351 // static
    5352 DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
    5353 {
    5354     LogFlowFuncEnter();
    5355 
    5356     AssertReturn(pvUser, VERR_INVALID_POINTER);
    5357 
    5358     HRESULT hr = com::Initialize();
    5359     if (FAILED(hr))
    5360         return VERR_COM_UNEXPECTED;
    5361 
    5362     int rc = VINF_SUCCESS;
    5363 
    5364     try
    5365     {
    5366         /* Create an event queue for the current thread. */
    5367         EventQueue *pEventQueue = new EventQueue();
    5368         AssertPtr(pEventQueue);
    5369 
    5370         /* Return the queue to the one who created this thread. */
    5371         *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
    5372 
    5373         /* signal that we're ready. */
    5374         RTThreadUserSignal(thread);
    5375 
    5376         /*
    5377          * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
    5378          * we must not stop processing events and delete the pEventQueue object. This must
    5379          * be done ONLY when we stop this loop via interruptEventQueueProcessing().
    5380          * See @bugref{5724}.
    5381          */
    5382         for (;;)
    5383         {
    5384             rc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);
    5385             if (rc == VERR_INTERRUPTED)
    5386             {
    5387                 LogFlow(("Event queue processing ended with rc=%Rrc\n", rc));
    5388                 rc = VINF_SUCCESS; /* Set success when exiting. */
    5389                 break;
    5390             }
    5391         }
    5392 
    5393         delete pEventQueue;
    5394     }
    5395     catch (std::bad_alloc &ba)
    5396     {
    5397         rc = VERR_NO_MEMORY;
    5398         NOREF(ba);
    5399     }
    5400 
    5401     com::Shutdown();
    5402 
    5403     LogFlowFuncLeaveRC(rc);
    5404     return rc;
    5405 }
    5406 
    5407 
    5408 ////////////////////////////////////////////////////////////////////////////////
    5409 
    5410 /**
    5411  *  Takes the current list of registered callbacks of the managed VirtualBox
    5412  *  instance, and calls #handleCallback() for every callback item from the
    5413  *  list, passing the item as an argument.
    5414  *
    5415  *  @note Locks the managed VirtualBox object for reading but leaves the lock
    5416  *        before iterating over callbacks and calling their methods.
    5417  */
    5418 void *VirtualBox::CallbackEvent::handler()
    5419 {
    5420     if (!mVirtualBox)
    5421         return NULL;
    5422 
    5423     AutoCaller autoCaller(mVirtualBox);
    5424     if (!autoCaller.isOk())
    5425     {
    5426         LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
    5427                         autoCaller.state()));
    5428         /* We don't need mVirtualBox any more, so release it */
    5429         mVirtualBox = NULL;
    5430         return NULL;
    5431     }
    5432 
    5433     {
    5434         VBoxEventDesc evDesc;
    5435         prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
    5436 
    5437         evDesc.fire(/* don't wait for delivery */0);
    5438     }
    5439 
    5440     mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
    5441     return NULL;
    5442 }
    5443 
    5444 //STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
    5445 //{
    5446 //    return E_NOTIMPL;
    5447 //}
    5448 
    5449 STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
    5450 {
    5451     CheckComArgStrNotEmptyOrNull(aName);
    5452     CheckComArgNotNull(aServer);
    5453 
    5454     AutoCaller autoCaller(this);
    5455     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5456 
    5457     ComObjPtr<DHCPServer> dhcpServer;
    5458     dhcpServer.createObject();
    5459     HRESULT rc = dhcpServer->init(this, aName);
    5460     if (FAILED(rc)) return rc;
    5461 
    5462     rc = registerDHCPServer(dhcpServer, true);
    5463     if (FAILED(rc)) return rc;
    5464 
    5465     dhcpServer.queryInterfaceTo(aServer);
    5466 
    5467     return rc;
    5468 }
    5469 
    5470 STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
    5471 {
    5472     CheckComArgStrNotEmptyOrNull(aName);
    5473     CheckComArgNotNull(aServer);
    5474 
    5475     AutoCaller autoCaller(this);
    5476     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5477 
    5478     HRESULT rc;
    5479     Bstr bstr;
    5480     ComPtr<DHCPServer> found;
    5481 
    5482     AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    5483 
    5484     for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
    5485          it != m->allDHCPServers.end();
    5486          ++it)
    5487     {
    5488         rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
    5489         if (FAILED(rc)) return rc;
    5490 
    5491         if (bstr == aName)
    5492         {
    5493             found = *it;
    5494             break;
    5495         }
    5496     }
    5497 
    5498     if (!found)
    5499         return E_INVALIDARG;
    5500 
    5501     return found.queryInterfaceTo(aServer);
    5502 }
    5503 
    5504 STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
    5505 {
    5506     CheckComArgNotNull(aServer);
    5507 
    5508     AutoCaller autoCaller(this);
    5509     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5510 
    5511     HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
    5512 
    5513     return rc;
    5514 }
    5515 
    5516 /**
    5517  * Remembers the given DHCP server in the settings.
    5518  *
    5519  * @param aDHCPServer   DHCP server object to remember.
    5520  * @param aSaveSettings @c true to save settings to disk (default).
    5521  *
    5522  * When @a aSaveSettings is @c true, this operation may fail because of the
    5523  * failed #saveSettings() method it calls. In this case, the dhcp server object
    5524  * will not be remembered. It is therefore the responsibility of the caller to
    5525  * call this method as the last step of some action that requires registration
    5526  * in order to make sure that only fully functional dhcp server objects get
    5527  * registered.
    5528  *
    5529  * @note Locks this object for writing and @a aDHCPServer for reading.
    5530  */
    5531 HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
    5532                                        bool aSaveSettings /*= true*/)
    5533 {
    5534     AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
    5535 
    5536     AutoCaller autoCaller(this);
    5537     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    5538 
    5539     AutoCaller dhcpServerCaller(aDHCPServer);
    5540     AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
    5541 
    5542     Bstr name;
    5543     HRESULT rc;
    5544     rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
    5545     if (FAILED(rc)) return rc;
    5546 
    5547     ComPtr<IDHCPServer> existing;
    5548     rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
    5549     if (SUCCEEDED(rc))
    5550         return E_INVALIDARG;
    5551 
    5552     rc = S_OK;
    5553 
    5554     m->allDHCPServers.addChild(aDHCPServer);
    5555 
    5556     if (aSaveSettings)
    5557     {
    5558         AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
    5559         rc = saveSettings();
    5560         vboxLock.release();
    5561 
    5562         if (FAILED(rc))
    5563             unregisterDHCPServer(aDHCPServer, false /* aSaveSettings */);
    5564     }
    5565 
    5566     return rc;
    5567 }
    5568 
    5569 /**
    5570  * Removes the given DHCP server from the settings.
    5571  *
    5572  * @param aDHCPServer   DHCP server object to remove.
    5573  * @param aSaveSettings @c true to save settings to disk (default).
    5574  *
    5575  * When @a aSaveSettings is @c true, this operation may fail because of the
    5576  * failed #saveSettings() method it calls. In this case, the DHCP server
    5577  * will NOT be removed from the settingsi when this method returns.
    5578  *
    5579  * @note Locks this object for writing.
    5580  */
    5581 HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
    5582                                          bool aSaveSettings /*= true*/)
    5583 {
    5584     AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
    5585 
    5586     AutoCaller autoCaller(this);
    5587     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    5588 
    5589     AutoCaller dhcpServerCaller(aDHCPServer);
    5590     AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
    5591 
    5592     m->allDHCPServers.removeChild(aDHCPServer);
    5593 
    5594     HRESULT rc = S_OK;
    5595 
    5596     if (aSaveSettings)
    5597     {
    5598         AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
    5599         rc = saveSettings();
    5600         vboxLock.release();
    5601 
    5602         if (FAILED(rc))
    5603             registerDHCPServer(aDHCPServer, false /* aSaveSettings */);
    5604     }
    5605 
    5606     return rc;
    5607 }
    5608 
    5609 
    5610 /**
    5611  * NAT Network
    5612  */
    5613 
    5614 STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork)
    5615 {
    5616 #ifdef VBOX_WITH_NAT_SERVICE
    5617     CheckComArgStrNotEmptyOrNull(aName);
    5618     CheckComArgNotNull(aNatNetwork);
    5619 
    5620     AutoCaller autoCaller(this);
    5621     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5622 
    5623     ComObjPtr<NATNetwork> natNetwork;
    5624     natNetwork.createObject();
    5625     HRESULT rc = natNetwork->init(this, aName);
    5626     if (FAILED(rc)) return rc;
    5627 
    5628     rc = registerNATNetwork(natNetwork, true);
    5629     if (FAILED(rc)) return rc;
    5630 
    5631     natNetwork.queryInterfaceTo(aNatNetwork);
    5632 
    5633     fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE);
    5634     return rc;
    5635 #else
    5636     NOREF(aName);
    5637     NOREF(aNatNetwork);
    5638     return E_NOTIMPL;
    5639 #endif
    5640 }
    5641 
    5642 STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork)
    5643 {
    5644 #ifdef VBOX_WITH_NAT_SERVICE
    5645     CheckComArgStrNotEmptyOrNull(aName);
    5646     CheckComArgNotNull(aNetwork);
    5647 
    5648     AutoCaller autoCaller(this);
    5649     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5650 
    5651     HRESULT rc;
    5652     Bstr bstr;
    5653     ComPtr<NATNetwork> found;
    5654 
    5655     AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    5656 
    5657     for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
    5658          it != m->allNATNetworks.end();
    5659          ++it)
    5660     {
    5661         rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
    5662         if (FAILED(rc)) return rc;
    5663 
    5664         if (bstr == aName)
    5665         {
    5666             found = *it;
    5667             break;
    5668         }
    5669     }
    5670 
    5671     if (!found)
    5672         return E_INVALIDARG;
    5673 
    5674     return found.queryInterfaceTo(aNetwork);
    5675 #else
    5676     NOREF(aName);
    5677     NOREF(aNetwork);
    5678     return E_NOTIMPL;
    5679 #endif
    5680 }
    5681 
    5682 STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork)
    5683 {
    5684 #ifdef VBOX_WITH_NAT_SERVICE
    5685     CheckComArgNotNull(aNetwork);
    5686 
    5687     AutoCaller autoCaller(this);
    5688     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5689     Bstr name;
    5690     HRESULT rc;
    5691     NATNetwork *network = static_cast<NATNetwork *>(aNetwork);
    5692     rc = network->COMGETTER(NetworkName)(name.asOutParam());
    5693     rc = unregisterNATNetwork(network, true);
    5694     fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
    5695     return rc;
    5696 #else
    5697     NOREF(aNetwork);
    5698     return E_NOTIMPL;
    5699 #endif
    5700 
    5701 }
    5702 /**
    5703  * Remembers the given NAT network in the settings.
    5704  *
    5705  * @param aNATNetwork    NAT Network object to remember.
    5706  * @param aSaveSettings @c true to save settings to disk (default).
    5707  *
    5708  *
    5709  * @note Locks this object for writing and @a aNATNetwork for reading.
    5710  */
    5711 HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork,
    5712                                        bool aSaveSettings /*= true*/)
    5713 {
    5714 #ifdef VBOX_WITH_NAT_SERVICE
    5715     AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
    5716 
    5717     AutoCaller autoCaller(this);
    5718     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    5719 
    5720     AutoCaller natNetworkCaller(aNATNetwork);
    5721     AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
    5722 
    5723     Bstr name;
    5724     HRESULT rc;
    5725     rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
    5726     if (FAILED(rc)) return rc;
    5727 
    5728     ComPtr<INATNetwork> existing;
    5729     rc = FindNATNetworkByName(name.raw(), existing.asOutParam());
    5730     if (SUCCEEDED(rc))
    5731         return E_INVALIDARG;
    5732 
    5733     rc = S_OK;
    5734 
    5735     m->allNATNetworks.addChild(aNATNetwork);
    5736 
    5737     if (aSaveSettings)
    5738     {
    5739         AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
    5740         rc = saveSettings();
    5741         vboxLock.release();
    5742 
    5743         if (FAILED(rc))
    5744             unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
    5745     }
    5746 
    5747     return rc;
    5748 #else
    5749     NOREF(aNATNetwork);
    5750     NOREF(aSaveSettings);
    5751     /* No panic please (silently ignore) */
    5752     return S_OK;
    5753 #endif
    5754 }
    5755 
    5756 /**
    5757  * Removes the given NAT network from the settings.
    5758  *
    5759  * @param aNATNetwork   NAT network object to remove.
    5760  * @param aSaveSettings @c true to save settings to disk (default).
    5761  *
    5762  * When @a aSaveSettings is @c true, this operation may fail because of the
    5763  * failed #saveSettings() method it calls. In this case, the DHCP server
    5764  * will NOT be removed from the settingsi when this method returns.
    5765  *
    5766  * @note Locks this object for writing.
    5767  */
    5768 HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork,
    5769                                          bool aSaveSettings /*= true*/)
    5770 {
    5771 #ifdef VBOX_WITH_NAT_SERVICE
    5772     AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
    5773 
    5774     AutoCaller autoCaller(this);
    5775     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    5776 
    5777     AutoCaller natNetworkCaller(aNATNetwork);
    5778     AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
    5779 
    5780     m->allNATNetworks.removeChild(aNATNetwork);
    5781 
    5782     HRESULT rc = S_OK;
    5783 
    5784     if (aSaveSettings)
    5785     {
    5786         AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
    5787         rc = saveSettings();
    5788         vboxLock.release();
    5789 
    5790         if (FAILED(rc))
    5791             registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
    5792     }
    5793 
    5794     return rc;
    5795 #else
    5796     NOREF(aNATNetwork);
    5797     NOREF(aSaveSettings);
    5798     return E_NOTIMPL;
    5799 #endif
    5800 }
    5801678/* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r47401 r47561  
    2424#endif
    2525
    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 
    3426#include "Logging.h"
    3527#include "VirtualBoxImpl.h"
    3628#include "MachineImpl.h"
     29#include "ClientToken.h"
    3730#include "ProgressImpl.h"
    3831#include "ProgressProxyImpl.h"
     
    37123705            mData->mSession.mState = SessionState_Spawning;
    37133706
     3707            /* Get the client token ID to be passed to the client process */
     3708            Utf8Str strTokenId;
     3709            sessionMachine->getTokenId(strTokenId);
     3710            Assert(!strTokenId.isEmpty());
     3711
    37143712            /*
    37153713             *  Release the lock before calling the client process -- it will call
     
    37253723
    37263724            LogFlowThisFunc(("Calling AssignMachine()...\n"));
    3727             rc = pSessionControl->AssignMachine(sessionMachine, lockType);
     3725            rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
    37283726            LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
    37293727
     
    37923790                /* Close the remote session, remove the remote control from the list
    37933791                 * and reset session state to Closed (@note keep the code in sync
    3794                  * with the relevant part in openSession()). */
     3792                 * with the relevant part in checkForSpawnFailure()). */
    37953793
    37963794                Assert(mData->mSession.mRemoteControls.size() == 1);
     
    80968094     *  Releasing the lock here is dangerous because we didn't prepare the
    80978095     *  launch data yet, but the client we've just started may happen to be
    8098      *  too fast and call openSession() that will fail (because of PID, etc.),
     8096     *  too fast and call LockMachine() that will fail (because of PID, etc.),
    80998097     *  so that the Machine will never get out of the Spawning session state.
    81008098     */
     
    81028100    /* inform the session that it will be a remote one */
    81038101    LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
    8104     HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
     8102    HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
    81058103    LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
    81068104
     
    81278125
    81288126/**
    8129  * Returns @c true if the given machine has an open direct session and returns
    8130  * the session machine instance and additional session data (on some platforms)
    8131  * if so.
     8127 * Returns @c true if the given session machine instance has an open direct
     8128 * session (and optionally also for direct sessions which are closing) and
     8129 * returns the session control machine instance if so.
    81328130 *
    81338131 * Note that when the method returns @c false, the arguments remain unchanged.
    81348132 *
    8135  * @param aMachine  Session machine object.
    8136  * @param aControl  Direct session control object (optional).
    8137  * @param aIPCSem   Mutex IPC semaphore handle for this machine (optional).
     8133 * @param aMachine      Session machine object.
     8134 * @param aControl      Direct session control object (optional).
     8135 * @param aAllowClosing If true then additionally a session which is currently
     8136 *                      being closed will also be allowed.
    81388137 *
    81398138 * @note locks this object for reading.
    81408139 */
    8141 #if defined(RT_OS_WINDOWS)
    8142 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    8143                             ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    8144                             HANDLE *aIPCSem /*= NULL*/,
    8145                             bool aAllowClosing /*= false*/)
    8146 #elif defined(RT_OS_OS2)
    8147 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    8148                             ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    8149                             HMTX *aIPCSem /*= NULL*/,
    8150                             bool aAllowClosing /*= false*/)
    8151 #else
    81528140bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
    81538141                            ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
    81548142                            bool aAllowClosing /*= false*/)
    8155 #endif
    81568143{
    81578144    AutoLimitedCaller autoCaller(this);
     
    81758162            *aControl = mData->mSession.mDirectControl;
    81768163
    8177 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8178         /* Additional session data */
    8179         if (aIPCSem != NULL)
    8180             *aIPCSem = aMachine->mIPCSem;
    8181 #endif
    81828164        return true;
    81838165    }
     
    81878169
    81888170/**
    8189  * Returns @c true if the given machine has an spawning direct session and
    8190  * returns and additional session data (on some platforms) if so.
    8191  *
    8192  * Note that when the method returns @c false, the arguments remain unchanged.
    8193  *
    8194  * @param aPID  PID of the spawned direct session process.
     8171 * Returns @c true if the given machine has an spawning direct session.
    81958172 *
    81968173 * @note locks this object for reading.
    81978174 */
    8198 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8199 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
    8200 #else
    82018175bool Machine::isSessionSpawning()
    8202 #endif
    82038176{
    82048177    AutoLimitedCaller autoCaller(this);
     
    82128185
    82138186    if (mData->mSession.mState == SessionState_Spawning)
    8214     {
    8215 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8216         /* Additional session data */
    8217         if (aPID != NULL)
    8218         {
    8219             AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
    8220             *aPID = mData->mSession.mPID;
    8221         }
    8222 #endif
    82238187        return true;
    8224     }
    82258188
    82268189    return false;
     
    82518214    }
    82528215
    8253     /* VirtualBox::addProcessToReap() needs a write lock */
    8254     AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
     8216    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    82558217
    82568218    if (mData->mSession.mState != SessionState_Spawning)
     
    82638225    HRESULT rc = S_OK;
    82648226
    8265 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    8266 
    8267     /* the process was already unexpectedly terminated, we just need to set an
    8268      * error and finalize session spawning */
    8269     rc = setError(E_FAIL,
    8270                   tr("The virtual machine '%s' has terminated unexpectedly during startup"),
    8271                   getName().c_str());
    8272 #else
    8273 
    82748227    /* PID not yet initialized, skip check. */
    82758228    if (mData->mSession.mPID == NIL_RTPROCESS)
     
    82778230
    82788231    RTPROCSTATUS status;
    8279     int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
    8280                            &status);
     8232    int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
    82818233
    82828234    if (vrc != VERR_PROCESS_RUNNING)
     
    83008252    }
    83018253
    8302 #endif
    8303 
    83048254    if (FAILED(rc))
    83058255    {
    83068256        /* Close the remote session, remove the remote control from the list
    83078257         * and reset session state to Closed (@note keep the code in sync with
    8308          * the relevant part in checkForSpawnFailure()). */
     8258         * the relevant part in LockMachine()). */
    83098259
    83108260        Assert(mData->mSession.mRemoteControls.size() == 1);
     
    83258275        }
    83268276
    8327         mParent->addProcessToReap(mData->mSession.mPID);
    83288277        mData->mSession.mPID = NIL_RTPROCESS;
    83298278
     
    1268012629    LogFlowThisFunc(("\n"));
    1268112630
    12682 #if defined(RT_OS_WINDOWS)
    12683     mIPCSem = NULL;
    12684 #elif defined(RT_OS_OS2)
    12685     mIPCSem = NULLHANDLE;
    12686 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    12687     mIPCSem = -1;
    12688 #else
    12689 # error "Port me!"
    12690 #endif
     12631    mClientToken = NULL;
    1269112632
    1269212633    return BaseFinalConstruct();
     
    1269712638    LogFlowThisFunc(("\n"));
    1269812639
     12640    Assert(!mClientToken);
     12641    /* paranoia, should not hang around any more */
     12642    if (mClientToken)
     12643    {
     12644        delete mClientToken;
     12645        mClientToken = NULL;
     12646    }
     12647
    1269912648    uninit(Uninit::Unexpected);
    1270012649
     
    1270312652
    1270412653/**
    12705  *  @note Must be called only by Machine::openSession() from its own write lock.
     12654 *  @note Must be called only by Machine::LockMachine() from its own write lock.
    1270612655 */
    1270712656HRESULT SessionMachine::init(Machine *aMachine)
     
    1271812667    AssertReturn(autoInitSpan.isOk(), E_FAIL);
    1271912668
    12720     /* create the interprocess semaphore */
    12721 #if defined(RT_OS_WINDOWS)
    12722     mIPCSemName = aMachine->mData->m_strConfigFileFull;
    12723     for (size_t i = 0; i < mIPCSemName.length(); i++)
    12724         if (mIPCSemName.raw()[i] == '\\')
    12725             mIPCSemName.raw()[i] = '/';
    12726     mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
    12727     ComAssertMsgRet(mIPCSem,
    12728                     ("Cannot create IPC mutex '%ls', err=%d",
    12729                      mIPCSemName.raw(), ::GetLastError()),
    12730                     E_FAIL);
    12731 #elif defined(RT_OS_OS2)
    12732     Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
    12733                                 aMachine->mData->mUuid.raw());
    12734     mIPCSemName = ipcSem;
    12735     APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
    12736     ComAssertMsgRet(arc == NO_ERROR,
    12737                     ("Cannot create IPC mutex '%s', arc=%ld",
    12738                      ipcSem.c_str(), arc),
    12739                     E_FAIL);
    12740 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    12741 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    12742 #  if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
    12743     /** @todo Check that this still works correctly. */
    12744     AssertCompileSize(key_t, 8);
    12745 #  else
    12746     AssertCompileSize(key_t, 4);
    12747 #  endif
    12748     key_t key;
    12749     mIPCSem = -1;
    12750     mIPCKey = "0";
    12751     for (uint32_t i = 0; i < 1 << 24; i++)
    12752     {
    12753         key = ((uint32_t)'V' << 24) | i;
    12754         int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
    12755         if (sem >= 0 || (errno != EEXIST && errno != EACCES))
    12756         {
    12757             mIPCSem = sem;
    12758             if (sem >= 0)
    12759                 mIPCKey = BstrFmt("%u", key);
    12760             break;
    12761         }
    12762     }
    12763 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    12764     Utf8Str semName = aMachine->mData->m_strConfigFileFull;
    12765     char *pszSemName = NULL;
    12766     RTStrUtf8ToCurrentCP(&pszSemName, semName);
    12767     key_t key = ::ftok(pszSemName, 'V');
    12768     RTStrFree(pszSemName);
    12769 
    12770     mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
    12771 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    12772 
    12773     int errnoSave = errno;
    12774     if (mIPCSem < 0 && errnoSave == ENOSYS)
    12775     {
    12776         setError(E_FAIL,
    12777                  tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
    12778                     "support for SysV IPC. Check the host kernel configuration for "
    12779                     "CONFIG_SYSVIPC=y"));
    12780         return E_FAIL;
    12781     }
    12782     /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
    12783      * the IPC semaphores */
    12784     if (mIPCSem < 0 && errnoSave == ENOSPC)
    12785     {
    12786 #ifdef RT_OS_LINUX
    12787         setError(E_FAIL,
    12788                  tr("Cannot create IPC semaphore because the system limit for the "
    12789                     "maximum number of semaphore sets (SEMMNI), or the system wide "
    12790                     "maximum number of semaphores (SEMMNS) would be exceeded. The "
    12791                     "current set of SysV IPC semaphores can be determined from "
    12792                     "the file /proc/sysvipc/sem"));
    12793 #else
    12794         setError(E_FAIL,
    12795                  tr("Cannot create IPC semaphore because the system-imposed limit "
    12796                     "on the maximum number of allowed  semaphores or semaphore "
    12797                     "identifiers system-wide would be exceeded"));
    12798 #endif
    12799         return E_FAIL;
    12800     }
    12801     ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
    12802                     E_FAIL);
    12803     /* set the initial value to 1 */
    12804     int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
    12805     ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
    12806                     E_FAIL);
    12807 #else
    12808 # error "Port me!"
    12809 #endif
     12669    HRESULT rc = S_OK;
     12670
     12671    /* create the machine client token */
     12672    try
     12673    {
     12674        mClientToken = new ClientToken(aMachine);
     12675        if (!mClientToken->isReady())
     12676        {
     12677            delete mClientToken;
     12678            mClientToken = NULL;
     12679            rc = E_FAIL;
     12680        }
     12681    }
     12682    catch (std::bad_alloc &)
     12683    {
     12684        rc = E_OUTOFMEMORY;
     12685    }
     12686    if (FAILED(rc))
     12687        return rc;
    1281012688
    1281112689    /* memorize the peer Machine */
     
    1288812766
    1288912767    LogFlowThisFuncLeave();
    12890     return S_OK;
     12768    return rc;
    1289112769}
    1289212770
     
    1292912807         */
    1293012808        LogFlowThisFunc(("Initialization failed.\n"));
    12931 #if defined(RT_OS_WINDOWS)
    12932         if (mIPCSem)
    12933             ::CloseHandle(mIPCSem);
    12934         mIPCSem = NULL;
    12935 #elif defined(RT_OS_OS2)
    12936         if (mIPCSem != NULLHANDLE)
    12937             ::DosCloseMutexSem(mIPCSem);
    12938         mIPCSem = NULLHANDLE;
    12939 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    12940         if (mIPCSem >= 0)
    12941             ::semctl(mIPCSem, 0, IPC_RMID);
    12942         mIPCSem = -1;
    12943 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    12944         mIPCKey = "0";
    12945 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
    12946 #else
    12947 # error "Port me!"
    12948 #endif
     12809        /* destroy the machine client token */
     12810        if (mClientToken)
     12811        {
     12812            delete mClientToken;
     12813            mClientToken = NULL;
     12814        }
    1294912815        uninitDataAndChildObjects();
    1295012816        mData.free();
     
    1313413000    mData->mSession.mType.setNull();
    1313513001
    13136     /* close the interprocess semaphore before leaving the exclusive lock */
    13137 #if defined(RT_OS_WINDOWS)
    13138     if (mIPCSem)
    13139         ::CloseHandle(mIPCSem);
    13140     mIPCSem = NULL;
    13141 #elif defined(RT_OS_OS2)
    13142     if (mIPCSem != NULLHANDLE)
    13143         ::DosCloseMutexSem(mIPCSem);
    13144     mIPCSem = NULLHANDLE;
    13145 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    13146     if (mIPCSem >= 0)
    13147         ::semctl(mIPCSem, 0, IPC_RMID);
    13148     mIPCSem = -1;
    13149 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    13150     mIPCKey = "0";
    13151 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
    13152 #else
    13153 # error "Port me!"
    13154 #endif
     13002    /* destroy the machine client token before leaving the exclusive lock */
     13003    if (mClientToken)
     13004    {
     13005        delete mClientToken;
     13006        mClientToken = NULL;
     13007    }
    1315513008
    1315613009    /* fire an event */
     
    1316513018    multilock.release();
    1316613019
     13020    RTThreadSleep(500);
     13021    mParent->AddRef();
     13022    LONG c = mParent->Release();
     13023    LogFlowThisFunc(("vbox ref=%d\n", c));
    1316713024    unconst(mParent) = NULL;
    1316813025    unconst(mPeer) = NULL;
     
    1325213109
    1325313110/**
    13254  *  @note Locks this object for reading.
    13255  */
    13256 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
    13257 {
    13258     AutoCaller autoCaller(this);
    13259     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    13260 
    13261     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    13262 
    13263 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    13264     mIPCSemName.cloneTo(aId);
    13265     return S_OK;
    13266 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    13267 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
    13268     mIPCKey.cloneTo(aId);
    13269 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    13270     mData->m_strConfigFileFull.cloneTo(aId);
    13271 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
    13272     return S_OK;
    13273 #else
    13274 # error "Port me!"
    13275 #endif
    13276 }
    13277 
    13278 /**
    1327913111 *  @note Locks this object for writing.
    1328013112 */
     
    1407813910                 Uninit::Abnormal;
    1407913911
    14080 #if defined(RT_OS_WINDOWS)
    14081 
    14082         AssertMsg(mIPCSem, ("semaphore must be created"));
    14083 
    14084         /* release the IPC mutex */
    14085         ::ReleaseMutex(mIPCSem);
    14086 
    14087         terminated = true;
    14088 
    14089 #elif defined(RT_OS_OS2)
    14090 
    14091         AssertMsg(mIPCSem, ("semaphore must be created"));
    14092 
    14093         /* release the IPC mutex */
    14094         ::DosReleaseMutexSem(mIPCSem);
    14095 
    14096         terminated = true;
    14097 
    14098 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    14099 
    14100         AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
    14101 
    14102         int val = ::semctl(mIPCSem, 0, GETVAL);
    14103         if (val > 0)
    14104         {
    14105             /* the semaphore is signaled, meaning the session is terminated */
    14106             terminated = true;
    14107         }
    14108 
    14109 #else
    14110 # error "Port me!"
    14111 #endif
    14112 
     13912        if (mClientToken)
     13913            terminated = mClientToken->release();
    1411313914    } /* AutoCaller block */
    1411413915
     
    1411813919    return terminated;
    1411913920}
     13921
     13922void SessionMachine::getTokenId(Utf8Str &strTokenId)
     13923{
     13924    LogFlowThisFunc(("\n"));
     13925
     13926    strTokenId.setNull();
     13927
     13928    AutoCaller autoCaller(this);
     13929    AssertComRCReturnVoid(autoCaller.rc());
     13930
     13931    Assert(mClientToken);
     13932    if (mClientToken)
     13933        mClientToken->getId(strTokenId);
     13934}
     13935
     13936Machine::ClientToken *SessionMachine::getClientToken()
     13937{
     13938    LogFlowThisFunc(("\n"));
     13939
     13940    AutoCaller autoCaller(this);
     13941    AssertComRCReturn(autoCaller.rc(), NULL);
     13942
     13943    return mClientToken;
     13944}
     13945
    1412013946
    1412113947/**
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r47018 r47561  
    3636#include <VBox/com/array.h>
    3737#include "VBox/com/EventQueue.h"
     38#include "VBox/com/MultiResult.h"
    3839
    3940#include <VBox/err.h>
     
    7374#endif
    7475#include "AutostartDb.h"
     76#include "ClientWatcher.h"
    7577
    7678#include "AutoCaller.h"
    7779#include "Logging.h"
    78 #include "objectslist.h"
    7980
    8081#ifdef RT_OS_WINDOWS
     
    111112// static
    112113Bstr VirtualBox::sAPIVersion;
    113 
    114 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    115 /** Table for adaptive timeouts in the client watcher. The counter starts at
    116  * the maximum value and decreases to 0. */
    117 static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 };
    118 #endif
    119114
    120115////////////////////////////////////////////////////////////////////////////////
     
    165160////////////////////////////////////////////////////////////////////////////////
    166161
    167 #if defined(RT_OS_WINDOWS)
    168     #define UPDATEREQARG NULL
    169     #define UPDATEREQTYPE HANDLE
    170 #elif defined(RT_OS_OS2)
    171     #define UPDATEREQARG NIL_RTSEMEVENT
    172     #define UPDATEREQTYPE RTSEMEVENT
    173 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    174     #define UPDATEREQARG
    175     #define UPDATEREQTYPE RTSEMEVENT
    176 #else
    177 # error "Port me!"
    178 #endif
    179 
    180 typedef ObjectsList<Machine> MachinesOList;
    181162typedef ObjectsList<Medium> MediaOList;
    182163typedef ObjectsList<GuestOSType> GuestOSTypesOList;
     
    214195          allNATNetworks(lockNATNetworks),
    215196          mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
    216           updateReq(UPDATEREQARG),
    217           threadClientWatcher(NIL_RTTHREAD),
     197          pClientWatcher(NULL),
    218198          threadAsyncEvent(NIL_RTTHREAD),
    219199          pAsyncEventQ(NULL),
     
    290270    RWLockHandle                        lockDHCPServers;
    291271    DHCPServersOList                    allDHCPServers;
    292    
    293     RWLockHandle                         lockNATNetworks;
    294     NATNetworksOList                     allNATNetworks;
     272
     273    RWLockHandle                        lockNATNetworks;
     274    NATNetworksOList                    allNATNetworks;
    295275
    296276    RWLockHandle                        mtxProgressOperations;
    297277    ProgressMap                         mapProgressOperations;
    298278
    299     // the following are data for the client watcher thread
    300     const UPDATEREQTYPE                 updateReq;
    301 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
    302     uint8_t                             updateAdaptCtr;
    303 #endif
    304     const RTTHREAD                      threadClientWatcher;
    305     typedef std::list<RTPROCESS> ProcessList;
    306     ProcessList                         llProcesses;
     279    ClientWatcher * const               pClientWatcher;
    307280
    308281    // the following are data for the async event thread
     
    551524    if (SUCCEEDED(rc))
    552525    {
    553         /* start the client watcher thread */
    554 #if defined(RT_OS_WINDOWS)
    555         unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    556 #elif defined(RT_OS_OS2)
    557         RTSemEventCreate(&unconst(m->updateReq));
    558 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    559         RTSemEventCreate(&unconst(m->updateReq));
    560         ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0);
    561 #else
    562 # error "Port me!"
    563 #endif
    564         int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
    565                                  ClientWatcher,
    566                                  (void *)this,
    567                                  0,
    568                                  RTTHREADTYPE_MAIN_WORKER,
    569                                  RTTHREADFLAGS_WAITABLE,
    570                                  "Watcher");
    571         ComAssertRC(vrc);
    572         if (RT_FAILURE(vrc))
    573             rc = E_FAIL;
     526        /* set up client monitoring */
     527        try
     528        {
     529            unconst(m->pClientWatcher) = new ClientWatcher(this);
     530            if (!m->pClientWatcher->isReady())
     531            {
     532                delete m->pClientWatcher;
     533                unconst(m->pClientWatcher) = NULL;
     534                rc = E_FAIL;
     535            }
     536        }
     537        catch (std::bad_alloc &)
     538        {
     539            rc = E_OUTOFMEMORY;
     540        }
    574541    }
    575542
     
    836803
    837804    LogFlowThisFunc(("Terminating the client watcher...\n"));
    838     if (m->threadClientWatcher != NIL_RTTHREAD)
    839     {
    840         /* signal the client watcher thread */
    841         updateClientWatcher();
    842         /* wait for the termination */
    843         RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
    844         unconst(m->threadClientWatcher) = NIL_RTTHREAD;
    845     }
    846     m->llProcesses.clear();
    847 #if defined(RT_OS_WINDOWS)
    848     if (m->updateReq != NULL)
    849     {
    850         ::CloseHandle(m->updateReq);
    851         unconst(m->updateReq) = NULL;
    852     }
    853 #elif defined(RT_OS_OS2)
    854     if (m->updateReq != NIL_RTSEMEVENT)
    855     {
    856         RTSemEventDestroy(m->updateReq);
    857         unconst(m->updateReq) = NIL_RTSEMEVENT;
    858     }
    859 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    860     if (m->updateReq != NIL_RTSEMEVENT)
    861     {
    862         RTSemEventDestroy(m->updateReq);
    863         unconst(m->updateReq) = NIL_RTSEMEVENT;
    864     }
    865 #else
    866 # error "Port me!"
    867 #endif
     805    if (m->pClientWatcher)
     806    {
     807        delete m->pClientWatcher;
     808        unconst(m->pClientWatcher) = NULL;
     809    }
    868810
    869811    delete m->pAutostartDb;
     
    888830    AutoCaller autoCaller(this);
    889831    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     832
     833    MultiResult mrc;
     834    setError(E_INVALIDARG, "test error first");
     835    setError(E_FAIL, "test error");
     836    return 0x8000ffff;
    890837
    891838    sVersion.cloneTo(aVersion);
     
    11531100    return S_OK;
    11541101#else /* !VBOX_WITH_RESOURCE_USAGE_API */
     1102    NOREF(aPerformanceCollector);
    11551103    ReturnComNotImplemented();
    11561104#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
     
    28372785
    28382786/**
    2839  *  Sends a signal to the client watcher thread to rescan the set of machines
     2787 *  Sends a signal to the client watcher to rescan the set of machines
    28402788 *  that have open sessions.
    28412789 *
     
    28472795    AssertComRCReturnVoid(autoCaller.rc());
    28482796
    2849     AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
    2850 
    2851     /* sent an update request */
    2852 #if defined(RT_OS_WINDOWS)
    2853     ::SetEvent(m->updateReq);
    2854 #elif defined(RT_OS_OS2)
    2855     RTSemEventSignal(m->updateReq);
    2856 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    2857     ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
    2858     RTSemEventSignal(m->updateReq);
    2859 #else
    2860 # error "Port me!"
    2861 #endif
     2797    AssertPtrReturnVoid(m->pClientWatcher);
     2798    m->pClientWatcher->update();
    28622799}
    28632800
     
    28652802 *  Adds the given child process ID to the list of processes to be reaped.
    28662803 *  This call should be followed by #updateClientWatcher() to take the effect.
     2804 *
     2805 *  @note Doesn't lock anything.
    28672806 */
    28682807void VirtualBox::addProcessToReap(RTPROCESS pid)
     
    28712810    AssertComRCReturnVoid(autoCaller.rc());
    28722811
    2873     /// @todo (dmik) Win32?
    2874 #ifndef RT_OS_WINDOWS
    2875     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2876     m->llProcesses.push_back(pid);
    2877 #endif
     2812    AssertPtrReturnVoid(m->pClientWatcher);
     2813    m->pClientWatcher->addProcess(pid);
    28782814}
    28792815
     
    31123048}
    31133049
    3114 /** Event for onMachineUninit(), this is not a CallbackEvent */
    3115 class MachineUninitEvent : public Event
    3116 {
    3117 public:
    3118 
    3119     MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
    3120         : mVirtualBox(aVirtualBox), mMachine(aMachine)
    3121     {
    3122         Assert(aVirtualBox);
    3123         Assert(aMachine);
    3124     }
    3125 
    3126     void *handler()
    3127     {
    3128 #ifdef VBOX_WITH_RESOURCE_USAGE_API
    3129         /* Handle unregistering metrics here, as it is not vital to get
    3130          * it done immediately. It reduces the number of locks needed and
    3131          * the lock contention in SessionMachine::uninit. */
    3132         {
    3133             AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
    3134             mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
    3135         }
    3136 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
    3137 
    3138         return NULL;
    3139     }
    3140 
    3141 private:
    3142 
    3143     /**
    3144      *  Note that this is a weak ref -- the CallbackEvent handler thread
    3145      *  is bound to the lifetime of the VirtualBox instance, so it's safe.
    3146      */
    3147     VirtualBox        *mVirtualBox;
    3148 
    3149     /** Reference to the machine object. */
    3150     ComObjPtr<Machine> mMachine;
    3151 };
    3152 
    3153 /**
    3154  *  Trigger internal event. This isn't meant to be signalled to clients.
    3155  *  @note Doesn't lock any object.
    3156  */
    3157 void VirtualBox::onMachineUninit(Machine *aMachine)
    3158 {
    3159     postEvent(new MachineUninitEvent(this, aMachine));
    3160 }
    3161 
    31623050/**
    31633051 *  @note Doesn't lock any object.
     
    31803068    fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
    31813069}
    3182 void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled, 
    3183                                      IN_BSTR aNetwork, IN_BSTR aGateway, 
    3184                                      BOOL aAdvertiseDefaultIpv6RouteEnabled, 
     3070void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled,
     3071                                     IN_BSTR aNetwork, IN_BSTR aGateway,
     3072                                     BOOL aAdvertiseDefaultIpv6RouteEnabled,
    31853073                                     BOOL fNeedDhcpServer)
    31863074{
    3187     fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, 
     3075    fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled,
    31883076                               aNetwork, aGateway,
    31893077                               aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
    31903078}
    31913079
    3192 void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6, 
    3193                                          IN_BSTR aRuleName, NATProtocol_T proto, 
    3194                                          IN_BSTR aHostIp, LONG aHostPort, 
     3080void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6,
     3081                                         IN_BSTR aRuleName, NATProtocol_T proto,
     3082                                         IN_BSTR aHostIp, LONG aHostPort,
    31953083                                         IN_BSTR aGuestIp, LONG aGuestPort)
    31963084{
    3197     fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, 
     3085    fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create,
    31983086                                   fIpv6, aRuleName, proto,
    31993087                                   aHostIp, aHostPort,
     
    32573145        }
    32583146    }
     3147}
     3148
     3149/**
     3150 * Gets a reference to the machine list. This is the real thing, not a copy,
     3151 * so bad things will happen if the caller doesn't hold the necessary lock.
     3152 *
     3153 * @returns reference to machine list
     3154 *
     3155 * @note Caller must hold the VirtualBox object lock at least for reading.
     3156 */
     3157VirtualBox::MachinesOList &VirtualBox::getMachinesList(void)
     3158{
     3159    return m->allMachines;
    32593160}
    32603161
     
    48494750
    48504751/**
    4851  *  Thread function that watches the termination of all client processes
    4852  *  that have opened sessions using IMachine::LockMachine()
    4853  */
    4854 // static
    4855 DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
    4856 {
    4857     LogFlowFuncEnter();
    4858 
    4859     VirtualBox *that = (VirtualBox*)pvUser;
    4860     Assert(that);
    4861 
    4862     typedef std::vector< ComObjPtr<Machine> > MachineVector;
    4863     typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
    4864 
    4865     SessionMachineVector machines;
    4866     MachineVector spawnedMachines;
    4867 
    4868     size_t cnt = 0;
    4869     size_t cntSpawned = 0;
    4870 
    4871     VirtualBoxBase::initializeComForThread();
    4872 
    4873 #if defined(RT_OS_WINDOWS)
    4874 
    4875     /// @todo (dmik) processes reaping!
    4876 
    4877     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
    4878     handles[0] = that->m->updateReq;
    4879 
    4880     do
    4881     {
    4882         AutoCaller autoCaller(that);
    4883         /* VirtualBox has been early uninitialized, terminate */
    4884         if (!autoCaller.isOk())
    4885             break;
    4886 
    4887         do
    4888         {
    4889             /* release the caller to let uninit() ever proceed */
    4890             autoCaller.release();
    4891 
    4892             DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
    4893                                                 handles,
    4894                                                 FALSE,
    4895                                                 INFINITE);
    4896 
    4897             /* Restore the caller before using VirtualBox. If it fails, this
    4898              * means VirtualBox is being uninitialized and we must terminate. */
    4899             autoCaller.add();
    4900             if (!autoCaller.isOk())
    4901                 break;
    4902 
    4903             bool update = false;
    4904 
    4905             if (rc == WAIT_OBJECT_0)
    4906             {
    4907                 /* update event is signaled */
    4908                 update = true;
    4909             }
    4910             else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
    4911             {
    4912                 /* machine mutex is released */
    4913                 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
    4914                 update = true;
    4915             }
    4916             else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
    4917             {
    4918                 /* machine mutex is abandoned due to client process termination */
    4919                 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
    4920                 update = true;
    4921             }
    4922             else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
    4923             {
    4924                 /* spawned VM process has terminated (normally or abnormally) */
    4925                 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
    4926                     checkForSpawnFailure();
    4927                 update = true;
    4928             }
    4929 
    4930             if (update)
    4931             {
    4932                 /* close old process handles */
    4933                 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
    4934                     CloseHandle(handles[i]);
    4935 
    4936                 // lock the machines list for reading
    4937                 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    4938 
    4939                 /* obtain a new set of opened machines */
    4940                 cnt = 0;
    4941                 machines.clear();
    4942 
    4943                 for (MachinesOList::iterator it = that->m->allMachines.begin();
    4944                      it != that->m->allMachines.end();
    4945                      ++it)
    4946                 {
    4947                     /// @todo handle situations with more than 64 objects
    4948                     AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
    4949                                    ("MAXIMUM_WAIT_OBJECTS reached"));
    4950 
    4951                     ComObjPtr<SessionMachine> sm;
    4952                     HANDLE ipcSem;
    4953                     if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
    4954                     {
    4955                         machines.push_back(sm);
    4956                         handles[1 + cnt] = ipcSem;
    4957                         ++cnt;
    4958                     }
    4959                 }
    4960 
    4961                 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
    4962 
    4963                 /* obtain a new set of spawned machines */
    4964                 cntSpawned = 0;
    4965                 spawnedMachines.clear();
    4966 
    4967                 for (MachinesOList::iterator it = that->m->allMachines.begin();
    4968                      it != that->m->allMachines.end();
    4969                      ++it)
    4970                 {
    4971                     /// @todo handle situations with more than 64 objects
    4972                     AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
    4973                                    ("MAXIMUM_WAIT_OBJECTS reached"));
    4974 
    4975                     RTPROCESS pid;
    4976                     if ((*it)->isSessionSpawning(&pid))
    4977                     {
    4978                         HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
    4979                         AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
    4980                                                pid, GetLastError()));
    4981                         if (rc == 0)
    4982                         {
    4983                             spawnedMachines.push_back(*it);
    4984                             handles[1 + cnt + cntSpawned] = ph;
    4985                             ++cntSpawned;
    4986                         }
    4987                     }
    4988                 }
    4989 
    4990                 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
    4991 
    4992                 // machines lock unwinds here
    4993             }
    4994         }
    4995         while (true);
    4996     }
    4997     while (0);
    4998 
    4999     /* close old process handles */
    5000     for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
    5001         CloseHandle(handles[i]);
    5002 
    5003     /* release sets of machines if any */
    5004     machines.clear();
    5005     spawnedMachines.clear();
    5006 
    5007     ::CoUninitialize();
    5008 
    5009 #elif defined(RT_OS_OS2)
    5010 
    5011     /// @todo (dmik) processes reaping!
    5012 
    5013     /* according to PMREF, 64 is the maximum for the muxwait list */
    5014     SEMRECORD handles[64];
    5015 
    5016     HMUX muxSem = NULLHANDLE;
    5017 
    5018     do
    5019     {
    5020         AutoCaller autoCaller(that);
    5021         /* VirtualBox has been early uninitialized, terminate */
    5022         if (!autoCaller.isOk())
    5023             break;
    5024 
    5025         do
    5026         {
    5027             /* release the caller to let uninit() ever proceed */
    5028             autoCaller.release();
    5029 
    5030             int vrc = RTSemEventWait(that->m->updateReq, 500);
    5031 
    5032             /* Restore the caller before using VirtualBox. If it fails, this
    5033              * means VirtualBox is being uninitialized and we must terminate. */
    5034             autoCaller.add();
    5035             if (!autoCaller.isOk())
    5036                 break;
    5037 
    5038             bool update = false;
    5039             bool updateSpawned = false;
    5040 
    5041             if (RT_SUCCESS(vrc))
    5042             {
    5043                 /* update event is signaled */
    5044                 update = true;
    5045                 updateSpawned = true;
    5046             }
    5047             else
    5048             {
    5049                 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
    5050                           ("RTSemEventWait returned %Rrc\n", vrc));
    5051 
    5052                 /* are there any mutexes? */
    5053                 if (cnt > 0)
    5054                 {
    5055                     /* figure out what's going on with machines */
    5056 
    5057                     unsigned long semId = 0;
    5058                     APIRET arc = ::DosWaitMuxWaitSem(muxSem,
    5059                                                      SEM_IMMEDIATE_RETURN, &semId);
    5060 
    5061                     if (arc == NO_ERROR)
    5062                     {
    5063                         /* machine mutex is normally released */
    5064                         Assert(semId >= 0 && semId < cnt);
    5065                         if (semId >= 0 && semId < cnt)
    5066                         {
    5067 #if 0//def DEBUG
    5068                             {
    5069                                 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
    5070                                 LogFlowFunc(("released mutex: machine='%ls'\n",
    5071                                              machines[semId]->name().raw()));
    5072                             }
    5073 #endif
    5074                             machines[semId]->checkForDeath();
    5075                         }
    5076                         update = true;
    5077                     }
    5078                     else if (arc == ERROR_SEM_OWNER_DIED)
    5079                     {
    5080                         /* machine mutex is abandoned due to client process
    5081                          * termination; find which mutex is in the Owner Died
    5082                          * state */
    5083                         for (size_t i = 0; i < cnt; ++ i)
    5084                         {
    5085                             PID pid; TID tid;
    5086                             unsigned long reqCnt;
    5087                             arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
    5088                             if (arc == ERROR_SEM_OWNER_DIED)
    5089                             {
    5090                                 /* close the dead mutex as asked by PMREF */
    5091                                 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
    5092 
    5093                                 Assert(i >= 0 && i < cnt);
    5094                                 if (i >= 0 && i < cnt)
    5095                                 {
    5096 #if 0//def DEBUG
    5097                                     {
    5098                                         AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
    5099                                         LogFlowFunc(("mutex owner dead: machine='%ls'\n",
    5100                                                      machines[i]->name().raw()));
    5101                                     }
    5102 #endif
    5103                                     machines[i]->checkForDeath();
    5104                                 }
    5105                             }
    5106                         }
    5107                         update = true;
    5108                     }
    5109                     else
    5110                         AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
    5111                                   ("DosWaitMuxWaitSem returned %d\n", arc));
    5112                 }
    5113 
    5114                 /* are there any spawning sessions? */
    5115                 if (cntSpawned > 0)
    5116                 {
    5117                     for (size_t i = 0; i < cntSpawned; ++ i)
    5118                         updateSpawned |= (spawnedMachines[i])->
    5119                             checkForSpawnFailure();
    5120                 }
    5121             }
    5122 
    5123             if (update || updateSpawned)
    5124             {
    5125                 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
    5126 
    5127                 if (update)
    5128                 {
    5129                     /* close the old muxsem */
    5130                     if (muxSem != NULLHANDLE)
    5131                         ::DosCloseMuxWaitSem(muxSem);
    5132 
    5133                     /* obtain a new set of opened machines */
    5134                     cnt = 0;
    5135                     machines.clear();
    5136 
    5137                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5138                          it != that->m->allMachines.end(); ++ it)
    5139                     {
    5140                         /// @todo handle situations with more than 64 objects
    5141                         AssertMsg(cnt <= 64 /* according to PMREF */,
    5142                                   ("maximum of 64 mutex semaphores reached (%d)",
    5143                                    cnt));
    5144 
    5145                         ComObjPtr<SessionMachine> sm;
    5146                         HMTX ipcSem;
    5147                         if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
    5148                         {
    5149                             machines.push_back(sm);
    5150                             handles[cnt].hsemCur = (HSEM)ipcSem;
    5151                             handles[cnt].ulUser = cnt;
    5152                             ++ cnt;
    5153                         }
    5154                     }
    5155 
    5156                     LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
    5157 
    5158                     if (cnt > 0)
    5159                     {
    5160                         /* create a new muxsem */
    5161                         APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
    5162                                                            handles,
    5163                                                            DCMW_WAIT_ANY);
    5164                         AssertMsg(arc == NO_ERROR,
    5165                                   ("DosCreateMuxWaitSem returned %d\n", arc));
    5166                         NOREF(arc);
    5167                     }
    5168                 }
    5169 
    5170                 if (updateSpawned)
    5171                 {
    5172                     /* obtain a new set of spawned machines */
    5173                     spawnedMachines.clear();
    5174 
    5175                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5176                          it != that->m->allMachines.end(); ++ it)
    5177                     {
    5178                         if ((*it)->isSessionSpawning())
    5179                             spawnedMachines.push_back(*it);
    5180                     }
    5181 
    5182                     cntSpawned = spawnedMachines.size();
    5183                     LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
    5184                 }
    5185             }
    5186         }
    5187         while (true);
    5188     }
    5189     while (0);
    5190 
    5191     /* close the muxsem */
    5192     if (muxSem != NULLHANDLE)
    5193         ::DosCloseMuxWaitSem(muxSem);
    5194 
    5195     /* release sets of machines if any */
    5196     machines.clear();
    5197     spawnedMachines.clear();
    5198 
    5199 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
    5200 
    5201     bool update = false;
    5202     bool updateSpawned = false;
    5203 
    5204     do
    5205     {
    5206         AutoCaller autoCaller(that);
    5207         if (!autoCaller.isOk())
    5208             break;
    5209 
    5210         do
    5211         {
    5212             /* release the caller to let uninit() ever proceed */
    5213             autoCaller.release();
    5214 
    5215             /* determine wait timeout adaptively: after updating information
    5216              * relevant to the client watcher, check a few times more
    5217              * frequently. This ensures good reaction time when the signalling
    5218              * has to be done a bit before the actual change for technical
    5219              * reasons, and saves CPU cycles when no activities are expected. */
    5220             RTMSINTERVAL cMillies;
    5221             {
    5222                 uint8_t uOld, uNew;
    5223                 do
    5224                 {
    5225                     uOld = ASMAtomicUoReadU8(&that->m->updateAdaptCtr);
    5226                     uNew = uOld ? uOld - 1 : uOld;
    5227                 } while (!ASMAtomicCmpXchgU8(&that->m->updateAdaptCtr, uNew, uOld));
    5228                 Assert(uOld <= RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
    5229                 cMillies = s_updateAdaptTimeouts[uOld];
    5230             }
    5231 
    5232             int rc = RTSemEventWait(that->m->updateReq, cMillies);
    5233 
    5234             /*
    5235              *  Restore the caller before using VirtualBox. If it fails, this
    5236              *  means VirtualBox is being uninitialized and we must terminate.
    5237              */
    5238             autoCaller.add();
    5239             if (!autoCaller.isOk())
    5240                 break;
    5241 
    5242             if (RT_SUCCESS(rc) || update || updateSpawned)
    5243             {
    5244                 /* RT_SUCCESS(rc) means an update event is signaled */
    5245 
    5246                 // lock the machines list for reading
    5247                 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
    5248 
    5249                 if (RT_SUCCESS(rc) || update)
    5250                 {
    5251                     /* obtain a new set of opened machines */
    5252                     machines.clear();
    5253 
    5254                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5255                          it != that->m->allMachines.end();
    5256                          ++it)
    5257                     {
    5258                         ComObjPtr<SessionMachine> sm;
    5259                         if ((*it)->isSessionOpenOrClosing(sm))
    5260                             machines.push_back(sm);
    5261                     }
    5262 
    5263                     cnt = machines.size();
    5264                     LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
    5265                 }
    5266 
    5267                 if (RT_SUCCESS(rc) || updateSpawned)
    5268                 {
    5269                     /* obtain a new set of spawned machines */
    5270                     spawnedMachines.clear();
    5271 
    5272                     for (MachinesOList::iterator it = that->m->allMachines.begin();
    5273                          it != that->m->allMachines.end();
    5274                          ++it)
    5275                     {
    5276                         if ((*it)->isSessionSpawning())
    5277                             spawnedMachines.push_back(*it);
    5278                     }
    5279 
    5280                     cntSpawned = spawnedMachines.size();
    5281                     LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
    5282                 }
    5283 
    5284                 // machines lock unwinds here
    5285             }
    5286 
    5287             update = false;
    5288             for (size_t i = 0; i < cnt; ++ i)
    5289                 update |= (machines[i])->checkForDeath();
    5290 
    5291             updateSpawned = false;
    5292             for (size_t i = 0; i < cntSpawned; ++ i)
    5293                 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
    5294 
    5295             /* reap child processes */
    5296             {
    5297                 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
    5298                 if (that->m->llProcesses.size())
    5299                 {
    5300                     LogFlowFunc(("UPDATE: child process count = %d\n",
    5301                                  that->m->llProcesses.size()));
    5302                     VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
    5303                     while (it != that->m->llProcesses.end())
    5304                     {
    5305                         RTPROCESS pid = *it;
    5306                         RTPROCSTATUS status;
    5307                         int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
    5308                         if (vrc == VINF_SUCCESS)
    5309                         {
    5310                             LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
    5311                                          pid, pid, status.iStatus,
    5312                                          status.enmReason));
    5313                             it = that->m->llProcesses.erase(it);
    5314                         }
    5315                         else
    5316                         {
    5317                             LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
    5318                                          pid, pid, vrc));
    5319                             if (vrc != VERR_PROCESS_RUNNING)
    5320                             {
    5321                                 /* remove the process if it is not already running */
    5322                                 it = that->m->llProcesses.erase(it);
    5323                             }
    5324                             else
    5325                                 ++ it;
    5326                         }
    5327                     }
    5328                 }
    5329             }
    5330         }
    5331         while (true);
    5332     }
    5333     while (0);
    5334 
    5335     /* release sets of machines if any */
    5336     machines.clear();
    5337     spawnedMachines.clear();
    5338 
    5339 #else
    5340 # error "Port me!"
    5341 #endif
    5342 
    5343     VirtualBoxBase::uninitializeComForThread();
    5344     LogFlowFuncLeave();
    5345     return 0;
    5346 }
    5347 
    5348 /**
    53494752 *  Thread function that handles custom events posted using #postEvent().
    53504753 */
Note: See TracChangeset for help on using the changeset viewer.

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