VirtualBox

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

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

Main: Shared Folders (#2130):

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