VirtualBox

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

Last change on this file since 11918 was 11918, checked in by vboxsync, 16 years ago

Main: Don't assert when the OnShowWindow request gets delayed and comes too late to Session from Machine.

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