VirtualBox

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

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

Main: fix Darwin burn

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