VirtualBox

source: vbox/trunk/src/VBox/Main/SessionImpl.cpp@ 6597

Last change on this file since 6597 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.2 KB
Line 
1/** @file
2 *
3 * VBox Client Session COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
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.
16 */
17
18#if defined(RT_OS_WINDOWS)
19#elif defined(RT_OS_LINUX)
20#endif
21
22#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
23# include <errno.h>
24# include <sys/types.h>
25# include <sys/stat.h>
26# include <sys/ipc.h>
27# include <sys/sem.h>
28#endif
29
30#include "SessionImpl.h"
31#include "ConsoleImpl.h"
32
33#include "Logging.h"
34
35#include <VBox/err.h>
36#include <iprt/process.h>
37
38#if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)
39/** VM IPC mutex holder thread */
40static DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser);
41#endif
42
43/**
44 * Local macro to check whether the session is open and return an error if not.
45 * @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this
46 * macro.
47 */
48#define CHECK_OPEN() \
49 do { \
50 if (mState != SessionState_SessionOpen) \
51 return setError (E_UNEXPECTED, \
52 tr ("The session is not open")); \
53 } while (0)
54
55// constructor / destructor
56/////////////////////////////////////////////////////////////////////////////
57
58HRESULT Session::FinalConstruct()
59{
60 LogFlowThisFunc (("\n"));
61
62 return init();
63}
64
65void Session::FinalRelease()
66{
67 LogFlowThisFunc (("\n"));
68
69 uninit (true /* aFinalRelease */);
70}
71
72// public initializer/uninitializer for internal purposes only
73/////////////////////////////////////////////////////////////////////////////
74
75/**
76 * Initializes the Session object.
77 */
78HRESULT Session::init()
79{
80 /* Enclose the state transition NotReady->InInit->Ready */
81 AutoInitSpan autoInitSpan (this);
82 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
83
84 LogFlowThisFuncEnter();
85
86 mState = SessionState_SessionClosed;
87 mType = SessionType_InvalidSessionType;
88
89#if defined(RT_OS_WINDOWS)
90 mIPCSem = NULL;
91 mIPCThreadSem = NULL;
92#elif defined(RT_OS_OS2)
93 mIPCThread = NIL_RTTHREAD;
94 mIPCThreadSem = NIL_RTSEMEVENT;
95#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
96 mIPCSem = -1;
97#else
98# error "Port me!"
99#endif
100
101 /* Confirm a successful initialization when it's the case */
102 autoInitSpan.setSucceeded();
103
104 LogFlowThisFuncLeave();
105
106 return S_OK;
107}
108
109/**
110 * Uninitializes the Session object.
111 *
112 * @note Locks this object for writing.
113 */
114void Session::uninit (bool aFinalRelease)
115{
116 LogFlowThisFuncEnter();
117 LogFlowThisFunc (("aFinalRelease=%d\n", aFinalRelease));
118
119 /* Enclose the state transition Ready->InUninit->NotReady */
120 AutoUninitSpan autoUninitSpan (this);
121 if (autoUninitSpan.uninitDone())
122 {
123 LogFlowThisFunc (("Already uninitialized.\n"));
124 LogFlowThisFuncLeave();
125 return;
126 }
127
128 AutoLock alock (this);
129
130 if (mState != SessionState_SessionClosed)
131 {
132 Assert (mState == SessionState_SessionOpen ||
133 mState == SessionState_SessionSpawning);
134
135 HRESULT rc = close (aFinalRelease, false /* aFromServer */);
136 AssertComRC (rc);
137 }
138
139 LogFlowThisFuncLeave();
140}
141
142// ISession properties
143/////////////////////////////////////////////////////////////////////////////
144
145STDMETHODIMP Session::COMGETTER(State) (SessionState_T *aState)
146{
147 if (!aState)
148 return E_POINTER;
149
150 AutoCaller autoCaller (this);
151 CheckComRCReturnRC (autoCaller.rc());
152
153 AutoReaderLock alock (this);
154
155 *aState = mState;
156
157 return S_OK;
158}
159
160STDMETHODIMP Session::COMGETTER(Type) (SessionType_T *aType)
161{
162 if (!aType)
163 return E_POINTER;
164
165 AutoCaller autoCaller (this);
166 CheckComRCReturnRC (autoCaller.rc());
167
168 AutoReaderLock alock (this);
169
170 CHECK_OPEN();
171
172 *aType = mType;
173 return S_OK;
174}
175
176STDMETHODIMP Session::COMGETTER(Machine) (IMachine **aMachine)
177{
178 if (!aMachine)
179 return E_POINTER;
180
181 AutoCaller autoCaller (this);
182 CheckComRCReturnRC (autoCaller.rc());
183
184 AutoReaderLock alock (this);
185
186 CHECK_OPEN();
187
188 HRESULT rc = E_FAIL;
189
190 if (mConsole)
191 rc = mConsole->machine().queryInterfaceTo (aMachine);
192 else
193 rc = mRemoteMachine.queryInterfaceTo (aMachine);
194 ComAssertComRC (rc);
195
196 return rc;
197}
198
199STDMETHODIMP Session::COMGETTER(Console) (IConsole **aConsole)
200{
201 if (!aConsole)
202 return E_POINTER;
203
204 AutoCaller autoCaller (this);
205 CheckComRCReturnRC (autoCaller.rc());
206
207 AutoReaderLock alock (this);
208
209 CHECK_OPEN();
210
211 HRESULT rc = E_FAIL;
212
213 if (mConsole)
214 rc = mConsole.queryInterfaceTo (aConsole);
215 else
216 rc = mRemoteConsole.queryInterfaceTo (aConsole);
217 ComAssertComRC (rc);
218
219 return rc;
220}
221
222// ISession methods
223/////////////////////////////////////////////////////////////////////////////
224
225STDMETHODIMP Session::Close()
226{
227 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
228
229 AutoCaller autoCaller (this);
230 CheckComRCReturnRC (autoCaller.rc());
231
232 /* close() needs write lock */
233 AutoLock alock (this);
234
235 CHECK_OPEN();
236
237 return close (false /* aFinalRelease */, false /* aFromServer */);
238}
239
240// IInternalSessionControl methods
241/////////////////////////////////////////////////////////////////////////////
242
243STDMETHODIMP Session::GetPID (ULONG *aPid)
244{
245 AssertReturn (aPid, E_POINTER);
246
247 AutoCaller autoCaller (this);
248 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
249
250 AutoReaderLock alock (this);
251
252 *aPid = (ULONG) RTProcSelf();
253 AssertCompile (sizeof (*aPid) == sizeof (RTPROCESS));
254
255 return S_OK;
256}
257
258STDMETHODIMP Session::GetRemoteConsole (IConsole **aConsole)
259{
260 AssertReturn (aConsole, E_POINTER);
261
262 AutoCaller autoCaller (this);
263 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
264
265 AutoReaderLock alock (this);
266
267 AssertReturn (mState == SessionState_SessionOpen, E_FAIL);
268
269 AssertMsgReturn (mType == SessionType_DirectSession && !!mConsole,
270 ("This is not a direct session!\n"), E_FAIL);
271
272 mConsole.queryInterfaceTo (aConsole);
273
274 return S_OK;
275}
276
277STDMETHODIMP Session::AssignMachine (IMachine *aMachine)
278{
279 LogFlowThisFuncEnter();
280 LogFlowThisFunc (("aMachine=%p\n", aMachine));
281
282 AutoCaller autoCaller (this);
283 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
284
285 AutoLock alock (this);
286
287 AssertReturn (mState == SessionState_SessionClosed, E_FAIL);
288
289 if (!aMachine)
290 {
291 /*
292 * A special case: the server informs us that this session has been
293 * passed to IVirtualBox::OpenRemoteSession() so this session will
294 * become remote (but not existing) when AssignRemoteMachine() is
295 * called.
296 */
297
298 AssertReturn (mType == SessionType_InvalidSessionType, E_FAIL);
299 mType = SessionType_RemoteSession;
300 mState = SessionState_SessionSpawning;
301
302 LogFlowThisFuncLeave();
303 return S_OK;
304 }
305
306 HRESULT rc = E_FAIL;
307
308 /* query IInternalMachineControl interface */
309 mControl = aMachine;
310 AssertReturn (!!mControl, E_FAIL);
311
312 rc = mConsole.createObject();
313 AssertComRCReturn (rc, rc);
314
315 rc = mConsole->init (aMachine, mControl);
316 AssertComRCReturn (rc, rc);
317
318 rc = grabIPCSemaphore();
319
320 /*
321 * Reference the VirtualBox object to ensure the server is up
322 * until the session is closed
323 */
324 if (SUCCEEDED (rc))
325 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
326
327 if (SUCCEEDED (rc))
328 {
329 mType = SessionType_DirectSession;
330 mState = SessionState_SessionOpen;
331 }
332 else
333 {
334 /* some cleanup */
335 mControl.setNull();
336 mConsole->uninit();
337 mConsole.setNull();
338 }
339
340 LogFlowThisFunc (("rc=%08X\n", rc));
341 LogFlowThisFuncLeave();
342
343 return rc;
344}
345
346STDMETHODIMP Session::AssignRemoteMachine (IMachine *aMachine, IConsole *aConsole)
347{
348 LogFlowThisFuncEnter();
349 LogFlowThisFunc (("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
350
351 AssertReturn (aMachine && aConsole, E_INVALIDARG);
352
353 AutoCaller autoCaller (this);
354 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
355
356 AutoLock alock (this);
357
358 AssertReturn (mState == SessionState_SessionClosed ||
359 mState == SessionState_SessionSpawning, E_FAIL);
360
361 HRESULT rc = E_FAIL;
362
363 /* query IInternalMachineControl interface */
364 mControl = aMachine;
365 AssertReturn (!!mControl, E_FAIL);
366
367 /// @todo (dmik)
368 // currently, the remote session returns the same machine and
369 // console objects as the direct session, thus giving the
370 // (remote) client full control over the direct session. For the
371 // console, it is the desired behavior (the ability to control
372 // VM execution is a must for the remote session). What about
373 // the machine object, we may want to prevent the remote client
374 // from modifying machine data. In this case, we must:
375 // 1) assign the Machine object (instead of the SessionMachine
376 // object that is passed to this method) to mRemoteMachine;
377 // 2) remove GetMachine() property from the IConsole interface
378 // because it always returns the SessionMachine object
379 // (alternatively, we can supply a separate IConsole
380 // implementation that will return the Machine object in
381 // response to GetMachine()).
382
383 mRemoteMachine = aMachine;
384 mRemoteConsole = aConsole;
385
386 /*
387 * Reference the VirtualBox object to ensure the server is up
388 * until the session is closed
389 */
390 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
391
392 if (SUCCEEDED (rc))
393 {
394 /*
395 * RemoteSession type can be already set by AssignMachine() when its
396 * argument is NULL (a special case)
397 */
398 if (mType != SessionType_RemoteSession)
399 mType = SessionType_ExistingSession;
400 else
401 Assert (mState == SessionState_SessionSpawning);
402
403 mState = SessionState_SessionOpen;
404 }
405 else
406 {
407 /* some cleanup */
408 mControl.setNull();
409 mRemoteMachine.setNull();
410 mRemoteConsole.setNull();
411 }
412
413 LogFlowThisFunc (("rc=%08X\n", rc));
414 LogFlowThisFuncLeave();
415
416 return rc;
417}
418
419STDMETHODIMP Session::UpdateMachineState (MachineState_T aMachineState)
420{
421 AutoCaller autoCaller (this);
422
423 if (autoCaller.state() != Ready)
424 {
425 /*
426 * We might have already entered Session::uninit() at this point, so
427 * return silently (not interested in the state change during uninit)
428 */
429 LogFlowThisFunc (("Already uninitialized.\n"));
430 return S_OK;
431 }
432
433 AutoReaderLock alock (this);
434
435 if (mState == SessionState_SessionClosing)
436 {
437 LogFlowThisFunc (("Already being closed.\n"));
438 return S_OK;
439 }
440
441 AssertReturn (mState == SessionState_SessionOpen &&
442 mType == SessionType_DirectSession, E_FAIL);
443
444 AssertReturn (!mControl.isNull(), E_FAIL);
445 AssertReturn (!mConsole.isNull(), E_FAIL);
446
447 return mConsole->updateMachineState (aMachineState);
448}
449
450STDMETHODIMP Session::Uninitialize()
451{
452 LogFlowThisFuncEnter();
453
454 AutoCaller autoCaller (this);
455
456 HRESULT rc = S_OK;
457
458 if (autoCaller.state() == Ready)
459 {
460 AutoReaderLock alock (this);
461
462 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
463
464 if (mState == SessionState_SessionClosing)
465 {
466 LogFlowThisFunc (("Already being closed.\n"));
467 return S_OK;
468 }
469
470 AssertReturn (mState == SessionState_SessionOpen, E_FAIL);
471
472 /* close ourselves */
473 rc = close (false /* aFinalRelease */, true /* aFromServer */);
474 }
475 else if (autoCaller.state() == InUninit)
476 {
477 /*
478 * We might have already entered Session::uninit() at this point,
479 * return silently
480 */
481 LogFlowThisFunc (("Already uninitialized.\n"));
482 }
483 else
484 {
485 LogWarningThisFunc (("UNEXPECTED uninitialization!\n"));
486 rc = autoCaller.rc();
487 }
488
489 LogFlowThisFunc (("rc=%08X\n", rc));
490 LogFlowThisFuncLeave();
491
492 return rc;
493}
494
495STDMETHODIMP Session::OnDVDDriveChange()
496{
497 LogFlowThisFunc (("\n"));
498
499 AutoCaller autoCaller (this);
500 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
501
502 AutoReaderLock alock (this);
503 AssertReturn (mState == SessionState_SessionOpen &&
504 mType == SessionType_DirectSession, E_FAIL);
505
506 return mConsole->onDVDDriveChange();
507}
508
509STDMETHODIMP Session::OnFloppyDriveChange()
510{
511 LogFlowThisFunc (("\n"));
512
513 AutoCaller autoCaller (this);
514 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
515
516 AutoReaderLock alock (this);
517 AssertReturn (mState == SessionState_SessionOpen &&
518 mType == SessionType_DirectSession, E_FAIL);
519
520 return mConsole->onFloppyDriveChange();
521}
522
523STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter)
524{
525 LogFlowThisFunc (("\n"));
526
527 AutoCaller autoCaller (this);
528 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
529
530 AutoReaderLock alock (this);
531 AssertReturn (mState == SessionState_SessionOpen &&
532 mType == SessionType_DirectSession, E_FAIL);
533
534 return mConsole->onNetworkAdapterChange(networkAdapter);
535}
536
537STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
538{
539 LogFlowThisFunc (("\n"));
540
541 AutoCaller autoCaller (this);
542 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
543
544 AutoReaderLock alock (this);
545 AssertReturn (mState == SessionState_SessionOpen &&
546 mType == SessionType_DirectSession, E_FAIL);
547
548 return mConsole->onSerialPortChange(serialPort);
549}
550
551STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
552{
553 LogFlowThisFunc (("\n"));
554
555 AutoCaller autoCaller (this);
556 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
557
558 AutoReaderLock alock (this);
559 AssertReturn (mState == SessionState_SessionOpen &&
560 mType == SessionType_DirectSession, E_FAIL);
561
562 return mConsole->onParallelPortChange(parallelPort);
563}
564
565STDMETHODIMP Session::OnVRDPServerChange()
566{
567 LogFlowThisFunc (("\n"));
568
569 AutoCaller autoCaller (this);
570 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
571
572 AutoReaderLock alock (this);
573 AssertReturn (mState == SessionState_SessionOpen &&
574 mType == SessionType_DirectSession, E_FAIL);
575
576 return mConsole->onVRDPServerChange();
577}
578
579STDMETHODIMP Session::OnUSBControllerChange()
580{
581 LogFlowThisFunc (("\n"));
582
583 AutoCaller autoCaller (this);
584 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
585
586 AutoReaderLock alock (this);
587 AssertReturn (mState == SessionState_SessionOpen &&
588 mType == SessionType_DirectSession, E_FAIL);
589
590 return mConsole->onUSBControllerChange();
591}
592
593STDMETHODIMP Session::OnSharedFolderChange (BOOL aGlobal)
594{
595 LogFlowThisFunc (("\n"));
596
597 AutoCaller autoCaller (this);
598 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
599
600 AutoReaderLock alock (this);
601 AssertReturn (mState == SessionState_SessionOpen &&
602 mType == SessionType_DirectSession, E_FAIL);
603
604 return mConsole->onSharedFolderChange (aGlobal);
605}
606
607STDMETHODIMP Session::OnUSBDeviceAttach (IUSBDevice *aDevice,
608 IVirtualBoxErrorInfo *aError,
609 ULONG aMaskedIfs)
610{
611 LogFlowThisFunc (("\n"));
612
613 AutoCaller autoCaller (this);
614 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
615
616 AutoReaderLock alock (this);
617 AssertReturn (mState == SessionState_SessionOpen &&
618 mType == SessionType_DirectSession, E_FAIL);
619
620 return mConsole->onUSBDeviceAttach (aDevice, aError, aMaskedIfs);
621}
622
623STDMETHODIMP Session::OnUSBDeviceDetach (INPTR GUIDPARAM aId,
624 IVirtualBoxErrorInfo *aError)
625{
626 LogFlowThisFunc (("\n"));
627
628 AutoCaller autoCaller (this);
629 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
630
631 AutoReaderLock alock (this);
632 AssertReturn (mState == SessionState_SessionOpen &&
633 mType == SessionType_DirectSession, E_FAIL);
634
635 return mConsole->onUSBDeviceDetach (aId, aError);
636}
637
638STDMETHODIMP Session::OnShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
639{
640 AutoCaller autoCaller (this);
641 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
642
643 AutoReaderLock alock (this);
644 AssertReturn (mState == SessionState_SessionOpen &&
645 mType == SessionType_DirectSession, E_FAIL);
646
647 return mConsole->onShowWindow (aCheck, aCanShow, aWinId);
648}
649
650// private methods
651///////////////////////////////////////////////////////////////////////////////
652
653/**
654 * Closes the current session.
655 *
656 * @param aFinalRelease called as a result of FinalRelease()
657 * @param aFromServer called as a result of Uninitialize()
658 *
659 * @note To be called only from #uninit(), #Close() or #Uninitialize().
660 * @note Locks this object for writing.
661 */
662HRESULT Session::close (bool aFinalRelease, bool aFromServer)
663{
664 LogFlowThisFuncEnter();
665 LogFlowThisFunc (("aFinalRelease=%d, isFromServer=%d\n",
666 aFinalRelease, aFromServer));
667
668 AutoCaller autoCaller (this);
669 AssertComRCReturnRC (autoCaller.rc());
670
671 AutoLock alock (this);
672
673 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
674
675 if (mState != SessionState_SessionOpen)
676 {
677 Assert (mState == SessionState_SessionSpawning);
678
679 /* The session object is going to be uninitialized by the client before
680 * it has been assigned a direct console of the machine the client
681 * requested to open a remote session to using IVirtualBox::
682 * openRemoteSession(). Theoretically it should not happen because
683 * openRemoteSession() doesn't return control to the client until the
684 * procedure is fully complete, so assert here. */
685 AssertFailed();
686
687 mState = SessionState_SessionClosed;
688 mType = SessionType_InvalidSessionType;
689#if defined(RT_OS_WINDOWS)
690 Assert (!mIPCSem && !mIPCThreadSem);
691#elif defined(RT_OS_OS2)
692 Assert (mIPCThread == NIL_RTTHREAD &&
693 mIPCThreadSem == NIL_RTSEMEVENT);
694#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
695 Assert (mIPCSem == -1);
696#else
697# error "Port me!"
698#endif
699 LogFlowThisFuncLeave();
700 return S_OK;
701 }
702
703 /* go to the closing state */
704 mState = SessionState_SessionClosing;
705
706 if (mType == SessionType_DirectSession)
707 {
708 mConsole->uninit();
709 mConsole.setNull();
710 }
711 else
712 {
713 mRemoteMachine.setNull();
714 mRemoteConsole.setNull();
715 }
716
717 ComPtr <IProgress> progress;
718
719 if (!aFinalRelease && !aFromServer)
720 {
721 /*
722 * We trigger OnSessionEnd() only when the session closes itself using
723 * Close(). Note that if isFinalRelease = TRUE here, this means that
724 * the client process has already initialized the termination procedure
725 * without issuing Close() and the IPC channel is no more operational --
726 * so we cannot call the server's method (it will definitely fail). The
727 * server will instead simply detect the abnormal client death (since
728 * OnSessionEnd() is not called) and reset the machine state to Aborted.
729 */
730
731 /*
732 * while waiting for OnSessionEnd() to complete one of our methods
733 * can be called by the server (for example, Uninitialize(), if the
734 * direct session has initiated a closure just a bit before us) so
735 * we need to release the lock to avoid deadlocks. The state is already
736 * SessionState_SessionClosing here, so it's safe.
737 */
738 alock.leave();
739
740 LogFlowThisFunc (("Calling mControl->OnSessionEnd()...\n"));
741 HRESULT rc = mControl->OnSessionEnd (this, progress.asOutParam());
742 LogFlowThisFunc (("mControl->OnSessionEnd()=%08X\n", rc));
743
744 alock.enter();
745
746 /*
747 * If we get E_UNEXPECTED this means that the direct session has already
748 * been closed, we're just too late with our notification and nothing more
749 */
750 if (mType != SessionType_DirectSession && rc == E_UNEXPECTED)
751 rc = S_OK;
752
753 AssertComRC (rc);
754 }
755
756 mControl.setNull();
757
758 if (mType == SessionType_DirectSession)
759 {
760 releaseIPCSemaphore();
761 if (!aFinalRelease && !aFromServer)
762 {
763 /*
764 * Wait for the server to grab the semaphore and destroy the session
765 * machine (allowing us to open a new session with the same machine
766 * once this method returns)
767 */
768 Assert (!!progress);
769 if (progress)
770 progress->WaitForCompletion (-1);
771 }
772 }
773
774 mState = SessionState_SessionClosed;
775 mType = SessionType_InvalidSessionType;
776
777 /* release the VirtualBox instance as the very last step */
778 mVirtualBox.setNull();
779
780 LogFlowThisFuncLeave();
781 return S_OK;
782}
783
784/** @note To be called only from #AssignMachine() */
785HRESULT Session::grabIPCSemaphore()
786{
787 HRESULT rc = E_FAIL;
788
789 /* open the IPC semaphore based on the sessionId and try to grab it */
790 Bstr ipcId;
791 rc = mControl->GetIPCId (ipcId.asOutParam());
792 AssertComRCReturnRC (rc);
793
794 LogFlowThisFunc (("ipcId='%ls'\n", ipcId.raw()));
795
796#if defined(RT_OS_WINDOWS)
797
798 /*
799 * Since Session is an MTA object, this method can be executed on
800 * any thread, and this thread will not necessarily match the thread on
801 * which close() will be called later. Therefore, we need a separate
802 * thread to hold the IPC mutex and then release it in close().
803 */
804
805 mIPCThreadSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
806 AssertMsgReturn (mIPCThreadSem,
807 ("Cannot create an event sem, err=%d", ::GetLastError()),
808 E_FAIL);
809
810 void *data [3];
811 data [0] = (void *) (BSTR) ipcId;
812 data [1] = (void *) mIPCThreadSem;
813 data [2] = 0; /* will get an output from the thread */
814
815 /* create a thread to hold the IPC mutex until signalled to release it */
816 RTTHREAD tid;
817 int vrc = RTThreadCreate (&tid, IPCMutexHolderThread, (void *) data,
818 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
819 AssertRCReturn (vrc, E_FAIL);
820
821 /* wait until thread init is completed */
822 DWORD wrc = ::WaitForSingleObject (mIPCThreadSem, INFINITE);
823 AssertMsg (wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
824 Assert (data [2]);
825
826 if (wrc == WAIT_OBJECT_0 && data [2])
827 {
828 /* memorize the event sem we should signal in close() */
829 mIPCSem = (HANDLE) data [2];
830 rc = S_OK;
831 }
832 else
833 {
834 ::CloseHandle (mIPCThreadSem);
835 mIPCThreadSem = NULL;
836 rc = E_FAIL;
837 }
838
839#elif defined(RT_OS_OS2)
840
841 /* We use XPCOM where any message (including close()) can arrive on any
842 * worker thread (which will not necessarily match this thread that opens
843 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
844 * and then release it in close(). */
845
846 int vrc = RTSemEventCreate (&mIPCThreadSem);
847 AssertRCReturn (vrc, E_FAIL);
848
849 void *data [3];
850 data [0] = (void *) ipcId.raw();
851 data [1] = (void *) mIPCThreadSem;
852 data [2] = (void *) false; /* will get the thread result here */
853
854 /* create a thread to hold the IPC mutex until signalled to release it */
855 vrc = RTThreadCreate (&mIPCThread, IPCMutexHolderThread, (void *) data,
856 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
857 AssertRCReturn (vrc, E_FAIL);
858
859 /* wait until thread init is completed */
860 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
861 AssertReturn (VBOX_SUCCESS (vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
862
863 /* the thread must succeed */
864 AssertReturn ((bool) data [2], E_FAIL);
865
866#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
867
868 Utf8Str semName = ipcId;
869 char *pszSemName = NULL;
870 RTStrUtf8ToCurrentCP (&pszSemName, semName);
871 key_t key = ::ftok (pszSemName, 0);
872 RTStrFree (pszSemName);
873
874 mIPCSem = ::semget (key, 0, 0);
875 AssertMsgReturn (mIPCSem >= 0,
876 ("Cannot open IPC semaphore, errno=%d", errno),
877 E_FAIL);
878
879 /* grab the semaphore */
880 ::sembuf sop = { 0, -1, SEM_UNDO };
881 int rv = ::semop (mIPCSem, &sop, 1);
882 AssertMsgReturn (rv == 0,
883 ("Cannot grab IPC semaphore, errno=%d", errno),
884 E_FAIL);
885
886#else
887# error "Port me!"
888#endif
889
890 return rc;
891}
892
893/** @note To be called only from #close() */
894void Session::releaseIPCSemaphore()
895{
896 /* release the IPC semaphore */
897#if defined(RT_OS_WINDOWS)
898
899 if (mIPCSem && mIPCThreadSem)
900 {
901 /*
902 * tell the thread holding the IPC mutex to release it;
903 * it will close mIPCSem handle
904 */
905 ::SetEvent (mIPCSem);
906 /* wait for the thread to finish */
907 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
908 ::CloseHandle (mIPCThreadSem);
909 }
910
911#elif defined(RT_OS_OS2)
912
913 if (mIPCThread != NIL_RTTHREAD)
914 {
915 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
916
917 /* tell the thread holding the IPC mutex to release it */
918 int vrc = RTSemEventSignal (mIPCThreadSem);
919 AssertRC (vrc == NO_ERROR);
920
921 /* wait for the thread to finish */
922 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
923 Assert (VBOX_SUCCESS (vrc) || vrc == VERR_INTERRUPTED);
924
925 mIPCThread = NIL_RTTHREAD;
926 }
927
928 if (mIPCThreadSem != NIL_RTSEMEVENT)
929 {
930 RTSemEventDestroy (mIPCThreadSem);
931 mIPCThreadSem = NIL_RTSEMEVENT;
932 }
933
934#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
935
936 if (mIPCSem >= 0)
937 {
938 ::sembuf sop = { 0, 1, SEM_UNDO };
939 ::semop (mIPCSem, &sop, 1);
940 }
941
942#else
943# error "Port me!"
944#endif
945}
946
947#if defined(RT_OS_WINDOWS)
948/** VM IPC mutex holder thread */
949DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
950{
951 LogFlowFuncEnter();
952
953 Assert (pvUser);
954 void **data = (void **) pvUser;
955
956 BSTR sessionId = (BSTR) data [0];
957 HANDLE initDoneSem = (HANDLE) data [1];
958
959 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
960 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
961
962 if (ipcMutex)
963 {
964 /* grab the mutex */
965 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
966 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
967 if (wrc == WAIT_OBJECT_0)
968 {
969 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
970 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
971 if (finishSem)
972 {
973 data [2] = (void *) finishSem;
974 /* signal we're done with init */
975 ::SetEvent (initDoneSem);
976 /* wait until we're signaled to release the IPC mutex */
977 ::WaitForSingleObject (finishSem, INFINITE);
978 /* release the IPC mutex */
979 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
980 BOOL success = ::ReleaseMutex (ipcMutex);
981 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
982 ::CloseHandle (ipcMutex);
983 ::CloseHandle (finishSem);
984 }
985 }
986 }
987
988 /* signal we're done */
989 ::SetEvent (initDoneSem);
990
991 LogFlowFuncLeave();
992
993 return 0;
994}
995#endif
996
997#if defined(RT_OS_OS2)
998/** VM IPC mutex holder thread */
999DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1000{
1001 LogFlowFuncEnter();
1002
1003 Assert (pvUser);
1004 void **data = (void **) pvUser;
1005
1006 Utf8Str ipcId = (BSTR) data [0];
1007 RTSEMEVENT finishSem = (RTSEMEVENT) data [1];
1008
1009 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1010
1011 HMTX ipcMutex = NULLHANDLE;
1012 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1013 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1014
1015 if (arc == NO_ERROR)
1016 {
1017 /* grab the mutex */
1018 LogFlowFunc (("grabbing IPC mutex...\n"));
1019 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1020 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1021 if (arc == NO_ERROR)
1022 {
1023 /* store the answer */
1024 data [2] = (void *) true;
1025 /* signal we're done */
1026 int vrc = RTThreadUserSignal (Thread);
1027 AssertRC (vrc);
1028
1029 /* wait until we're signaled to release the IPC mutex */
1030 LogFlowFunc (("waiting for termination signal..\n"));
1031 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1032 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1033
1034 /* release the IPC mutex */
1035 LogFlowFunc (("releasing IPC mutex...\n"));
1036 arc = ::DosReleaseMutexSem (ipcMutex);
1037 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1038 }
1039
1040 ::DosCloseMutexSem (ipcMutex);
1041 }
1042
1043 /* store the answer */
1044 data [1] = (void *) false;
1045 /* signal we're done */
1046 int vrc = RTThreadUserSignal (Thread);
1047 AssertRC (vrc);
1048
1049 LogFlowFuncLeave();
1050
1051 return 0;
1052}
1053#endif
Note: See TracBrowser for help on using the repository browser.

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