1
/* $Id: SessionImpl.cpp 35368 2010-12-30 13:38:23Z vboxsync $ */
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"));
62
void Session::FinalRelease()
64
LogFlowThisFunc(("\n"));
69
// public initializer/uninitializer for internal purposes only
70
/////////////////////////////////////////////////////////////////////////////
73
* Initializes the Session object.
75
HRESULT Session::init()
77
/* Enclose the state transition NotReady->InInit->Ready */
78
AutoInitSpan autoInitSpan(this);
79
AssertReturn(autoInitSpan.isOk(), E_FAIL);
81
LogFlowThisFuncEnter();
83
mState = SessionState_Unlocked;
84
mType = SessionType_Null;
86
#if defined(RT_OS_WINDOWS)
89
#elif defined(RT_OS_OS2)
90
mIPCThread = NIL_RTTHREAD;
91
mIPCThreadSem = NIL_RTSEMEVENT;
92
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
98
/* Confirm a successful initialization when it's the case */
99
autoInitSpan.setSucceeded();
101
LogFlowThisFuncLeave();
107
* Uninitializes the Session object.
109
* @note Locks this object for writing.
111
void Session::uninit()
113
LogFlowThisFuncEnter();
115
/* Enclose the state transition Ready->InUninit->NotReady */
116
AutoUninitSpan autoUninitSpan(this);
117
if (autoUninitSpan.uninitDone())
119
LogFlowThisFunc(("Already uninitialized.\n"));
120
LogFlowThisFuncLeave();
124
/* close() needs write lock */
125
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
127
if (mState != SessionState_Unlocked)
129
Assert(mState == SessionState_Locked ||
130
mState == SessionState_Spawning);
132
HRESULT rc = unlockMachine(true /* aFinalRelease */, false /* aFromServer */);
136
LogFlowThisFuncLeave();
139
// ISession properties
140
/////////////////////////////////////////////////////////////////////////////
142
STDMETHODIMP Session::COMGETTER(State)(SessionState_T *aState)
144
CheckComArgOutPointerValid(aState);
146
AutoCaller autoCaller(this);
147
if (FAILED(autoCaller.rc())) return autoCaller.rc();
149
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
156
STDMETHODIMP Session::COMGETTER(Type)(SessionType_T *aType)
158
CheckComArgOutPointerValid(aType);
160
AutoCaller autoCaller(this);
161
if (FAILED(autoCaller.rc())) return autoCaller.rc();
163
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
171
STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine)
173
CheckComArgOutPointerValid(aMachine);
175
AutoCaller autoCaller(this);
176
if (FAILED(autoCaller.rc())) return autoCaller.rc();
178
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
184
rc = mConsole->machine().queryInterfaceTo(aMachine);
186
rc = mRemoteMachine.queryInterfaceTo(aMachine);
189
/** @todo VBox 3.3: replace E_FAIL with rc here. */
191
setError(E_FAIL, tr("Failed to query the session machine (%Rhrc)"), rc);
192
else if (FAILED_DEAD_INTERFACE(rc))
193
setError(E_FAIL, tr("Peer process crashed"));
195
setError(E_FAIL, tr("Failed to query the remote session machine (%Rhrc)"), rc);
201
STDMETHODIMP Session::COMGETTER(Console)(IConsole **aConsole)
203
CheckComArgOutPointerValid(aConsole);
205
AutoCaller autoCaller(this);
206
if (FAILED(autoCaller.rc())) return autoCaller.rc();
208
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
214
rc = mConsole.queryInterfaceTo(aConsole);
216
rc = mRemoteConsole.queryInterfaceTo(aConsole);
219
/** @todo VBox 3.3: replace E_FAIL with rc here. */
221
setError(E_FAIL, tr("Failed to query the console (%Rhrc)"), rc);
222
else if (FAILED_DEAD_INTERFACE(rc))
223
setError(E_FAIL, tr("Peer process crashed"));
225
setError(E_FAIL, tr("Failed to query the remote console (%Rhrc)"), rc);
232
/////////////////////////////////////////////////////////////////////////////
234
STDMETHODIMP Session::UnlockMachine()
236
LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType));
238
AutoCaller autoCaller(this);
239
if (FAILED(autoCaller.rc())) return autoCaller.rc();
241
/* close() needs write lock */
242
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
246
return unlockMachine(false /* aFinalRelease */, false /* aFromServer */);
249
// IInternalSessionControl methods
250
/////////////////////////////////////////////////////////////////////////////
252
STDMETHODIMP Session::GetPID(ULONG *aPid)
254
AssertReturn(aPid, E_POINTER);
256
AutoCaller autoCaller(this);
257
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
259
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
261
*aPid = (ULONG)RTProcSelf();
262
AssertCompile(sizeof(*aPid) == sizeof(RTPROCESS));
267
STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole)
269
LogFlowThisFuncEnter();
270
AssertReturn(aConsole, E_POINTER);
272
AutoCaller autoCaller(this);
273
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
275
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
277
AssertReturn(mState != SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
279
AssertMsgReturn(mType == SessionType_WriteLock && !!mConsole,
280
("This is not a direct session!\n"),
281
VBOX_E_INVALID_OBJECT_STATE);
283
/* return a failure if the session already transitioned to Closing
284
* but the server hasn't processed Machine::OnSessionEnd() yet. */
285
if (mState != SessionState_Locked)
286
return VBOX_E_INVALID_VM_STATE;
288
mConsole.queryInterfaceTo(aConsole);
290
LogFlowThisFuncLeave();
295
STDMETHODIMP Session::AssignMachine(IMachine *aMachine)
297
LogFlowThisFuncEnter();
298
LogFlowThisFunc(("aMachine=%p\n", aMachine));
300
AutoCaller autoCaller(this);
301
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
303
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
305
AssertReturn(mState == SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
310
* A special case: the server informs us that this session has been
311
* passed to IMachine::launchVMProcess() so this session will become
312
* remote (but not existing) when AssignRemoteMachine() is called.
315
AssertReturn(mType == SessionType_Null, VBOX_E_INVALID_OBJECT_STATE);
316
mType = SessionType_Remote;
317
mState = SessionState_Spawning;
319
LogFlowThisFuncLeave();
325
/* query IInternalMachineControl interface */
327
AssertReturn(!!mControl, E_FAIL);
329
rc = mConsole.createObject();
330
AssertComRCReturn(rc, rc);
332
rc = mConsole->init(aMachine, mControl);
333
AssertComRCReturn(rc, rc);
335
rc = grabIPCSemaphore();
338
* Reference the VirtualBox object to ensure the server is up
339
* until the session is closed
342
rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
346
mType = SessionType_WriteLock;
347
mState = SessionState_Locked;
357
LogFlowThisFunc(("rc=%08X\n", rc));
358
LogFlowThisFuncLeave();
363
STDMETHODIMP Session::AssignRemoteMachine(IMachine *aMachine, IConsole *aConsole)
365
LogFlowThisFuncEnter();
366
LogFlowThisFunc(("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
368
AssertReturn(aMachine && aConsole, E_INVALIDARG);
370
AutoCaller autoCaller(this);
371
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
373
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
375
AssertReturn(mState == SessionState_Unlocked ||
376
mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
380
/* query IInternalMachineControl interface */
382
AssertReturn(!!mControl, E_FAIL);
385
// currently, the remote session returns the same machine and
386
// console objects as the direct session, thus giving the
387
// (remote) client full control over the direct session. For the
388
// console, it is the desired behavior (the ability to control
389
// VM execution is a must for the remote session). What about
390
// the machine object, we may want to prevent the remote client
391
// from modifying machine data. In this case, we must:
392
// 1) assign the Machine object (instead of the SessionMachine
393
// object that is passed to this method) to mRemoteMachine;
394
// 2) remove GetMachine() property from the IConsole interface
395
// because it always returns the SessionMachine object
396
// (alternatively, we can supply a separate IConsole
397
// implementation that will return the Machine object in
398
// response to GetMachine()).
400
mRemoteMachine = aMachine;
401
mRemoteConsole = aConsole;
404
* Reference the VirtualBox object to ensure the server is up
405
* until the session is closed
407
rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam());
412
* RemoteSession type can be already set by AssignMachine() when its
413
* argument is NULL (a special case)
415
if (mType != SessionType_Remote)
416
mType = SessionType_Shared;
418
Assert(mState == SessionState_Spawning);
420
mState = SessionState_Locked;
426
mRemoteMachine.setNull();
427
mRemoteConsole.setNull();
430
LogFlowThisFunc(("rc=%08X\n", rc));
431
LogFlowThisFuncLeave();
436
STDMETHODIMP Session::UpdateMachineState(MachineState_T aMachineState)
438
AutoCaller autoCaller(this);
440
if (autoCaller.state() != Ready)
443
* We might have already entered Session::uninit() at this point, so
444
* return silently (not interested in the state change during uninit)
446
LogFlowThisFunc(("Already uninitialized.\n"));
450
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
452
if (mState == SessionState_Unlocking)
454
LogFlowThisFunc(("Already being unlocked.\n"));
458
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
459
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
461
AssertReturn(!mControl.isNull(), E_FAIL);
462
AssertReturn(!mConsole.isNull(), E_FAIL);
464
return mConsole->updateMachineState(aMachineState);
467
STDMETHODIMP Session::Uninitialize()
469
LogFlowThisFuncEnter();
471
AutoCaller autoCaller(this);
475
if (autoCaller.state() == Ready)
477
/* close() needs write lock */
478
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
480
LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
482
if (mState == SessionState_Unlocking)
484
LogFlowThisFunc(("Already being unlocked.\n"));
488
AssertReturn(mState == SessionState_Locked ||
489
mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
491
/* close ourselves */
492
rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
494
else if (autoCaller.state() == InUninit)
497
* We might have already entered Session::uninit() at this point,
500
LogFlowThisFunc(("Already uninitialized.\n"));
504
LogWarningThisFunc(("UNEXPECTED uninitialization!\n"));
505
rc = autoCaller.rc();
508
LogFlowThisFunc(("rc=%08X\n", rc));
509
LogFlowThisFuncLeave();
514
STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
516
LogFlowThisFunc(("\n"));
518
AutoCaller autoCaller(this);
519
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
521
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
522
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
523
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
525
return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
528
STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
530
LogFlowThisFunc(("\n"));
532
AutoCaller autoCaller(this);
533
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
535
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
536
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
537
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
539
return mConsole->onSerialPortChange(serialPort);
542
STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
544
LogFlowThisFunc(("\n"));
546
AutoCaller autoCaller(this);
547
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
549
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
550
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
551
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
553
return mConsole->onParallelPortChange(parallelPort);
556
STDMETHODIMP Session::OnStorageControllerChange()
558
LogFlowThisFunc(("\n"));
560
AutoCaller autoCaller(this);
561
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
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);
567
return mConsole->onStorageControllerChange();
570
STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
572
LogFlowThisFunc(("\n"));
574
AutoCaller autoCaller(this);
575
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
577
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
578
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
579
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
581
return mConsole->onMediumChange(aMediumAttachment, aForce);
584
STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
586
LogFlowThisFunc(("\n"));
588
AutoCaller autoCaller(this);
589
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
591
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
592
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
593
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
595
return mConsole->onCPUChange(aCPU, aRemove);
598
STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
600
LogFlowThisFunc(("\n"));
602
AutoCaller autoCaller(this);
603
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
605
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
606
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
607
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
609
return mConsole->onCPUExecutionCapChange(aExecutionCap);
612
STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
614
LogFlowThisFunc(("\n"));
616
AutoCaller autoCaller(this);
617
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
619
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
620
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
621
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
623
return mConsole->onVRDEServerChange(aRestart);
626
STDMETHODIMP Session::OnUSBControllerChange()
628
LogFlowThisFunc(("\n"));
630
AutoCaller autoCaller(this);
631
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
633
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
634
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
635
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
637
return mConsole->onUSBControllerChange();
640
STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
642
LogFlowThisFunc(("\n"));
644
AutoCaller autoCaller(this);
645
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
647
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
648
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
649
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
651
return mConsole->onSharedFolderChange(aGlobal);
654
STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
655
IVirtualBoxErrorInfo *aError,
658
LogFlowThisFunc(("\n"));
660
AutoCaller autoCaller(this);
661
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
663
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
664
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
665
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
667
return mConsole->onUSBDeviceAttach(aDevice, aError, aMaskedIfs);
670
STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
671
IVirtualBoxErrorInfo *aError)
673
LogFlowThisFunc(("\n"));
675
AutoCaller autoCaller(this);
676
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
678
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
679
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
680
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
682
return mConsole->onUSBDeviceDetach(aId, aError);
685
STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
687
AutoCaller autoCaller(this);
688
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
690
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
692
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
694
if (mState != SessionState_Locked)
696
/* the call from Machine issued when the session is open can arrive
697
* after the session starts closing or gets closed. Note that when
698
* aCheck is false, we return E_FAIL to indicate that aWinId we return
702
return aCheck ? S_OK : E_FAIL;
705
return mConsole->onShowWindow(aCheck, aCanShow, aWinId);
708
STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
710
LogFlowThisFunc(("\n"));
712
AutoCaller autoCaller(this);
713
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
715
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
716
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
717
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
719
return mConsole->onBandwidthGroupChange(aBandwidthGroup);
722
STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
723
BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
725
#ifdef VBOX_WITH_GUEST_PROPS
726
AutoCaller autoCaller(this);
727
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
729
if (mState != SessionState_Locked)
730
return setError(VBOX_E_INVALID_VM_STATE,
731
tr("Machine is not locked by session (session state: %s)."),
732
Global::stringifySessionState(mState));
733
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
734
CheckComArgStrNotEmptyOrNull(aName);
735
if (!aIsSetter && !VALID_PTR(aRetValue))
737
if (!aIsSetter && !VALID_PTR(aRetTimestamp))
739
if (!aIsSetter && !VALID_PTR(aRetFlags))
741
/* aValue can be NULL for a setter call if the property is to be deleted. */
742
if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
744
/* aFlags can be null if it is to be left as is */
745
if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
748
return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
750
return mConsole->setGuestProperty(aName, aValue, aFlags);
751
#else /* VBOX_WITH_GUEST_PROPS not defined */
752
ReturnComNotImplemented();
753
#endif /* VBOX_WITH_GUEST_PROPS not defined */
756
STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
757
ComSafeArrayOut(BSTR, aNames),
758
ComSafeArrayOut(BSTR, aValues),
759
ComSafeArrayOut(LONG64, aTimestamps),
760
ComSafeArrayOut(BSTR, aFlags))
762
#ifdef VBOX_WITH_GUEST_PROPS
763
AutoCaller autoCaller(this);
764
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
766
if (mState != SessionState_Locked)
767
return setError(VBOX_E_INVALID_VM_STATE,
768
tr("Machine is not locked by session (session state: %s)."),
769
Global::stringifySessionState(mState));
770
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
771
if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
773
if (ComSafeArrayOutIsNull(aNames))
775
if (ComSafeArrayOutIsNull(aValues))
777
if (ComSafeArrayOutIsNull(aTimestamps))
779
if (ComSafeArrayOutIsNull(aFlags))
781
return mConsole->enumerateGuestProperties(aPatterns,
782
ComSafeArrayOutArg(aNames),
783
ComSafeArrayOutArg(aValues),
784
ComSafeArrayOutArg(aTimestamps),
785
ComSafeArrayOutArg(aFlags));
786
#else /* VBOX_WITH_GUEST_PROPS not defined */
787
ReturnComNotImplemented();
788
#endif /* VBOX_WITH_GUEST_PROPS not defined */
791
STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
792
ULONG aSourceIdx, ULONG aTargetIdx,
793
IMedium *aSource, IMedium *aTarget,
795
IMedium *aParentForTarget,
796
ComSafeArrayIn(IMedium *, aChildrenToReparent),
797
IProgress *aProgress)
799
AutoCaller autoCaller(this);
800
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
802
if (mState != SessionState_Locked)
803
return setError(VBOX_E_INVALID_VM_STATE,
804
tr("Machine is not locked by session (session state: %s)."),
805
Global::stringifySessionState(mState));
806
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
807
CheckComArgNotNull(aMediumAttachment);
808
CheckComArgSafeArrayNotNull(aChildrenToReparent);
810
return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
811
aTargetIdx, aSource, aTarget,
812
aMergeForward, aParentForTarget,
813
ComSafeArrayInArg(aChildrenToReparent),
819
///////////////////////////////////////////////////////////////////////////////
822
* Unlocks a machine associated with the current session.
824
* @param aFinalRelease called as a result of FinalRelease()
825
* @param aFromServer called as a result of Uninitialize()
827
* @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize().
828
* @note Locks this object for writing.
830
HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
832
LogFlowThisFuncEnter();
833
LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
834
aFinalRelease, aFromServer));
836
AutoCaller autoCaller(this);
837
AssertComRCReturnRC(autoCaller.rc());
839
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
841
LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
843
if (mState != SessionState_Locked)
845
Assert(mState == SessionState_Spawning);
847
/* The session object is going to be uninitialized before it has been
848
* assigned a direct console of the machine the client requested to open
849
* a remote session to using IVirtualBox:: openRemoteSession(). It is OK
850
* only if this close request comes from the server (for example, it
851
* detected that the VM process it started terminated before opening a
852
* direct session). Otherwise, it means that the client is too fast and
853
* trying to close the session before waiting for the progress object it
854
* got from IVirtualBox:: openRemoteSession() to complete, so assert. */
857
mState = SessionState_Unlocked;
858
mType = SessionType_Null;
859
#if defined(RT_OS_WINDOWS)
860
Assert(!mIPCSem && !mIPCThreadSem);
861
#elif defined(RT_OS_OS2)
862
Assert(mIPCThread == NIL_RTTHREAD &&
863
mIPCThreadSem == NIL_RTSEMEVENT);
864
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
865
Assert(mIPCSem == -1);
869
LogFlowThisFuncLeave();
873
/* go to the closing state */
874
mState = SessionState_Unlocking;
876
if (mType == SessionType_WriteLock)
883
mRemoteMachine.setNull();
884
mRemoteConsole.setNull();
887
ComPtr<IProgress> progress;
889
if (!aFinalRelease && !aFromServer)
892
* We trigger OnSessionEnd() only when the session closes itself using
893
* Close(). Note that if isFinalRelease = TRUE here, this means that
894
* the client process has already initialized the termination procedure
895
* without issuing Close() and the IPC channel is no more operational --
896
* so we cannot call the server's method (it will definitely fail). The
897
* server will instead simply detect the abnormal client death (since
898
* OnSessionEnd() is not called) and reset the machine state to Aborted.
902
* while waiting for OnSessionEnd() to complete one of our methods
903
* can be called by the server (for example, Uninitialize(), if the
904
* direct session has initiated a closure just a bit before us) so
905
* we need to release the lock to avoid deadlocks. The state is already
906
* SessionState_Closing here, so it's safe.
910
LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
911
HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
912
LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
917
* If we get E_UNEXPECTED this means that the direct session has already
918
* been closed, we're just too late with our notification and nothing more
920
* bird: Seems E_ACCESSDENIED is what gets returned these days; see
921
* VirtualBoxBase::addCaller.
923
if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
926
#ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */
933
if (mType == SessionType_WriteLock)
935
releaseIPCSemaphore();
936
if (!aFinalRelease && !aFromServer)
939
* Wait for the server to grab the semaphore and destroy the session
940
* machine (allowing us to open a new session with the same machine
941
* once this method returns)
945
progress->WaitForCompletion(-1);
949
mState = SessionState_Unlocked;
950
mType = SessionType_Null;
952
/* release the VirtualBox instance as the very last step */
953
mVirtualBox.setNull();
955
LogFlowThisFuncLeave();
959
/** @note To be called only from #AssignMachine() */
960
HRESULT Session::grabIPCSemaphore()
964
/* open the IPC semaphore based on the sessionId and try to grab it */
966
rc = mControl->GetIPCId(ipcId.asOutParam());
967
AssertComRCReturnRC(rc);
969
LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
971
#if defined(RT_OS_WINDOWS)
974
* Since Session is an MTA object, this method can be executed on
975
* any thread, and this thread will not necessarily match the thread on
976
* which close() will be called later. Therefore, we need a separate
977
* thread to hold the IPC mutex and then release it in close().
980
mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
981
AssertMsgReturn(mIPCThreadSem,
982
("Cannot create an event sem, err=%d", ::GetLastError()),
986
data[0] = (void*)(BSTR)ipcId.raw();
987
data[1] = (void*)mIPCThreadSem;
988
data[2] = 0; /* will get an output from the thread */
990
/* create a thread to hold the IPC mutex until signalled to release it */
992
int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
993
AssertRCReturn(vrc, E_FAIL);
995
/* wait until thread init is completed */
996
DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
997
AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
1000
if (wrc == WAIT_OBJECT_0 && data[2])
1002
/* memorize the event sem we should signal in close() */
1003
mIPCSem = (HANDLE)data[2];
1008
::CloseHandle(mIPCThreadSem);
1009
mIPCThreadSem = NULL;
1013
#elif defined(RT_OS_OS2)
1015
/* We use XPCOM where any message (including close()) can arrive on any
1016
* worker thread (which will not necessarily match this thread that opens
1017
* the mutex). Therefore, we need a separate thread to hold the IPC mutex
1018
* and then release it in close(). */
1020
int vrc = RTSemEventCreate(&mIPCThreadSem);
1021
AssertRCReturn(vrc, E_FAIL);
1024
data[0] = (void*)ipcId.raw();
1025
data[1] = (void*)mIPCThreadSem;
1026
data[2] = (void*)false; /* will get the thread result here */
1028
/* create a thread to hold the IPC mutex until signalled to release it */
1029
vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
1030
0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
1031
AssertRCReturn(vrc, E_FAIL);
1033
/* wait until thread init is completed */
1034
vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1035
AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
1037
/* the thread must succeed */
1038
AssertReturn((bool)data[2], E_FAIL);
1040
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1042
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
1043
Utf8Str ipcKey = ipcId;
1044
key_t key = RTStrToUInt32(ipcKey.c_str());
1045
AssertMsgReturn (key != 0,
1046
("Key value of 0 is not valid for IPC semaphore"),
1048
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1049
Utf8Str semName = ipcId;
1050
char *pszSemName = NULL;
1051
RTStrUtf8ToCurrentCP (&pszSemName, semName);
1052
key_t key = ::ftok (pszSemName, 'V');
1053
RTStrFree (pszSemName);
1054
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1056
mIPCSem = ::semget (key, 0, 0);
1057
AssertMsgReturn (mIPCSem >= 0,
1058
("Cannot open IPC semaphore, errno=%d", errno),
1061
/* grab the semaphore */
1062
::sembuf sop = { 0, -1, SEM_UNDO };
1063
int rv = ::semop (mIPCSem, &sop, 1);
1064
AssertMsgReturn (rv == 0,
1065
("Cannot grab IPC semaphore, errno=%d", errno),
1075
/** @note To be called only from #close() */
1076
void Session::releaseIPCSemaphore()
1078
/* release the IPC semaphore */
1079
#if defined(RT_OS_WINDOWS)
1081
if (mIPCSem && mIPCThreadSem)
1084
* tell the thread holding the IPC mutex to release it;
1085
* it will close mIPCSem handle
1087
::SetEvent (mIPCSem);
1088
/* wait for the thread to finish */
1089
::WaitForSingleObject (mIPCThreadSem, INFINITE);
1090
::CloseHandle (mIPCThreadSem);
1092
mIPCThreadSem = NULL;
1096
#elif defined(RT_OS_OS2)
1098
if (mIPCThread != NIL_RTTHREAD)
1100
Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1102
/* tell the thread holding the IPC mutex to release it */
1103
int vrc = RTSemEventSignal (mIPCThreadSem);
1104
AssertRC(vrc == NO_ERROR);
1106
/* wait for the thread to finish */
1107
vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1108
Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1110
mIPCThread = NIL_RTTHREAD;
1113
if (mIPCThreadSem != NIL_RTSEMEVENT)
1115
RTSemEventDestroy (mIPCThreadSem);
1116
mIPCThreadSem = NIL_RTSEMEVENT;
1119
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1123
::sembuf sop = { 0, 1, SEM_UNDO };
1124
::semop (mIPCSem, &sop, 1);
1134
#if defined(RT_OS_WINDOWS)
1135
/** VM IPC mutex holder thread */
1136
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1141
void **data = (void **) pvUser;
1143
BSTR sessionId = (BSTR)data[0];
1144
HANDLE initDoneSem = (HANDLE)data[1];
1146
HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1147
AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1151
/* grab the mutex */
1152
DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1153
AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1154
if (wrc == WAIT_OBJECT_0)
1156
HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1157
AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1160
data[2] = (void*)finishSem;
1161
/* signal we're done with init */
1162
::SetEvent (initDoneSem);
1163
/* wait until we're signaled to release the IPC mutex */
1164
::WaitForSingleObject (finishSem, INFINITE);
1165
/* release the IPC mutex */
1166
LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1167
BOOL success = ::ReleaseMutex (ipcMutex);
1168
AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1169
::CloseHandle (ipcMutex);
1170
::CloseHandle (finishSem);
1175
/* signal we're done */
1176
::SetEvent (initDoneSem);
1184
#if defined(RT_OS_OS2)
1185
/** VM IPC mutex holder thread */
1186
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1191
void **data = (void **) pvUser;
1193
Utf8Str ipcId = (BSTR)data[0];
1194
RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1196
LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1198
HMTX ipcMutex = NULLHANDLE;
1199
APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1200
AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1202
if (arc == NO_ERROR)
1204
/* grab the mutex */
1205
LogFlowFunc (("grabbing IPC mutex...\n"));
1206
arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1207
AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1208
if (arc == NO_ERROR)
1210
/* store the answer */
1211
data[2] = (void*)true;
1212
/* signal we're done */
1213
int vrc = RTThreadUserSignal (Thread);
1216
/* wait until we're signaled to release the IPC mutex */
1217
LogFlowFunc (("waiting for termination signal..\n"));
1218
vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1219
Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1221
/* release the IPC mutex */
1222
LogFlowFunc (("releasing IPC mutex...\n"));
1223
arc = ::DosReleaseMutexSem (ipcMutex);
1224
AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1227
::DosCloseMutexSem (ipcMutex);
1230
/* store the answer */
1231
data[1] = (void*)false;
1232
/* signal we're done */
1233
int vrc = RTThreadUserSignal (Thread);
1241
/* vi: set tabstop=4 shiftwidth=4 expandtab: */