VirtualBox

Changeset 47561 in vbox for trunk/src/VBox/Main/src-client


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/src-client
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • 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: */
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