VirtualBox

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

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

#3285: Improve error handling API to include unique error numbers
Document IMachine interface, part II.

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