1
/* $Id: SessionImpl.cpp $ */
3
* VBox Client Session COM Class implementation in VBoxC.
7
* Copyright (C) 2006-2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
20
# include <sys/types.h>
21
# include <sys/stat.h>
26
#include "SessionImpl.h"
27
#include "ConsoleImpl.h"
30
#include "AutoCaller.h"
34
#include <iprt/process.h>
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);
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
46
#define CHECK_OPEN() \
48
if (mState != SessionState_Locked) \
49
return setError(E_UNEXPECTED, tr ("The session is not locked (session state: %s)"), Global::stringifySessionState(mState)); \
52
// constructor / destructor
53
/////////////////////////////////////////////////////////////////////////////
55
HRESULT Session::FinalConstruct()
57
LogFlowThisFunc(("\n"));
66
void Session::FinalRelease()
68
LogFlowThisFunc(("\n"));
75
// public initializer/uninitializer for internal purposes only
76
/////////////////////////////////////////////////////////////////////////////
79
* Initializes the Session object.
81
HRESULT Session::init()
83
/* Enclose the state transition NotReady->InInit->Ready */
84
AutoInitSpan autoInitSpan(this);
85
AssertReturn(autoInitSpan.isOk(), E_FAIL);
87
LogFlowThisFuncEnter();
89
mState = SessionState_Unlocked;
90
mType = SessionType_Null;
92
#if defined(RT_OS_WINDOWS)
95
#elif defined(RT_OS_OS2)
96
mIPCThread = NIL_RTTHREAD;
97
mIPCThreadSem = NIL_RTSEMEVENT;
98
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
104
/* Confirm a successful initialization when it's the case */
105
autoInitSpan.setSucceeded();
107
LogFlowThisFuncLeave();
113
* Uninitializes the Session object.
115
* @note Locks this object for writing.
117
void Session::uninit()
119
LogFlowThisFuncEnter();
121
/* Enclose the state transition Ready->InUninit->NotReady */
122
AutoUninitSpan autoUninitSpan(this);
123
if (autoUninitSpan.uninitDone())
125
LogFlowThisFunc(("Already uninitialized.\n"));
126
LogFlowThisFuncLeave();
130
/* close() needs write lock */
131
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
133
if (mState != SessionState_Unlocked)
135
Assert(mState == SessionState_Locked ||
136
mState == SessionState_Spawning);
138
HRESULT rc = unlockMachine(true /* aFinalRelease */, false /* aFromServer */);
142
LogFlowThisFuncLeave();
145
// ISession properties
146
/////////////////////////////////////////////////////////////////////////////
148
STDMETHODIMP Session::COMGETTER(State)(SessionState_T *aState)
150
CheckComArgOutPointerValid(aState);
152
AutoCaller autoCaller(this);
153
if (FAILED(autoCaller.rc())) return autoCaller.rc();
155
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
162
STDMETHODIMP Session::COMGETTER(Type)(SessionType_T *aType)
164
CheckComArgOutPointerValid(aType);
166
AutoCaller autoCaller(this);
167
if (FAILED(autoCaller.rc())) return autoCaller.rc();
169
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
177
STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine)
179
CheckComArgOutPointerValid(aMachine);
181
AutoCaller autoCaller(this);
182
if (FAILED(autoCaller.rc())) return autoCaller.rc();
184
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
190
rc = mConsole->machine().queryInterfaceTo(aMachine);
192
rc = mRemoteMachine.queryInterfaceTo(aMachine);
195
/** @todo VBox 3.3: replace E_FAIL with rc here. */
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"));
201
setError(E_FAIL, tr("Failed to query the remote session machine (%Rhrc)"), rc);
207
STDMETHODIMP Session::COMGETTER(Console)(IConsole **aConsole)
209
CheckComArgOutPointerValid(aConsole);
211
AutoCaller autoCaller(this);
212
if (FAILED(autoCaller.rc())) return autoCaller.rc();
214
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
220
rc = mConsole.queryInterfaceTo(aConsole);
222
rc = mRemoteConsole.queryInterfaceTo(aConsole);
226
/** @todo VBox 3.3: replace E_FAIL with rc here. */
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"));
232
setError(E_FAIL, tr("Failed to query the remote console (%Rhrc)"), rc);
239
/////////////////////////////////////////////////////////////////////////////
241
STDMETHODIMP Session::UnlockMachine()
243
LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType));
245
AutoCaller autoCaller(this);
246
if (FAILED(autoCaller.rc())) return autoCaller.rc();
248
/* close() needs write lock */
249
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
253
return unlockMachine(false /* aFinalRelease */, false /* aFromServer */);
256
// IInternalSessionControl methods
257
/////////////////////////////////////////////////////////////////////////////
259
STDMETHODIMP Session::GetPID(ULONG *aPid)
261
AssertReturn(aPid, E_POINTER);
263
AutoCaller autoCaller(this);
264
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
266
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
268
*aPid = (ULONG)RTProcSelf();
269
AssertCompile(sizeof(*aPid) == sizeof(RTPROCESS));
274
STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole)
276
LogFlowThisFuncEnter();
277
AssertReturn(aConsole, E_POINTER);
279
AutoCaller autoCaller(this);
280
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
282
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
284
AssertReturn(mState != SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
286
AssertMsgReturn(mType == SessionType_WriteLock && !!mConsole,
287
("This is not a direct session!\n"),
288
VBOX_E_INVALID_OBJECT_STATE);
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;
295
mConsole.queryInterfaceTo(aConsole);
297
LogFlowThisFuncLeave();
302
STDMETHODIMP Session::AssignMachine(IMachine *aMachine)
304
LogFlowThisFuncEnter();
305
LogFlowThisFunc(("aMachine=%p\n", aMachine));
307
AutoCaller autoCaller(this);
308
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
310
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
312
AssertReturn(mState == SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
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.
322
AssertReturn(mType == SessionType_Null, VBOX_E_INVALID_OBJECT_STATE);
323
mType = SessionType_Remote;
324
mState = SessionState_Spawning;
326
LogFlowThisFuncLeave();
332
/* query IInternalMachineControl interface */
334
AssertReturn(!!mControl, E_FAIL);
336
rc = mConsole.createObject();
337
AssertComRCReturn(rc, rc);
339
rc = mConsole->init(aMachine, mControl);
340
AssertComRCReturn(rc, rc);
342
rc = grabIPCSemaphore();
345
* Reference the VirtualBox object to ensure the server is up
346
* until the session is closed
349
rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
353
mType = SessionType_WriteLock;
354
mState = SessionState_Locked;
364
LogFlowThisFunc(("rc=%08X\n", rc));
365
LogFlowThisFuncLeave();
370
STDMETHODIMP Session::AssignRemoteMachine(IMachine *aMachine, IConsole *aConsole)
372
LogFlowThisFuncEnter();
373
LogFlowThisFunc(("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
375
AssertReturn(aMachine && aConsole, E_INVALIDARG);
377
AutoCaller autoCaller(this);
378
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
380
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
382
AssertReturn(mState == SessionState_Unlocked ||
383
mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
387
/* query IInternalMachineControl interface */
389
AssertReturn(!!mControl, E_FAIL);
392
// currently, the remote session returns the same machine and
393
// console objects as the direct session, thus giving the
394
// (remote) client full control over the direct session. For the
395
// console, it is the desired behavior (the ability to control
396
// VM execution is a must for the remote session). What about
397
// the machine object, we may want to prevent the remote client
398
// from modifying machine data. In this case, we must:
399
// 1) assign the Machine object (instead of the SessionMachine
400
// object that is passed to this method) to mRemoteMachine;
401
// 2) remove GetMachine() property from the IConsole interface
402
// because it always returns the SessionMachine object
403
// (alternatively, we can supply a separate IConsole
404
// implementation that will return the Machine object in
405
// response to GetMachine()).
407
mRemoteMachine = aMachine;
408
mRemoteConsole = aConsole;
411
* Reference the VirtualBox object to ensure the server is up
412
* until the session is closed
414
rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
419
* RemoteSession type can be already set by AssignMachine() when its
420
* argument is NULL (a special case)
422
if (mType != SessionType_Remote)
423
mType = SessionType_Shared;
425
Assert(mState == SessionState_Spawning);
427
mState = SessionState_Locked;
433
mRemoteMachine.setNull();
434
mRemoteConsole.setNull();
437
LogFlowThisFunc(("rc=%08X\n", rc));
438
LogFlowThisFuncLeave();
443
STDMETHODIMP Session::UpdateMachineState(MachineState_T aMachineState)
445
AutoCaller autoCaller(this);
447
if (autoCaller.state() != Ready)
450
* We might have already entered Session::uninit() at this point, so
451
* return silently (not interested in the state change during uninit)
453
LogFlowThisFunc(("Already uninitialized.\n"));
457
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
459
if (mState == SessionState_Unlocking)
461
LogFlowThisFunc(("Already being unlocked.\n"));
465
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
466
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
468
AssertReturn(!mControl.isNull(), E_FAIL);
469
AssertReturn(!mConsole.isNull(), E_FAIL);
471
return mConsole->updateMachineState(aMachineState);
474
STDMETHODIMP Session::Uninitialize()
476
LogFlowThisFuncEnter();
478
AutoCaller autoCaller(this);
482
if (autoCaller.state() == Ready)
484
/* close() needs write lock */
485
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
487
LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
489
if (mState == SessionState_Unlocking)
491
LogFlowThisFunc(("Already being unlocked.\n"));
495
AssertReturn(mState == SessionState_Locked ||
496
mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
498
/* close ourselves */
499
rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
501
else if (autoCaller.state() == InUninit)
504
* We might have already entered Session::uninit() at this point,
507
LogFlowThisFunc(("Already uninitialized.\n"));
511
LogWarningThisFunc(("UNEXPECTED uninitialization!\n"));
512
rc = autoCaller.rc();
515
LogFlowThisFunc(("rc=%08X\n", rc));
516
LogFlowThisFuncLeave();
521
STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
523
LogFlowThisFunc(("\n"));
525
AutoCaller autoCaller(this);
526
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
528
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
529
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
530
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
532
return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
535
STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
537
LogFlowThisFunc(("\n"));
539
AutoCaller autoCaller(this);
540
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
542
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
543
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
544
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
546
return mConsole->onSerialPortChange(serialPort);
549
STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
551
LogFlowThisFunc(("\n"));
553
AutoCaller autoCaller(this);
554
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
556
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
557
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
558
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
560
return mConsole->onParallelPortChange(parallelPort);
563
STDMETHODIMP Session::OnStorageControllerChange()
565
LogFlowThisFunc(("\n"));
567
AutoCaller autoCaller(this);
568
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
570
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
571
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
572
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
574
return mConsole->onStorageControllerChange();
577
STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
579
LogFlowThisFunc(("\n"));
581
AutoCaller autoCaller(this);
582
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
584
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
585
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
586
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
588
return mConsole->onMediumChange(aMediumAttachment, aForce);
591
STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
593
LogFlowThisFunc(("\n"));
595
AutoCaller autoCaller(this);
596
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
598
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
599
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
600
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
602
return mConsole->onCPUChange(aCPU, aRemove);
605
STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
607
LogFlowThisFunc(("\n"));
609
AutoCaller autoCaller(this);
610
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
612
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
613
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
614
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
616
return mConsole->onCPUExecutionCapChange(aExecutionCap);
619
STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
621
LogFlowThisFunc(("\n"));
623
AutoCaller autoCaller(this);
624
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
626
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
627
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
628
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
630
return mConsole->onVRDEServerChange(aRestart);
633
STDMETHODIMP Session::OnUSBControllerChange()
635
LogFlowThisFunc(("\n"));
637
AutoCaller autoCaller(this);
638
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
640
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
641
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
642
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
644
return mConsole->onUSBControllerChange();
647
STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
649
LogFlowThisFunc(("\n"));
651
AutoCaller autoCaller(this);
652
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
654
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
655
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
656
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
658
return mConsole->onSharedFolderChange(aGlobal);
661
STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
662
IVirtualBoxErrorInfo *aError,
665
LogFlowThisFunc(("\n"));
667
AutoCaller autoCaller(this);
668
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
670
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
671
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
672
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
674
return mConsole->onUSBDeviceAttach(aDevice, aError, aMaskedIfs);
677
STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
678
IVirtualBoxErrorInfo *aError)
680
LogFlowThisFunc(("\n"));
682
AutoCaller autoCaller(this);
683
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
685
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
686
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
687
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
689
return mConsole->onUSBDeviceDetach(aId, aError);
692
STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
694
AutoCaller autoCaller(this);
695
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
697
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
699
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
701
if (mState != SessionState_Locked)
703
/* the call from Machine issued when the session is open can arrive
704
* after the session starts closing or gets closed. Note that when
705
* aCheck is false, we return E_FAIL to indicate that aWinId we return
709
return aCheck ? S_OK : E_FAIL;
712
return mConsole->onShowWindow(aCheck, aCanShow, aWinId);
715
STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
717
LogFlowThisFunc(("\n"));
719
AutoCaller autoCaller(this);
720
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
722
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
723
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
724
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
726
return mConsole->onBandwidthGroupChange(aBandwidthGroup);
729
STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
731
LogFlowThisFunc(("\n"));
733
AutoCaller autoCaller(this);
734
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
736
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
737
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
738
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
740
return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove);
743
STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
744
BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
746
#ifdef VBOX_WITH_GUEST_PROPS
747
AutoCaller autoCaller(this);
748
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
750
if (mState != SessionState_Locked)
751
return setError(VBOX_E_INVALID_VM_STATE,
752
tr("Machine is not locked by session (session state: %s)."),
753
Global::stringifySessionState(mState));
754
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
755
CheckComArgStrNotEmptyOrNull(aName);
756
if (!aIsSetter && !VALID_PTR(aRetValue))
758
if (!aIsSetter && !VALID_PTR(aRetTimestamp))
760
if (!aIsSetter && !VALID_PTR(aRetFlags))
762
/* aValue can be NULL for a setter call if the property is to be deleted. */
763
if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
765
/* aFlags can be null if it is to be left as is */
766
if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
769
return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
771
return mConsole->setGuestProperty(aName, aValue, aFlags);
772
#else /* VBOX_WITH_GUEST_PROPS not defined */
773
ReturnComNotImplemented();
774
#endif /* VBOX_WITH_GUEST_PROPS not defined */
777
STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
778
ComSafeArrayOut(BSTR, aNames),
779
ComSafeArrayOut(BSTR, aValues),
780
ComSafeArrayOut(LONG64, aTimestamps),
781
ComSafeArrayOut(BSTR, aFlags))
783
#ifdef VBOX_WITH_GUEST_PROPS
784
AutoCaller autoCaller(this);
785
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
787
if (mState != SessionState_Locked)
788
return setError(VBOX_E_INVALID_VM_STATE,
789
tr("Machine is not locked by session (session state: %s)."),
790
Global::stringifySessionState(mState));
791
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
792
if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
794
if (ComSafeArrayOutIsNull(aNames))
796
if (ComSafeArrayOutIsNull(aValues))
798
if (ComSafeArrayOutIsNull(aTimestamps))
800
if (ComSafeArrayOutIsNull(aFlags))
802
return mConsole->enumerateGuestProperties(aPatterns,
803
ComSafeArrayOutArg(aNames),
804
ComSafeArrayOutArg(aValues),
805
ComSafeArrayOutArg(aTimestamps),
806
ComSafeArrayOutArg(aFlags));
807
#else /* VBOX_WITH_GUEST_PROPS not defined */
808
ReturnComNotImplemented();
809
#endif /* VBOX_WITH_GUEST_PROPS not defined */
812
STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
813
ULONG aSourceIdx, ULONG aTargetIdx,
814
IMedium *aSource, IMedium *aTarget,
816
IMedium *aParentForTarget,
817
ComSafeArrayIn(IMedium *, aChildrenToReparent),
818
IProgress *aProgress)
820
AutoCaller autoCaller(this);
821
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
823
if (mState != SessionState_Locked)
824
return setError(VBOX_E_INVALID_VM_STATE,
825
tr("Machine is not locked by session (session state: %s)."),
826
Global::stringifySessionState(mState));
827
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
828
CheckComArgNotNull(aMediumAttachment);
829
CheckComArgSafeArrayNotNull(aChildrenToReparent);
831
return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
832
aTargetIdx, aSource, aTarget,
833
aMergeForward, aParentForTarget,
834
ComSafeArrayInArg(aChildrenToReparent),
838
STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable)
840
AutoCaller autoCaller(this);
841
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
843
mConsole->enableVMMStatistics(aEnable);
849
///////////////////////////////////////////////////////////////////////////////
852
* Unlocks a machine associated with the current session.
854
* @param aFinalRelease called as a result of FinalRelease()
855
* @param aFromServer called as a result of Uninitialize()
857
* @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize().
858
* @note Locks this object for writing.
860
HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
862
LogFlowThisFuncEnter();
863
LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
864
aFinalRelease, aFromServer));
866
AutoCaller autoCaller(this);
867
AssertComRCReturnRC(autoCaller.rc());
869
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
871
LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
873
if (mState != SessionState_Locked)
875
Assert(mState == SessionState_Spawning);
877
/* The session object is going to be uninitialized before it has been
878
* assigned a direct console of the machine the client requested to open
879
* a remote session to using IVirtualBox:: openRemoteSession(). It is OK
880
* only if this close request comes from the server (for example, it
881
* detected that the VM process it started terminated before opening a
882
* direct session). Otherwise, it means that the client is too fast and
883
* trying to close the session before waiting for the progress object it
884
* got from IVirtualBox:: openRemoteSession() to complete, so assert. */
887
mState = SessionState_Unlocked;
888
mType = SessionType_Null;
889
#if defined(RT_OS_WINDOWS)
890
Assert(!mIPCSem && !mIPCThreadSem);
891
#elif defined(RT_OS_OS2)
892
Assert(mIPCThread == NIL_RTTHREAD &&
893
mIPCThreadSem == NIL_RTSEMEVENT);
894
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
895
Assert(mIPCSem == -1);
899
LogFlowThisFuncLeave();
903
/* go to the closing state */
904
mState = SessionState_Unlocking;
906
if (mType == SessionType_WriteLock)
913
mRemoteMachine.setNull();
914
mRemoteConsole.setNull();
917
ComPtr<IProgress> progress;
919
if (!aFinalRelease && !aFromServer)
922
* We trigger OnSessionEnd() only when the session closes itself using
923
* Close(). Note that if isFinalRelease = TRUE here, this means that
924
* the client process has already initialized the termination procedure
925
* without issuing Close() and the IPC channel is no more operational --
926
* so we cannot call the server's method (it will definitely fail). The
927
* server will instead simply detect the abnormal client death (since
928
* OnSessionEnd() is not called) and reset the machine state to Aborted.
932
* while waiting for OnSessionEnd() to complete one of our methods
933
* can be called by the server (for example, Uninitialize(), if the
934
* direct session has initiated a closure just a bit before us) so
935
* we need to release the lock to avoid deadlocks. The state is already
936
* SessionState_Closing here, so it's safe.
940
LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
941
HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
942
LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
947
* If we get E_UNEXPECTED this means that the direct session has already
948
* been closed, we're just too late with our notification and nothing more
950
* bird: Seems E_ACCESSDENIED is what gets returned these days; see
951
* VirtualBoxBase::addCaller.
953
if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
956
#ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */
963
if (mType == SessionType_WriteLock)
965
releaseIPCSemaphore();
966
if (!aFinalRelease && !aFromServer)
969
* Wait for the server to grab the semaphore and destroy the session
970
* machine (allowing us to open a new session with the same machine
971
* once this method returns)
975
progress->WaitForCompletion(-1);
979
mState = SessionState_Unlocked;
980
mType = SessionType_Null;
982
/* release the VirtualBox instance as the very last step */
983
mVirtualBox.setNull();
985
LogFlowThisFuncLeave();
989
/** @note To be called only from #AssignMachine() */
990
HRESULT Session::grabIPCSemaphore()
994
/* open the IPC semaphore based on the sessionId and try to grab it */
996
rc = mControl->GetIPCId(ipcId.asOutParam());
997
AssertComRCReturnRC(rc);
999
LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
1001
#if defined(RT_OS_WINDOWS)
1004
* Since Session is an MTA object, this method can be executed on
1005
* any thread, and this thread will not necessarily match the thread on
1006
* which close() will be called later. Therefore, we need a separate
1007
* thread to hold the IPC mutex and then release it in close().
1010
mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
1011
AssertMsgReturn(mIPCThreadSem,
1012
("Cannot create an event sem, err=%d", ::GetLastError()),
1016
data[0] = (void*)(BSTR)ipcId.raw();
1017
data[1] = (void*)mIPCThreadSem;
1018
data[2] = 0; /* will get an output from the thread */
1020
/* create a thread to hold the IPC mutex until signalled to release it */
1022
int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
1023
AssertRCReturn(vrc, E_FAIL);
1025
/* wait until thread init is completed */
1026
DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
1027
AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
1030
if (wrc == WAIT_OBJECT_0 && data[2])
1032
/* memorize the event sem we should signal in close() */
1033
mIPCSem = (HANDLE)data[2];
1038
::CloseHandle(mIPCThreadSem);
1039
mIPCThreadSem = NULL;
1043
#elif defined(RT_OS_OS2)
1045
/* We use XPCOM where any message (including close()) can arrive on any
1046
* worker thread (which will not necessarily match this thread that opens
1047
* the mutex). Therefore, we need a separate thread to hold the IPC mutex
1048
* and then release it in close(). */
1050
int vrc = RTSemEventCreate(&mIPCThreadSem);
1051
AssertRCReturn(vrc, E_FAIL);
1054
data[0] = (void*)ipcId.raw();
1055
data[1] = (void*)mIPCThreadSem;
1056
data[2] = (void*)false; /* will get the thread result here */
1058
/* create a thread to hold the IPC mutex until signalled to release it */
1059
vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
1060
0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
1061
AssertRCReturn(vrc, E_FAIL);
1063
/* wait until thread init is completed */
1064
vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1065
AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
1067
/* the thread must succeed */
1068
AssertReturn((bool)data[2], E_FAIL);
1070
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1072
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
1073
Utf8Str ipcKey = ipcId;
1074
key_t key = RTStrToUInt32(ipcKey.c_str());
1075
AssertMsgReturn (key != 0,
1076
("Key value of 0 is not valid for IPC semaphore"),
1078
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1079
Utf8Str semName = ipcId;
1080
char *pszSemName = NULL;
1081
RTStrUtf8ToCurrentCP (&pszSemName, semName);
1082
key_t key = ::ftok (pszSemName, 'V');
1083
RTStrFree (pszSemName);
1084
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1086
mIPCSem = ::semget (key, 0, 0);
1087
AssertMsgReturn (mIPCSem >= 0,
1088
("Cannot open IPC semaphore, errno=%d", errno),
1091
/* grab the semaphore */
1092
::sembuf sop = { 0, -1, SEM_UNDO };
1093
int rv = ::semop (mIPCSem, &sop, 1);
1094
AssertMsgReturn (rv == 0,
1095
("Cannot grab IPC semaphore, errno=%d", errno),
1105
/** @note To be called only from #close() */
1106
void Session::releaseIPCSemaphore()
1108
/* release the IPC semaphore */
1109
#if defined(RT_OS_WINDOWS)
1111
if (mIPCSem && mIPCThreadSem)
1114
* tell the thread holding the IPC mutex to release it;
1115
* it will close mIPCSem handle
1117
::SetEvent (mIPCSem);
1118
/* wait for the thread to finish */
1119
::WaitForSingleObject (mIPCThreadSem, INFINITE);
1120
::CloseHandle (mIPCThreadSem);
1122
mIPCThreadSem = NULL;
1126
#elif defined(RT_OS_OS2)
1128
if (mIPCThread != NIL_RTTHREAD)
1130
Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1132
/* tell the thread holding the IPC mutex to release it */
1133
int vrc = RTSemEventSignal (mIPCThreadSem);
1134
AssertRC(vrc == NO_ERROR);
1136
/* wait for the thread to finish */
1137
vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1138
Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1140
mIPCThread = NIL_RTTHREAD;
1143
if (mIPCThreadSem != NIL_RTSEMEVENT)
1145
RTSemEventDestroy (mIPCThreadSem);
1146
mIPCThreadSem = NIL_RTSEMEVENT;
1149
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1153
::sembuf sop = { 0, 1, SEM_UNDO };
1154
::semop (mIPCSem, &sop, 1);
1164
#if defined(RT_OS_WINDOWS)
1165
/** VM IPC mutex holder thread */
1166
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1171
void **data = (void **) pvUser;
1173
BSTR sessionId = (BSTR)data[0];
1174
HANDLE initDoneSem = (HANDLE)data[1];
1176
HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1177
AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1181
/* grab the mutex */
1182
DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1183
AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1184
if (wrc == WAIT_OBJECT_0)
1186
HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1187
AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1190
data[2] = (void*)finishSem;
1191
/* signal we're done with init */
1192
::SetEvent (initDoneSem);
1193
/* wait until we're signaled to release the IPC mutex */
1194
::WaitForSingleObject (finishSem, INFINITE);
1195
/* release the IPC mutex */
1196
LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1197
BOOL success = ::ReleaseMutex (ipcMutex);
1198
AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1199
::CloseHandle (ipcMutex);
1200
::CloseHandle (finishSem);
1205
/* signal we're done */
1206
::SetEvent (initDoneSem);
1214
#if defined(RT_OS_OS2)
1215
/** VM IPC mutex holder thread */
1216
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1221
void **data = (void **) pvUser;
1223
Utf8Str ipcId = (BSTR)data[0];
1224
RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1226
LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1228
HMTX ipcMutex = NULLHANDLE;
1229
APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1230
AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1232
if (arc == NO_ERROR)
1234
/* grab the mutex */
1235
LogFlowFunc (("grabbing IPC mutex...\n"));
1236
arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1237
AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1238
if (arc == NO_ERROR)
1240
/* store the answer */
1241
data[2] = (void*)true;
1242
/* signal we're done */
1243
int vrc = RTThreadUserSignal (Thread);
1246
/* wait until we're signaled to release the IPC mutex */
1247
LogFlowFunc (("waiting for termination signal..\n"));
1248
vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1249
Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1251
/* release the IPC mutex */
1252
LogFlowFunc (("releasing IPC mutex...\n"));
1253
arc = ::DosReleaseMutexSem (ipcMutex);
1254
AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1257
::DosCloseMutexSem (ipcMutex);
1260
/* store the answer */
1261
data[1] = (void*)false;
1262
/* signal we're done */
1263
int vrc = RTThreadUserSignal (Thread);
1271
/* vi: set tabstop=4 shiftwidth=4 expandtab: */