VirtualBox

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

Last change on this file since 19844 was 19239, checked in by vboxsync, 16 years ago

Main: support for using VBox from Python on Windows (still certain limitation apply, such as enum visibility)

  • 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#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::OnStorageControllerChange()
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->onStorageControllerChange();
585}
586
587STDMETHODIMP Session::OnVRDPServerChange()
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->onVRDPServerChange();
599}
600
601STDMETHODIMP Session::OnUSBControllerChange()
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->onUSBControllerChange();
613}
614
615STDMETHODIMP Session::OnSharedFolderChange (BOOL aGlobal)
616{
617 LogFlowThisFunc (("\n"));
618
619 AutoCaller autoCaller (this);
620 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
621
622 AutoReadLock alock (this);
623 AssertReturn (mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
624 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
625
626 return mConsole->onSharedFolderChange (aGlobal);
627}
628
629STDMETHODIMP Session::OnUSBDeviceAttach (IUSBDevice *aDevice,
630 IVirtualBoxErrorInfo *aError,
631 ULONG aMaskedIfs)
632{
633 LogFlowThisFunc (("\n"));
634
635 AutoCaller autoCaller (this);
636 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
637
638 AutoReadLock alock (this);
639 AssertReturn (mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
640 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
641
642 return mConsole->onUSBDeviceAttach (aDevice, aError, aMaskedIfs);
643}
644
645STDMETHODIMP Session::OnUSBDeviceDetach (IN_BSTR aId,
646 IVirtualBoxErrorInfo *aError)
647{
648 LogFlowThisFunc (("\n"));
649
650 AutoCaller autoCaller (this);
651 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
652
653 AutoReadLock alock (this);
654 AssertReturn (mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
655 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
656
657 return mConsole->onUSBDeviceDetach (aId, aError);
658}
659
660STDMETHODIMP Session::OnShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
661{
662 AutoCaller autoCaller (this);
663 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
664
665 AutoReadLock alock (this);
666
667 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
668
669 if (mState != SessionState_Open)
670 {
671 /* the call from Machine issued when the session is open can arrive
672 * after the session starts closing or gets closed. Note that when
673 * aCheck is false, we return E_FAIL to indicate that aWinId we return
674 * is not valid */
675 *aCanShow = FALSE;
676 *aWinId = 0;
677 return aCheck ? S_OK : E_FAIL;
678 }
679
680 return mConsole->onShowWindow (aCheck, aCanShow, aWinId);
681}
682
683STDMETHODIMP Session::AccessGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
684 BOOL aIsSetter, BSTR *aRetValue, ULONG64 *aRetTimestamp, BSTR *aRetFlags)
685{
686#ifdef VBOX_WITH_GUEST_PROPS
687 AutoCaller autoCaller (this);
688 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
689
690 if (mState != SessionState_Open)
691 return setError (VBOX_E_INVALID_VM_STATE,
692 tr ("Machine session is not open (session state: %d)."),
693 mState);
694 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
695 CheckComArgNotNull(aName);
696 if (!aIsSetter && !VALID_PTR (aRetValue))
697 return E_POINTER;
698 if (!aIsSetter && !VALID_PTR (aRetTimestamp))
699 return E_POINTER;
700 if (!aIsSetter && !VALID_PTR (aRetFlags))
701 return E_POINTER;
702 /* aValue can be NULL for a setter call if the property is to be deleted. */
703 if (aIsSetter && (aValue != NULL) && !VALID_PTR (aValue))
704 return E_INVALIDARG;
705 /* aFlags can be null if it is to be left as is */
706 if (aIsSetter && (aFlags != NULL) && !VALID_PTR (aFlags))
707 return E_INVALIDARG;
708 if (!aIsSetter)
709 return mConsole->getGuestProperty (aName, aRetValue, aRetTimestamp, aRetFlags);
710 else
711 return mConsole->setGuestProperty (aName, aValue, aFlags);
712#else /* VBOX_WITH_GUEST_PROPS not defined */
713 ReturnComNotImplemented();
714#endif /* VBOX_WITH_GUEST_PROPS not defined */
715}
716
717STDMETHODIMP Session::EnumerateGuestProperties (IN_BSTR aPatterns,
718 ComSafeArrayOut(BSTR, aNames),
719 ComSafeArrayOut(BSTR, aValues),
720 ComSafeArrayOut(ULONG64, aTimestamps),
721 ComSafeArrayOut(BSTR, aFlags))
722{
723#ifdef VBOX_WITH_GUEST_PROPS
724 AutoCaller autoCaller (this);
725 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
726
727 if (mState != SessionState_Open)
728 return setError (VBOX_E_INVALID_VM_STATE,
729 tr ("Machine session is not open (session state: %d)."),
730 mState);
731 AssertReturn (mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
732 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
733 return E_POINTER;
734 if (ComSafeArrayOutIsNull (aNames))
735 return E_POINTER;
736 if (ComSafeArrayOutIsNull (aValues))
737 return E_POINTER;
738 if (ComSafeArrayOutIsNull (aTimestamps))
739 return E_POINTER;
740 if (ComSafeArrayOutIsNull (aFlags))
741 return E_POINTER;
742 return mConsole->enumerateGuestProperties(aPatterns,
743 ComSafeArrayOutArg(aNames),
744 ComSafeArrayOutArg(aValues),
745 ComSafeArrayOutArg(aTimestamps),
746 ComSafeArrayOutArg(aFlags));
747#else /* VBOX_WITH_GUEST_PROPS not defined */
748 ReturnComNotImplemented();
749#endif /* VBOX_WITH_GUEST_PROPS not defined */
750}
751
752// private methods
753///////////////////////////////////////////////////////////////////////////////
754
755/**
756 * Closes the current session.
757 *
758 * @param aFinalRelease called as a result of FinalRelease()
759 * @param aFromServer called as a result of Uninitialize()
760 *
761 * @note To be called only from #uninit(), #Close() or #Uninitialize().
762 * @note Locks this object for writing.
763 */
764HRESULT Session::close (bool aFinalRelease, bool aFromServer)
765{
766 LogFlowThisFuncEnter();
767 LogFlowThisFunc (("aFinalRelease=%d, isFromServer=%d\n",
768 aFinalRelease, aFromServer));
769
770 AutoCaller autoCaller (this);
771 AssertComRCReturnRC (autoCaller.rc());
772
773 AutoWriteLock alock (this);
774
775 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
776
777 if (mState != SessionState_Open)
778 {
779 Assert (mState == SessionState_Spawning);
780
781 /* The session object is going to be uninitialized before it has been
782 * assigned a direct console of the machine the client requested to open
783 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
784 * only if this close reqiest comes from the server (for example, it
785 * detected that the VM process it started terminated before opening a
786 * direct session). Otherwise, it means that the client is too fast and
787 * trying to close the session before waiting for the progress object it
788 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
789 Assert (aFromServer);
790
791 mState = SessionState_Closed;
792 mType = SessionType_Null;
793#if defined(RT_OS_WINDOWS)
794 Assert (!mIPCSem && !mIPCThreadSem);
795#elif defined(RT_OS_OS2)
796 Assert (mIPCThread == NIL_RTTHREAD &&
797 mIPCThreadSem == NIL_RTSEMEVENT);
798#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
799 Assert (mIPCSem == -1);
800#else
801# error "Port me!"
802#endif
803 LogFlowThisFuncLeave();
804 return S_OK;
805 }
806
807 /* go to the closing state */
808 mState = SessionState_Closing;
809
810 if (mType == SessionType_Direct)
811 {
812 mConsole->uninit();
813 mConsole.setNull();
814 }
815 else
816 {
817 mRemoteMachine.setNull();
818 mRemoteConsole.setNull();
819 }
820
821 ComPtr <IProgress> progress;
822
823 if (!aFinalRelease && !aFromServer)
824 {
825 /*
826 * We trigger OnSessionEnd() only when the session closes itself using
827 * Close(). Note that if isFinalRelease = TRUE here, this means that
828 * the client process has already initialized the termination procedure
829 * without issuing Close() and the IPC channel is no more operational --
830 * so we cannot call the server's method (it will definitely fail). The
831 * server will instead simply detect the abnormal client death (since
832 * OnSessionEnd() is not called) and reset the machine state to Aborted.
833 */
834
835 /*
836 * while waiting for OnSessionEnd() to complete one of our methods
837 * can be called by the server (for example, Uninitialize(), if the
838 * direct session has initiated a closure just a bit before us) so
839 * we need to release the lock to avoid deadlocks. The state is already
840 * SessionState_Closing here, so it's safe.
841 */
842 alock.leave();
843
844 LogFlowThisFunc (("Calling mControl->OnSessionEnd()...\n"));
845 HRESULT rc = mControl->OnSessionEnd (this, progress.asOutParam());
846 LogFlowThisFunc (("mControl->OnSessionEnd()=%08X\n", rc));
847
848 alock.enter();
849
850 /*
851 * If we get E_UNEXPECTED this means that the direct session has already
852 * been closed, we're just too late with our notification and nothing more
853 */
854 if (mType != SessionType_Direct && rc == E_UNEXPECTED)
855 rc = S_OK;
856
857 AssertComRC (rc);
858 }
859
860 mControl.setNull();
861
862 if (mType == SessionType_Direct)
863 {
864 releaseIPCSemaphore();
865 if (!aFinalRelease && !aFromServer)
866 {
867 /*
868 * Wait for the server to grab the semaphore and destroy the session
869 * machine (allowing us to open a new session with the same machine
870 * once this method returns)
871 */
872 Assert (!!progress);
873 if (progress)
874 progress->WaitForCompletion (-1);
875 }
876 }
877
878 mState = SessionState_Closed;
879 mType = SessionType_Null;
880
881 /* release the VirtualBox instance as the very last step */
882 mVirtualBox.setNull();
883
884 LogFlowThisFuncLeave();
885 return S_OK;
886}
887
888/** @note To be called only from #AssignMachine() */
889HRESULT Session::grabIPCSemaphore()
890{
891 HRESULT rc = E_FAIL;
892
893 /* open the IPC semaphore based on the sessionId and try to grab it */
894 Bstr ipcId;
895 rc = mControl->GetIPCId (ipcId.asOutParam());
896 AssertComRCReturnRC (rc);
897
898 LogFlowThisFunc (("ipcId='%ls'\n", ipcId.raw()));
899
900#if defined(RT_OS_WINDOWS)
901
902 /*
903 * Since Session is an MTA object, this method can be executed on
904 * any thread, and this thread will not necessarily match the thread on
905 * which close() will be called later. Therefore, we need a separate
906 * thread to hold the IPC mutex and then release it in close().
907 */
908
909 mIPCThreadSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
910 AssertMsgReturn (mIPCThreadSem,
911 ("Cannot create an event sem, err=%d", ::GetLastError()),
912 E_FAIL);
913
914 void *data [3];
915 data [0] = (void *) (BSTR) ipcId;
916 data [1] = (void *) mIPCThreadSem;
917 data [2] = 0; /* will get an output from the thread */
918
919 /* create a thread to hold the IPC mutex until signalled to release it */
920 RTTHREAD tid;
921 int vrc = RTThreadCreate (&tid, IPCMutexHolderThread, (void *) data,
922 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
923 AssertRCReturn (vrc, E_FAIL);
924
925 /* wait until thread init is completed */
926 DWORD wrc = ::WaitForSingleObject (mIPCThreadSem, INFINITE);
927 AssertMsg (wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
928 Assert (data [2]);
929
930 if (wrc == WAIT_OBJECT_0 && data [2])
931 {
932 /* memorize the event sem we should signal in close() */
933 mIPCSem = (HANDLE) data [2];
934 rc = S_OK;
935 }
936 else
937 {
938 ::CloseHandle (mIPCThreadSem);
939 mIPCThreadSem = NULL;
940 rc = E_FAIL;
941 }
942
943#elif defined(RT_OS_OS2)
944
945 /* We use XPCOM where any message (including close()) can arrive on any
946 * worker thread (which will not necessarily match this thread that opens
947 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
948 * and then release it in close(). */
949
950 int vrc = RTSemEventCreate (&mIPCThreadSem);
951 AssertRCReturn (vrc, E_FAIL);
952
953 void *data [3];
954 data [0] = (void *) ipcId.raw();
955 data [1] = (void *) mIPCThreadSem;
956 data [2] = (void *) false; /* will get the thread result here */
957
958 /* create a thread to hold the IPC mutex until signalled to release it */
959 vrc = RTThreadCreate (&mIPCThread, IPCMutexHolderThread, (void *) data,
960 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
961 AssertRCReturn (vrc, E_FAIL);
962
963 /* wait until thread init is completed */
964 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
965 AssertReturn (RT_SUCCESS (vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
966
967 /* the thread must succeed */
968 AssertReturn ((bool) data [2], E_FAIL);
969
970#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
971
972# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
973 Utf8Str ipcKey = ipcId;
974 key_t key = RTStrToUInt32(ipcKey.raw());
975 AssertMsgReturn (key != 0,
976 ("Key value of 0 is not valid for IPC semaphore"),
977 E_FAIL);
978# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
979 Utf8Str semName = ipcId;
980 char *pszSemName = NULL;
981 RTStrUtf8ToCurrentCP (&pszSemName, semName);
982 key_t key = ::ftok (pszSemName, 'V');
983 RTStrFree (pszSemName);
984# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
985
986 mIPCSem = ::semget (key, 0, 0);
987 AssertMsgReturn (mIPCSem >= 0,
988 ("Cannot open IPC semaphore, errno=%d", errno),
989 E_FAIL);
990
991 /* grab the semaphore */
992 ::sembuf sop = { 0, -1, SEM_UNDO };
993 int rv = ::semop (mIPCSem, &sop, 1);
994 AssertMsgReturn (rv == 0,
995 ("Cannot grab IPC semaphore, errno=%d", errno),
996 E_FAIL);
997
998#else
999# error "Port me!"
1000#endif
1001
1002 return rc;
1003}
1004
1005/** @note To be called only from #close() */
1006void Session::releaseIPCSemaphore()
1007{
1008 /* release the IPC semaphore */
1009#if defined(RT_OS_WINDOWS)
1010
1011 if (mIPCSem && mIPCThreadSem)
1012 {
1013 /*
1014 * tell the thread holding the IPC mutex to release it;
1015 * it will close mIPCSem handle
1016 */
1017 ::SetEvent (mIPCSem);
1018 /* wait for the thread to finish */
1019 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1020 ::CloseHandle (mIPCThreadSem);
1021
1022 mIPCThreadSem = NULL;
1023 mIPCSem = NULL;
1024 }
1025
1026#elif defined(RT_OS_OS2)
1027
1028 if (mIPCThread != NIL_RTTHREAD)
1029 {
1030 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1031
1032 /* tell the thread holding the IPC mutex to release it */
1033 int vrc = RTSemEventSignal (mIPCThreadSem);
1034 AssertRC (vrc == NO_ERROR);
1035
1036 /* wait for the thread to finish */
1037 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1038 Assert (RT_SUCCESS (vrc) || vrc == VERR_INTERRUPTED);
1039
1040 mIPCThread = NIL_RTTHREAD;
1041 }
1042
1043 if (mIPCThreadSem != NIL_RTSEMEVENT)
1044 {
1045 RTSemEventDestroy (mIPCThreadSem);
1046 mIPCThreadSem = NIL_RTSEMEVENT;
1047 }
1048
1049#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1050
1051 if (mIPCSem >= 0)
1052 {
1053 ::sembuf sop = { 0, 1, SEM_UNDO };
1054 ::semop (mIPCSem, &sop, 1);
1055
1056 mIPCSem = -1;
1057 }
1058
1059#else
1060# error "Port me!"
1061#endif
1062}
1063
1064#if defined(RT_OS_WINDOWS)
1065/** VM IPC mutex holder thread */
1066DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1067{
1068 LogFlowFuncEnter();
1069
1070 Assert (pvUser);
1071 void **data = (void **) pvUser;
1072
1073 BSTR sessionId = (BSTR) data [0];
1074 HANDLE initDoneSem = (HANDLE) data [1];
1075
1076 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1077 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1078
1079 if (ipcMutex)
1080 {
1081 /* grab the mutex */
1082 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1083 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1084 if (wrc == WAIT_OBJECT_0)
1085 {
1086 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1087 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1088 if (finishSem)
1089 {
1090 data [2] = (void *) finishSem;
1091 /* signal we're done with init */
1092 ::SetEvent (initDoneSem);
1093 /* wait until we're signaled to release the IPC mutex */
1094 ::WaitForSingleObject (finishSem, INFINITE);
1095 /* release the IPC mutex */
1096 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1097 BOOL success = ::ReleaseMutex (ipcMutex);
1098 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1099 ::CloseHandle (ipcMutex);
1100 ::CloseHandle (finishSem);
1101 }
1102 }
1103 }
1104
1105 /* signal we're done */
1106 ::SetEvent (initDoneSem);
1107
1108 LogFlowFuncLeave();
1109
1110 return 0;
1111}
1112#endif
1113
1114#if defined(RT_OS_OS2)
1115/** VM IPC mutex holder thread */
1116DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1117{
1118 LogFlowFuncEnter();
1119
1120 Assert (pvUser);
1121 void **data = (void **) pvUser;
1122
1123 Utf8Str ipcId = (BSTR) data [0];
1124 RTSEMEVENT finishSem = (RTSEMEVENT) data [1];
1125
1126 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1127
1128 HMTX ipcMutex = NULLHANDLE;
1129 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1130 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1131
1132 if (arc == NO_ERROR)
1133 {
1134 /* grab the mutex */
1135 LogFlowFunc (("grabbing IPC mutex...\n"));
1136 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1137 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1138 if (arc == NO_ERROR)
1139 {
1140 /* store the answer */
1141 data [2] = (void *) true;
1142 /* signal we're done */
1143 int vrc = RTThreadUserSignal (Thread);
1144 AssertRC (vrc);
1145
1146 /* wait until we're signaled to release the IPC mutex */
1147 LogFlowFunc (("waiting for termination signal..\n"));
1148 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1149 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1150
1151 /* release the IPC mutex */
1152 LogFlowFunc (("releasing IPC mutex...\n"));
1153 arc = ::DosReleaseMutexSem (ipcMutex);
1154 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1155 }
1156
1157 ::DosCloseMutexSem (ipcMutex);
1158 }
1159
1160 /* store the answer */
1161 data [1] = (void *) false;
1162 /* signal we're done */
1163 int vrc = RTThreadUserSignal (Thread);
1164 AssertRC (vrc);
1165
1166 LogFlowFuncLeave();
1167
1168 return 0;
1169}
1170#endif
1171/* 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