VirtualBox

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

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

Main: Removed too strict assertion in Session::GetRemoteConsole().

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