VirtualBox

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

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

Main: Fixed a bunch of assertions caused by the code that detects a spawned session termination. Don't fetch the new spawned session list unless it is changed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 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 ||
482 mState == SessionState_Spawning, E_FAIL);
483
484 /* close ourselves */
485 rc = close (false /* aFinalRelease */, true /* aFromServer */);
486 }
487 else if (autoCaller.state() == InUninit)
488 {
489 /*
490 * We might have already entered Session::uninit() at this point,
491 * return silently
492 */
493 LogFlowThisFunc (("Already uninitialized.\n"));
494 }
495 else
496 {
497 LogWarningThisFunc (("UNEXPECTED uninitialization!\n"));
498 rc = autoCaller.rc();
499 }
500
501 LogFlowThisFunc (("rc=%08X\n", rc));
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507STDMETHODIMP Session::OnDVDDriveChange()
508{
509 LogFlowThisFunc (("\n"));
510
511 AutoCaller autoCaller (this);
512 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
513
514 AutoReadLock alock (this);
515 AssertReturn (mState == SessionState_Open &&
516 mType == SessionType_Direct, E_FAIL);
517
518 return mConsole->onDVDDriveChange();
519}
520
521STDMETHODIMP Session::OnFloppyDriveChange()
522{
523 LogFlowThisFunc (("\n"));
524
525 AutoCaller autoCaller (this);
526 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
527
528 AutoReadLock alock (this);
529 AssertReturn (mState == SessionState_Open &&
530 mType == SessionType_Direct, E_FAIL);
531
532 return mConsole->onFloppyDriveChange();
533}
534
535STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter)
536{
537 LogFlowThisFunc (("\n"));
538
539 AutoCaller autoCaller (this);
540 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
541
542 AutoReadLock alock (this);
543 AssertReturn (mState == SessionState_Open &&
544 mType == SessionType_Direct, E_FAIL);
545
546 return mConsole->onNetworkAdapterChange(networkAdapter);
547}
548
549STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
550{
551 LogFlowThisFunc (("\n"));
552
553 AutoCaller autoCaller (this);
554 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
555
556 AutoReadLock alock (this);
557 AssertReturn (mState == SessionState_Open &&
558 mType == SessionType_Direct, E_FAIL);
559
560 return mConsole->onSerialPortChange(serialPort);
561}
562
563STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
564{
565 LogFlowThisFunc (("\n"));
566
567 AutoCaller autoCaller (this);
568 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
569
570 AutoReadLock alock (this);
571 AssertReturn (mState == SessionState_Open &&
572 mType == SessionType_Direct, E_FAIL);
573
574 return mConsole->onParallelPortChange(parallelPort);
575}
576
577STDMETHODIMP Session::OnVRDPServerChange()
578{
579 LogFlowThisFunc (("\n"));
580
581 AutoCaller autoCaller (this);
582 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
583
584 AutoReadLock alock (this);
585 AssertReturn (mState == SessionState_Open &&
586 mType == SessionType_Direct, E_FAIL);
587
588 return mConsole->onVRDPServerChange();
589}
590
591STDMETHODIMP Session::OnUSBControllerChange()
592{
593 LogFlowThisFunc (("\n"));
594
595 AutoCaller autoCaller (this);
596 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
597
598 AutoReadLock alock (this);
599 AssertReturn (mState == SessionState_Open &&
600 mType == SessionType_Direct, E_FAIL);
601
602 return mConsole->onUSBControllerChange();
603}
604
605STDMETHODIMP Session::OnSharedFolderChange (BOOL aGlobal)
606{
607 LogFlowThisFunc (("\n"));
608
609 AutoCaller autoCaller (this);
610 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
611
612 AutoReadLock alock (this);
613 AssertReturn (mState == SessionState_Open &&
614 mType == SessionType_Direct, E_FAIL);
615
616 return mConsole->onSharedFolderChange (aGlobal);
617}
618
619STDMETHODIMP Session::OnUSBDeviceAttach (IUSBDevice *aDevice,
620 IVirtualBoxErrorInfo *aError,
621 ULONG aMaskedIfs)
622{
623 LogFlowThisFunc (("\n"));
624
625 AutoCaller autoCaller (this);
626 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
627
628 AutoReadLock alock (this);
629 AssertReturn (mState == SessionState_Open &&
630 mType == SessionType_Direct, E_FAIL);
631
632 return mConsole->onUSBDeviceAttach (aDevice, aError, aMaskedIfs);
633}
634
635STDMETHODIMP Session::OnUSBDeviceDetach (INPTR GUIDPARAM aId,
636 IVirtualBoxErrorInfo *aError)
637{
638 LogFlowThisFunc (("\n"));
639
640 AutoCaller autoCaller (this);
641 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
642
643 AutoReadLock alock (this);
644 AssertReturn (mState == SessionState_Open &&
645 mType == SessionType_Direct, E_FAIL);
646
647 return mConsole->onUSBDeviceDetach (aId, aError);
648}
649
650STDMETHODIMP Session::OnShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
651{
652 AutoCaller autoCaller (this);
653 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
654
655 AutoReadLock alock (this);
656
657 AssertReturn (mType == SessionType_Direct, E_FAIL);
658
659 if (mState != SessionState_Open)
660 {
661 /* the call from Machine issued when the session is open can arrive
662 * after the session starts closing or gets closed. Note that when
663 * aCheck is false, we return E_FAIL to indicate that aWinId we return
664 * is not valid */
665 *aCanShow = FALSE;
666 *aWinId = 0;
667 return aCheck ? S_OK : E_FAIL;
668 }
669
670 return mConsole->onShowWindow (aCheck, aCanShow, aWinId);
671}
672
673STDMETHODIMP Session::AccessGuestProperty (INPTR BSTR aName, INPTR BSTR aValue, INPTR BSTR aFlags,
674 BOOL aIsSetter, BSTR *aRetValue, ULONG64 *aRetTimestamp, BSTR *aRetFlags)
675{
676#ifdef VBOX_WITH_GUEST_PROPS
677 AutoCaller autoCaller (this);
678 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
679
680 if (mState != SessionState_Open)
681 return setError (E_FAIL,
682 tr ("Machine session is not open (session state: %d)."),
683 mState);
684 AssertReturn (mType == SessionType_Direct, E_UNEXPECTED);
685 if (!VALID_PTR (aName))
686 return E_POINTER;
687 if (!aIsSetter && !VALID_PTR (aRetValue))
688 return E_POINTER;
689 if (!aIsSetter && !VALID_PTR (aRetTimestamp))
690 return E_POINTER;
691 if (!aIsSetter && !VALID_PTR (aRetFlags))
692 return E_POINTER;
693 /* aValue can be NULL for a setter call if the property is to be deleted. */
694 if (aIsSetter && (aValue != NULL) && !VALID_PTR (aValue))
695 return E_INVALIDARG;
696 /* aFlags can be null if it is to be left as is */
697 if (aIsSetter && (aFlags != NULL) && !VALID_PTR (aFlags))
698 return E_INVALIDARG;
699 if (!aIsSetter)
700 return mConsole->getGuestProperty (aName, aRetValue, aRetTimestamp, aRetFlags);
701 else
702 return mConsole->setGuestProperty (aName, aValue, aFlags);
703#else /* VBOX_WITH_GUEST_PROPS not defined */
704 return E_NOTIMPL;
705#endif /* VBOX_WITH_GUEST_PROPS not defined */
706}
707
708STDMETHODIMP Session::EnumerateGuestProperties (INPTR BSTR aPatterns,
709 ComSafeArrayOut(BSTR, aNames),
710 ComSafeArrayOut(BSTR, aValues),
711 ComSafeArrayOut(ULONG64, aTimestamps),
712 ComSafeArrayOut(BSTR, aFlags))
713{
714#ifdef VBOX_WITH_GUEST_PROPS
715 AutoCaller autoCaller (this);
716 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
717
718 if (mState != SessionState_Open)
719 return setError (E_FAIL,
720 tr ("Machine session is not open (session state: %d)."),
721 mState);
722 AssertReturn (mType == SessionType_Direct, E_UNEXPECTED);
723 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
724 return E_POINTER;
725 if (ComSafeArrayOutIsNull (aNames))
726 return E_POINTER;
727 if (ComSafeArrayOutIsNull (aValues))
728 return E_POINTER;
729 if (ComSafeArrayOutIsNull (aTimestamps))
730 return E_POINTER;
731 if (ComSafeArrayOutIsNull (aFlags))
732 return E_POINTER;
733 return mConsole->enumerateGuestProperties(aPatterns,
734 ComSafeArrayOutArg(aNames),
735 ComSafeArrayOutArg(aValues),
736 ComSafeArrayOutArg(aTimestamps),
737 ComSafeArrayOutArg(aFlags));
738#else /* VBOX_WITH_GUEST_PROPS not defined */
739 return E_NOTIMPL;
740#endif /* VBOX_WITH_GUEST_PROPS not defined */
741}
742
743// private methods
744///////////////////////////////////////////////////////////////////////////////
745
746/**
747 * Closes the current session.
748 *
749 * @param aFinalRelease called as a result of FinalRelease()
750 * @param aFromServer called as a result of Uninitialize()
751 *
752 * @note To be called only from #uninit(), #Close() or #Uninitialize().
753 * @note Locks this object for writing.
754 */
755HRESULT Session::close (bool aFinalRelease, bool aFromServer)
756{
757 LogFlowThisFuncEnter();
758 LogFlowThisFunc (("aFinalRelease=%d, isFromServer=%d\n",
759 aFinalRelease, aFromServer));
760
761 AutoCaller autoCaller (this);
762 AssertComRCReturnRC (autoCaller.rc());
763
764 AutoWriteLock alock (this);
765
766 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
767
768 if (mState != SessionState_Open)
769 {
770 Assert (mState == SessionState_Spawning);
771
772 /* The session object is going to be uninitialized before it has been
773 * assigned a direct console of the machine the client requested to open
774 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
775 * only if this close reqiest comes from the server (for example, it
776 * detected that the VM process it started terminated before opening a
777 * direct session). Otherwise, it means that the client is too fast and
778 * trying to close the session before waiting for the progress object it
779 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
780 Assert (aFromServer);
781
782 mState = SessionState_Closed;
783 mType = SessionType_Null;
784#if defined(RT_OS_WINDOWS)
785 Assert (!mIPCSem && !mIPCThreadSem);
786#elif defined(RT_OS_OS2)
787 Assert (mIPCThread == NIL_RTTHREAD &&
788 mIPCThreadSem == NIL_RTSEMEVENT);
789#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
790 Assert (mIPCSem == -1);
791#else
792# error "Port me!"
793#endif
794 LogFlowThisFuncLeave();
795 return S_OK;
796 }
797
798 /* go to the closing state */
799 mState = SessionState_Closing;
800
801 if (mType == SessionType_Direct)
802 {
803 mConsole->uninit();
804 mConsole.setNull();
805 }
806 else
807 {
808 mRemoteMachine.setNull();
809 mRemoteConsole.setNull();
810 }
811
812 ComPtr <IProgress> progress;
813
814 if (!aFinalRelease && !aFromServer)
815 {
816 /*
817 * We trigger OnSessionEnd() only when the session closes itself using
818 * Close(). Note that if isFinalRelease = TRUE here, this means that
819 * the client process has already initialized the termination procedure
820 * without issuing Close() and the IPC channel is no more operational --
821 * so we cannot call the server's method (it will definitely fail). The
822 * server will instead simply detect the abnormal client death (since
823 * OnSessionEnd() is not called) and reset the machine state to Aborted.
824 */
825
826 /*
827 * while waiting for OnSessionEnd() to complete one of our methods
828 * can be called by the server (for example, Uninitialize(), if the
829 * direct session has initiated a closure just a bit before us) so
830 * we need to release the lock to avoid deadlocks. The state is already
831 * SessionState_Closing here, so it's safe.
832 */
833 alock.leave();
834
835 LogFlowThisFunc (("Calling mControl->OnSessionEnd()...\n"));
836 HRESULT rc = mControl->OnSessionEnd (this, progress.asOutParam());
837 LogFlowThisFunc (("mControl->OnSessionEnd()=%08X\n", rc));
838
839 alock.enter();
840
841 /*
842 * If we get E_UNEXPECTED this means that the direct session has already
843 * been closed, we're just too late with our notification and nothing more
844 */
845 if (mType != SessionType_Direct && rc == E_UNEXPECTED)
846 rc = S_OK;
847
848 AssertComRC (rc);
849 }
850
851 mControl.setNull();
852
853 if (mType == SessionType_Direct)
854 {
855 releaseIPCSemaphore();
856 if (!aFinalRelease && !aFromServer)
857 {
858 /*
859 * Wait for the server to grab the semaphore and destroy the session
860 * machine (allowing us to open a new session with the same machine
861 * once this method returns)
862 */
863 Assert (!!progress);
864 if (progress)
865 progress->WaitForCompletion (-1);
866 }
867 }
868
869 mState = SessionState_Closed;
870 mType = SessionType_Null;
871
872 /* release the VirtualBox instance as the very last step */
873 mVirtualBox.setNull();
874
875 LogFlowThisFuncLeave();
876 return S_OK;
877}
878
879/** @note To be called only from #AssignMachine() */
880HRESULT Session::grabIPCSemaphore()
881{
882 HRESULT rc = E_FAIL;
883
884 /* open the IPC semaphore based on the sessionId and try to grab it */
885 Bstr ipcId;
886 rc = mControl->GetIPCId (ipcId.asOutParam());
887 AssertComRCReturnRC (rc);
888
889 LogFlowThisFunc (("ipcId='%ls'\n", ipcId.raw()));
890
891#if defined(RT_OS_WINDOWS)
892
893 /*
894 * Since Session is an MTA object, this method can be executed on
895 * any thread, and this thread will not necessarily match the thread on
896 * which close() will be called later. Therefore, we need a separate
897 * thread to hold the IPC mutex and then release it in close().
898 */
899
900 mIPCThreadSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
901 AssertMsgReturn (mIPCThreadSem,
902 ("Cannot create an event sem, err=%d", ::GetLastError()),
903 E_FAIL);
904
905 void *data [3];
906 data [0] = (void *) (BSTR) ipcId;
907 data [1] = (void *) mIPCThreadSem;
908 data [2] = 0; /* will get an output from the thread */
909
910 /* create a thread to hold the IPC mutex until signalled to release it */
911 RTTHREAD tid;
912 int vrc = RTThreadCreate (&tid, IPCMutexHolderThread, (void *) data,
913 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
914 AssertRCReturn (vrc, E_FAIL);
915
916 /* wait until thread init is completed */
917 DWORD wrc = ::WaitForSingleObject (mIPCThreadSem, INFINITE);
918 AssertMsg (wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
919 Assert (data [2]);
920
921 if (wrc == WAIT_OBJECT_0 && data [2])
922 {
923 /* memorize the event sem we should signal in close() */
924 mIPCSem = (HANDLE) data [2];
925 rc = S_OK;
926 }
927 else
928 {
929 ::CloseHandle (mIPCThreadSem);
930 mIPCThreadSem = NULL;
931 rc = E_FAIL;
932 }
933
934#elif defined(RT_OS_OS2)
935
936 /* We use XPCOM where any message (including close()) can arrive on any
937 * worker thread (which will not necessarily match this thread that opens
938 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
939 * and then release it in close(). */
940
941 int vrc = RTSemEventCreate (&mIPCThreadSem);
942 AssertRCReturn (vrc, E_FAIL);
943
944 void *data [3];
945 data [0] = (void *) ipcId.raw();
946 data [1] = (void *) mIPCThreadSem;
947 data [2] = (void *) false; /* will get the thread result here */
948
949 /* create a thread to hold the IPC mutex until signalled to release it */
950 vrc = RTThreadCreate (&mIPCThread, IPCMutexHolderThread, (void *) data,
951 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
952 AssertRCReturn (vrc, E_FAIL);
953
954 /* wait until thread init is completed */
955 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
956 AssertReturn (VBOX_SUCCESS (vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
957
958 /* the thread must succeed */
959 AssertReturn ((bool) data [2], E_FAIL);
960
961#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
962
963 Utf8Str semName = ipcId;
964 char *pszSemName = NULL;
965 RTStrUtf8ToCurrentCP (&pszSemName, semName);
966 key_t key = ::ftok (pszSemName, 0);
967 RTStrFree (pszSemName);
968
969 mIPCSem = ::semget (key, 0, 0);
970 AssertMsgReturn (mIPCSem >= 0,
971 ("Cannot open IPC semaphore, errno=%d", errno),
972 E_FAIL);
973
974 /* grab the semaphore */
975 ::sembuf sop = { 0, -1, SEM_UNDO };
976 int rv = ::semop (mIPCSem, &sop, 1);
977 AssertMsgReturn (rv == 0,
978 ("Cannot grab IPC semaphore, errno=%d", errno),
979 E_FAIL);
980
981#else
982# error "Port me!"
983#endif
984
985 return rc;
986}
987
988/** @note To be called only from #close() */
989void Session::releaseIPCSemaphore()
990{
991 /* release the IPC semaphore */
992#if defined(RT_OS_WINDOWS)
993
994 if (mIPCSem && mIPCThreadSem)
995 {
996 /*
997 * tell the thread holding the IPC mutex to release it;
998 * it will close mIPCSem handle
999 */
1000 ::SetEvent (mIPCSem);
1001 /* wait for the thread to finish */
1002 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1003 ::CloseHandle (mIPCThreadSem);
1004 }
1005
1006#elif defined(RT_OS_OS2)
1007
1008 if (mIPCThread != NIL_RTTHREAD)
1009 {
1010 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1011
1012 /* tell the thread holding the IPC mutex to release it */
1013 int vrc = RTSemEventSignal (mIPCThreadSem);
1014 AssertRC (vrc == NO_ERROR);
1015
1016 /* wait for the thread to finish */
1017 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1018 Assert (VBOX_SUCCESS (vrc) || vrc == VERR_INTERRUPTED);
1019
1020 mIPCThread = NIL_RTTHREAD;
1021 }
1022
1023 if (mIPCThreadSem != NIL_RTSEMEVENT)
1024 {
1025 RTSemEventDestroy (mIPCThreadSem);
1026 mIPCThreadSem = NIL_RTSEMEVENT;
1027 }
1028
1029#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1030
1031 if (mIPCSem >= 0)
1032 {
1033 ::sembuf sop = { 0, 1, SEM_UNDO };
1034 ::semop (mIPCSem, &sop, 1);
1035 }
1036
1037#else
1038# error "Port me!"
1039#endif
1040}
1041
1042#if defined(RT_OS_WINDOWS)
1043/** VM IPC mutex holder thread */
1044DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1045{
1046 LogFlowFuncEnter();
1047
1048 Assert (pvUser);
1049 void **data = (void **) pvUser;
1050
1051 BSTR sessionId = (BSTR) data [0];
1052 HANDLE initDoneSem = (HANDLE) data [1];
1053
1054 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1055 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1056
1057 if (ipcMutex)
1058 {
1059 /* grab the mutex */
1060 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1061 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1062 if (wrc == WAIT_OBJECT_0)
1063 {
1064 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1065 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1066 if (finishSem)
1067 {
1068 data [2] = (void *) finishSem;
1069 /* signal we're done with init */
1070 ::SetEvent (initDoneSem);
1071 /* wait until we're signaled to release the IPC mutex */
1072 ::WaitForSingleObject (finishSem, INFINITE);
1073 /* release the IPC mutex */
1074 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1075 BOOL success = ::ReleaseMutex (ipcMutex);
1076 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1077 ::CloseHandle (ipcMutex);
1078 ::CloseHandle (finishSem);
1079 }
1080 }
1081 }
1082
1083 /* signal we're done */
1084 ::SetEvent (initDoneSem);
1085
1086 LogFlowFuncLeave();
1087
1088 return 0;
1089}
1090#endif
1091
1092#if defined(RT_OS_OS2)
1093/** VM IPC mutex holder thread */
1094DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1095{
1096 LogFlowFuncEnter();
1097
1098 Assert (pvUser);
1099 void **data = (void **) pvUser;
1100
1101 Utf8Str ipcId = (BSTR) data [0];
1102 RTSEMEVENT finishSem = (RTSEMEVENT) data [1];
1103
1104 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1105
1106 HMTX ipcMutex = NULLHANDLE;
1107 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1108 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1109
1110 if (arc == NO_ERROR)
1111 {
1112 /* grab the mutex */
1113 LogFlowFunc (("grabbing IPC mutex...\n"));
1114 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1115 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1116 if (arc == NO_ERROR)
1117 {
1118 /* store the answer */
1119 data [2] = (void *) true;
1120 /* signal we're done */
1121 int vrc = RTThreadUserSignal (Thread);
1122 AssertRC (vrc);
1123
1124 /* wait until we're signaled to release the IPC mutex */
1125 LogFlowFunc (("waiting for termination signal..\n"));
1126 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1127 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1128
1129 /* release the IPC mutex */
1130 LogFlowFunc (("releasing IPC mutex...\n"));
1131 arc = ::DosReleaseMutexSem (ipcMutex);
1132 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1133 }
1134
1135 ::DosCloseMutexSem (ipcMutex);
1136 }
1137
1138 /* store the answer */
1139 data [1] = (void *) false;
1140 /* signal we're done */
1141 int vrc = RTThreadUserSignal (Thread);
1142 AssertRC (vrc);
1143
1144 LogFlowFuncLeave();
1145
1146 return 0;
1147}
1148#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use