VirtualBox

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

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

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

  • 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 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 ReturnComNotImplemented();
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 ReturnComNotImplemented();
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 (RT_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 (RT_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
1149/* 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