VirtualBox

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

Last change on this file since 22358 was 22305, checked in by vboxsync, 15 years ago

Main: generic callback wrappers mechanism

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