VirtualBox

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

Last change on this file since 17539 was 17180, checked in by vboxsync, 16 years ago

Main/Unix: redo SYSV semaphore key generation. The old solution was prone to collisions, which led to mysterious hangs at VM poweroff for the second VM which accidentally shared the same semaphore. Additionally tightened access rights.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.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, VBOX_E_INVALID_VM_STATE);
269
270 AssertMsgReturn (mType == SessionType_Direct && !!mConsole,
271 ("This is not a direct session!\n"), VBOX_E_INVALID_OBJECT_STATE);
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 VBOX_E_INVALID_VM_STATE;
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, VBOX_E_INVALID_VM_STATE);
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, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
366
367 HRESULT rc = E_FAIL;
368
369 /* query IInternalMachineControl interface */
370 mControl = aMachine;
371 AssertReturn (!!mControl, E_FAIL); // This test appears to be redundant --JS
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, VBOX_E_INVALID_VM_STATE);
448 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
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, VBOX_E_INVALID_VM_STATE);
512 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
526 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
540 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
554 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
568 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
582 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
596 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
610 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
626 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_VM_STATE);
641 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_OBJECT_STATE);
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, VBOX_E_INVALID_OBJECT_STATE);
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 (VBOX_E_INVALID_VM_STATE,
715 tr ("Machine session is not open (session state: %d)."),
716 mState);
717 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
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# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
959 Utf8Str ipcKey = ipcId;
960 key_t key = RTStrToUInt32(ipcKey.raw());
961 AssertMsgReturn (key != 0,
962 ("Key value of 0 is not valid for IPC semaphore"),
963 E_FAIL);
964# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
965 Utf8Str semName = ipcId;
966 char *pszSemName = NULL;
967 RTStrUtf8ToCurrentCP (&pszSemName, semName);
968 key_t key = ::ftok (pszSemName, 'V');
969 RTStrFree (pszSemName);
970# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
971
972 mIPCSem = ::semget (key, 0, 0);
973 AssertMsgReturn (mIPCSem >= 0,
974 ("Cannot open IPC semaphore, errno=%d", errno),
975 E_FAIL);
976
977 /* grab the semaphore */
978 ::sembuf sop = { 0, -1, SEM_UNDO };
979 int rv = ::semop (mIPCSem, &sop, 1);
980 AssertMsgReturn (rv == 0,
981 ("Cannot grab IPC semaphore, errno=%d", errno),
982 E_FAIL);
983
984#else
985# error "Port me!"
986#endif
987
988 return rc;
989}
990
991/** @note To be called only from #close() */
992void Session::releaseIPCSemaphore()
993{
994 /* release the IPC semaphore */
995#if defined(RT_OS_WINDOWS)
996
997 if (mIPCSem && mIPCThreadSem)
998 {
999 /*
1000 * tell the thread holding the IPC mutex to release it;
1001 * it will close mIPCSem handle
1002 */
1003 ::SetEvent (mIPCSem);
1004 /* wait for the thread to finish */
1005 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1006 ::CloseHandle (mIPCThreadSem);
1007 }
1008
1009#elif defined(RT_OS_OS2)
1010
1011 if (mIPCThread != NIL_RTTHREAD)
1012 {
1013 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1014
1015 /* tell the thread holding the IPC mutex to release it */
1016 int vrc = RTSemEventSignal (mIPCThreadSem);
1017 AssertRC (vrc == NO_ERROR);
1018
1019 /* wait for the thread to finish */
1020 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1021 Assert (RT_SUCCESS (vrc) || vrc == VERR_INTERRUPTED);
1022
1023 mIPCThread = NIL_RTTHREAD;
1024 }
1025
1026 if (mIPCThreadSem != NIL_RTSEMEVENT)
1027 {
1028 RTSemEventDestroy (mIPCThreadSem);
1029 mIPCThreadSem = NIL_RTSEMEVENT;
1030 }
1031
1032#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1033
1034 if (mIPCSem >= 0)
1035 {
1036 ::sembuf sop = { 0, 1, SEM_UNDO };
1037 ::semop (mIPCSem, &sop, 1);
1038 }
1039
1040#else
1041# error "Port me!"
1042#endif
1043}
1044
1045#if defined(RT_OS_WINDOWS)
1046/** VM IPC mutex holder thread */
1047DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1048{
1049 LogFlowFuncEnter();
1050
1051 Assert (pvUser);
1052 void **data = (void **) pvUser;
1053
1054 BSTR sessionId = (BSTR) data [0];
1055 HANDLE initDoneSem = (HANDLE) data [1];
1056
1057 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1058 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1059
1060 if (ipcMutex)
1061 {
1062 /* grab the mutex */
1063 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1064 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1065 if (wrc == WAIT_OBJECT_0)
1066 {
1067 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1068 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1069 if (finishSem)
1070 {
1071 data [2] = (void *) finishSem;
1072 /* signal we're done with init */
1073 ::SetEvent (initDoneSem);
1074 /* wait until we're signaled to release the IPC mutex */
1075 ::WaitForSingleObject (finishSem, INFINITE);
1076 /* release the IPC mutex */
1077 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1078 BOOL success = ::ReleaseMutex (ipcMutex);
1079 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1080 ::CloseHandle (ipcMutex);
1081 ::CloseHandle (finishSem);
1082 }
1083 }
1084 }
1085
1086 /* signal we're done */
1087 ::SetEvent (initDoneSem);
1088
1089 LogFlowFuncLeave();
1090
1091 return 0;
1092}
1093#endif
1094
1095#if defined(RT_OS_OS2)
1096/** VM IPC mutex holder thread */
1097DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1098{
1099 LogFlowFuncEnter();
1100
1101 Assert (pvUser);
1102 void **data = (void **) pvUser;
1103
1104 Utf8Str ipcId = (BSTR) data [0];
1105 RTSEMEVENT finishSem = (RTSEMEVENT) data [1];
1106
1107 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1108
1109 HMTX ipcMutex = NULLHANDLE;
1110 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1111 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1112
1113 if (arc == NO_ERROR)
1114 {
1115 /* grab the mutex */
1116 LogFlowFunc (("grabbing IPC mutex...\n"));
1117 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1118 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1119 if (arc == NO_ERROR)
1120 {
1121 /* store the answer */
1122 data [2] = (void *) true;
1123 /* signal we're done */
1124 int vrc = RTThreadUserSignal (Thread);
1125 AssertRC (vrc);
1126
1127 /* wait until we're signaled to release the IPC mutex */
1128 LogFlowFunc (("waiting for termination signal..\n"));
1129 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1130 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1131
1132 /* release the IPC mutex */
1133 LogFlowFunc (("releasing IPC mutex...\n"));
1134 arc = ::DosReleaseMutexSem (ipcMutex);
1135 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1136 }
1137
1138 ::DosCloseMutexSem (ipcMutex);
1139 }
1140
1141 /* store the answer */
1142 data [1] = (void *) false;
1143 /* signal we're done */
1144 int vrc = RTThreadUserSignal (Thread);
1145 AssertRC (vrc);
1146
1147 LogFlowFuncLeave();
1148
1149 return 0;
1150}
1151#endif
1152/* 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