VirtualBox

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

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

NetworkAttachment: trigger the network attachment change only when AttachTo* API are called

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 KB
Line 
1/** @file
2 *
3 * VBox Client Session COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#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 LogFlowThisFuncEnter();
262 AssertReturn(aConsole, E_POINTER);
263
264 AutoCaller autoCaller(this);
265 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
266
267 AutoReadLock alock(this);
268
269 AssertReturn(mState != SessionState_Closed, VBOX_E_INVALID_VM_STATE);
270
271 AssertMsgReturn (mType == SessionType_Direct && !!mConsole,
272 ("This is not a direct session!\n"), VBOX_E_INVALID_OBJECT_STATE);
273
274 /* return a failure if the session already transitioned to Closing
275 * but the server hasn't processed Machine::OnSessionEnd() yet. */
276 if (mState != SessionState_Open)
277 return VBOX_E_INVALID_VM_STATE;
278
279 mConsole.queryInterfaceTo(aConsole);
280
281 LogFlowThisFuncLeave();
282
283 return S_OK;
284}
285
286STDMETHODIMP Session::AssignMachine (IMachine *aMachine)
287{
288 LogFlowThisFuncEnter();
289 LogFlowThisFunc(("aMachine=%p\n", aMachine));
290
291 AutoCaller autoCaller(this);
292 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
293
294 AutoWriteLock alock(this);
295
296 AssertReturn(mState == SessionState_Closed, VBOX_E_INVALID_VM_STATE);
297
298 if (!aMachine)
299 {
300 /*
301 * A special case: the server informs us that this session has been
302 * passed to IVirtualBox::OpenRemoteSession() so this session will
303 * become remote (but not existing) when AssignRemoteMachine() is
304 * called.
305 */
306
307 AssertReturn(mType == SessionType_Null, VBOX_E_INVALID_OBJECT_STATE);
308 mType = SessionType_Remote;
309 mState = SessionState_Spawning;
310
311 LogFlowThisFuncLeave();
312 return S_OK;
313 }
314
315 HRESULT rc = E_FAIL;
316
317 /* query IInternalMachineControl interface */
318 mControl = aMachine;
319 AssertReturn(!!mControl, E_FAIL);
320
321 rc = mConsole.createObject();
322 AssertComRCReturn (rc, rc);
323
324 rc = mConsole->init (aMachine, mControl);
325 AssertComRCReturn (rc, rc);
326
327 rc = grabIPCSemaphore();
328
329 /*
330 * Reference the VirtualBox object to ensure the server is up
331 * until the session is closed
332 */
333 if (SUCCEEDED(rc))
334 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
335
336 if (SUCCEEDED(rc))
337 {
338 mType = SessionType_Direct;
339 mState = SessionState_Open;
340 }
341 else
342 {
343 /* some cleanup */
344 mControl.setNull();
345 mConsole->uninit();
346 mConsole.setNull();
347 }
348
349 LogFlowThisFunc(("rc=%08X\n", rc));
350 LogFlowThisFuncLeave();
351
352 return rc;
353}
354
355STDMETHODIMP Session::AssignRemoteMachine (IMachine *aMachine, IConsole *aConsole)
356{
357 LogFlowThisFuncEnter();
358 LogFlowThisFunc(("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
359
360 AssertReturn(aMachine && aConsole, E_INVALIDARG);
361
362 AutoCaller autoCaller(this);
363 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
364
365 AutoWriteLock alock(this);
366
367 AssertReturn(mState == SessionState_Closed ||
368 mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
369
370 HRESULT rc = E_FAIL;
371
372 /* query IInternalMachineControl interface */
373 mControl = aMachine;
374 AssertReturn(!!mControl, E_FAIL); // This test appears to be redundant --JS
375
376 /// @todo (dmik)
377 // currently, the remote session returns the same machine and
378 // console objects as the direct session, thus giving the
379 // (remote) client full control over the direct session. For the
380 // console, it is the desired behavior (the ability to control
381 // VM execution is a must for the remote session). What about
382 // the machine object, we may want to prevent the remote client
383 // from modifying machine data. In this case, we must:
384 // 1) assign the Machine object (instead of the SessionMachine
385 // object that is passed to this method) to mRemoteMachine;
386 // 2) remove GetMachine() property from the IConsole interface
387 // because it always returns the SessionMachine object
388 // (alternatively, we can supply a separate IConsole
389 // implementation that will return the Machine object in
390 // response to GetMachine()).
391
392 mRemoteMachine = aMachine;
393 mRemoteConsole = aConsole;
394
395 /*
396 * Reference the VirtualBox object to ensure the server is up
397 * until the session is closed
398 */
399 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
400
401 if (SUCCEEDED(rc))
402 {
403 /*
404 * RemoteSession type can be already set by AssignMachine() when its
405 * argument is NULL (a special case)
406 */
407 if (mType != SessionType_Remote)
408 mType = SessionType_Existing;
409 else
410 Assert (mState == SessionState_Spawning);
411
412 mState = SessionState_Open;
413 }
414 else
415 {
416 /* some cleanup */
417 mControl.setNull();
418 mRemoteMachine.setNull();
419 mRemoteConsole.setNull();
420 }
421
422 LogFlowThisFunc(("rc=%08X\n", rc));
423 LogFlowThisFuncLeave();
424
425 return rc;
426}
427
428STDMETHODIMP Session::UpdateMachineState (MachineState_T aMachineState)
429{
430 AutoCaller autoCaller(this);
431
432 if (autoCaller.state() != Ready)
433 {
434 /*
435 * We might have already entered Session::uninit() at this point, so
436 * return silently (not interested in the state change during uninit)
437 */
438 LogFlowThisFunc(("Already uninitialized.\n"));
439 return S_OK;
440 }
441
442 AutoReadLock alock(this);
443
444 if (mState == SessionState_Closing)
445 {
446 LogFlowThisFunc(("Already being closed.\n"));
447 return S_OK;
448 }
449
450 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
451 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
452
453 AssertReturn(!mControl.isNull(), E_FAIL);
454 AssertReturn(!mConsole.isNull(), E_FAIL);
455
456 return mConsole->updateMachineState (aMachineState);
457}
458
459STDMETHODIMP Session::Uninitialize()
460{
461 LogFlowThisFuncEnter();
462
463 AutoCaller autoCaller(this);
464
465 HRESULT rc = S_OK;
466
467 if (autoCaller.state() == Ready)
468 {
469 /* close() needs write lock */
470 AutoWriteLock alock(this);
471
472 LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType));
473
474 if (mState == SessionState_Closing)
475 {
476 LogFlowThisFunc(("Already being closed.\n"));
477 return S_OK;
478 }
479
480 AssertReturn(mState == SessionState_Open ||
481 mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
482
483 /* close ourselves */
484 rc = close (false /* aFinalRelease */, true /* aFromServer */);
485 }
486 else if (autoCaller.state() == InUninit)
487 {
488 /*
489 * We might have already entered Session::uninit() at this point,
490 * return silently
491 */
492 LogFlowThisFunc(("Already uninitialized.\n"));
493 }
494 else
495 {
496 LogWarningThisFunc(("UNEXPECTED uninitialization!\n"));
497 rc = autoCaller.rc();
498 }
499
500 LogFlowThisFunc(("rc=%08X\n", rc));
501 LogFlowThisFuncLeave();
502
503 return rc;
504}
505
506STDMETHODIMP Session::OnDVDDriveChange()
507{
508 LogFlowThisFunc(("\n"));
509
510 AutoCaller autoCaller(this);
511 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
512
513 AutoReadLock alock(this);
514 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
515 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
516
517 return mConsole->onDVDDriveChange();
518}
519
520STDMETHODIMP Session::OnFloppyDriveChange()
521{
522 LogFlowThisFunc(("\n"));
523
524 AutoCaller autoCaller(this);
525 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
526
527 AutoReadLock alock(this);
528 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
529 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
530
531 return mConsole->onFloppyDriveChange();
532}
533
534STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
535{
536 LogFlowThisFunc(("\n"));
537
538 AutoCaller autoCaller(this);
539 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
540
541 AutoReadLock alock(this);
542 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
543 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
544
545 return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
546}
547
548STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
549{
550 LogFlowThisFunc(("\n"));
551
552 AutoCaller autoCaller(this);
553 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
554
555 AutoReadLock alock(this);
556 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
557 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
558
559 return mConsole->onSerialPortChange(serialPort);
560}
561
562STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
563{
564 LogFlowThisFunc(("\n"));
565
566 AutoCaller autoCaller(this);
567 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
568
569 AutoReadLock alock(this);
570 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
571 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
572
573 return mConsole->onParallelPortChange(parallelPort);
574}
575
576STDMETHODIMP Session::OnStorageControllerChange()
577{
578 LogFlowThisFunc(("\n"));
579
580 AutoCaller autoCaller(this);
581 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
582
583 AutoReadLock alock(this);
584 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
585 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
586
587 return mConsole->onStorageControllerChange();
588}
589
590STDMETHODIMP Session::OnVRDPServerChange()
591{
592 LogFlowThisFunc(("\n"));
593
594 AutoCaller autoCaller(this);
595 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
596
597 AutoReadLock alock(this);
598 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
599 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
600
601 return mConsole->onVRDPServerChange();
602}
603
604STDMETHODIMP Session::OnUSBControllerChange()
605{
606 LogFlowThisFunc(("\n"));
607
608 AutoCaller autoCaller(this);
609 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
610
611 AutoReadLock alock(this);
612 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
613 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
614
615 return mConsole->onUSBControllerChange();
616}
617
618STDMETHODIMP Session::OnSharedFolderChange (BOOL aGlobal)
619{
620 LogFlowThisFunc(("\n"));
621
622 AutoCaller autoCaller(this);
623 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
624
625 AutoReadLock alock(this);
626 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
627 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
628
629 return mConsole->onSharedFolderChange (aGlobal);
630}
631
632STDMETHODIMP Session::OnUSBDeviceAttach (IUSBDevice *aDevice,
633 IVirtualBoxErrorInfo *aError,
634 ULONG aMaskedIfs)
635{
636 LogFlowThisFunc(("\n"));
637
638 AutoCaller autoCaller(this);
639 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
640
641 AutoReadLock alock(this);
642 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
643 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
644
645 return mConsole->onUSBDeviceAttach (aDevice, aError, aMaskedIfs);
646}
647
648STDMETHODIMP Session::OnUSBDeviceDetach (IN_BSTR aId,
649 IVirtualBoxErrorInfo *aError)
650{
651 LogFlowThisFunc(("\n"));
652
653 AutoCaller autoCaller(this);
654 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
655
656 AutoReadLock alock(this);
657 AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
658 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
659
660 return mConsole->onUSBDeviceDetach (aId, aError);
661}
662
663STDMETHODIMP Session::OnShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
664{
665 AutoCaller autoCaller(this);
666 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
667
668 AutoReadLock alock(this);
669
670 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
671
672 if (mState != SessionState_Open)
673 {
674 /* the call from Machine issued when the session is open can arrive
675 * after the session starts closing or gets closed. Note that when
676 * aCheck is false, we return E_FAIL to indicate that aWinId we return
677 * is not valid */
678 *aCanShow = FALSE;
679 *aWinId = 0;
680 return aCheck ? S_OK : E_FAIL;
681 }
682
683 return mConsole->onShowWindow (aCheck, aCanShow, aWinId);
684}
685
686STDMETHODIMP Session::AccessGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
687 BOOL aIsSetter, BSTR *aRetValue, ULONG64 *aRetTimestamp, BSTR *aRetFlags)
688{
689#ifdef VBOX_WITH_GUEST_PROPS
690 AutoCaller autoCaller(this);
691 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
692
693 if (mState != SessionState_Open)
694 return setError (VBOX_E_INVALID_VM_STATE,
695 tr ("Machine session is not open (session state: %d)."),
696 mState);
697 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
698 CheckComArgNotNull(aName);
699 if (!aIsSetter && !VALID_PTR (aRetValue))
700 return E_POINTER;
701 if (!aIsSetter && !VALID_PTR (aRetTimestamp))
702 return E_POINTER;
703 if (!aIsSetter && !VALID_PTR (aRetFlags))
704 return E_POINTER;
705 /* aValue can be NULL for a setter call if the property is to be deleted. */
706 if (aIsSetter && (aValue != NULL) && !VALID_PTR (aValue))
707 return E_INVALIDARG;
708 /* aFlags can be null if it is to be left as is */
709 if (aIsSetter && (aFlags != NULL) && !VALID_PTR (aFlags))
710 return E_INVALIDARG;
711 if (!aIsSetter)
712 return mConsole->getGuestProperty (aName, aRetValue, aRetTimestamp, aRetFlags);
713 else
714 return mConsole->setGuestProperty (aName, aValue, aFlags);
715#else /* VBOX_WITH_GUEST_PROPS not defined */
716 ReturnComNotImplemented();
717#endif /* VBOX_WITH_GUEST_PROPS not defined */
718}
719
720STDMETHODIMP Session::EnumerateGuestProperties (IN_BSTR aPatterns,
721 ComSafeArrayOut(BSTR, aNames),
722 ComSafeArrayOut(BSTR, aValues),
723 ComSafeArrayOut(ULONG64, aTimestamps),
724 ComSafeArrayOut(BSTR, aFlags))
725{
726#ifdef VBOX_WITH_GUEST_PROPS
727 AutoCaller autoCaller(this);
728 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
729
730 if (mState != SessionState_Open)
731 return setError (VBOX_E_INVALID_VM_STATE,
732 tr ("Machine session is not open (session state: %d)."),
733 mState);
734 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
735 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
736 return E_POINTER;
737 if (ComSafeArrayOutIsNull(aNames))
738 return E_POINTER;
739 if (ComSafeArrayOutIsNull(aValues))
740 return E_POINTER;
741 if (ComSafeArrayOutIsNull(aTimestamps))
742 return E_POINTER;
743 if (ComSafeArrayOutIsNull(aFlags))
744 return E_POINTER;
745 return mConsole->enumerateGuestProperties(aPatterns,
746 ComSafeArrayOutArg(aNames),
747 ComSafeArrayOutArg(aValues),
748 ComSafeArrayOutArg(aTimestamps),
749 ComSafeArrayOutArg(aFlags));
750#else /* VBOX_WITH_GUEST_PROPS not defined */
751 ReturnComNotImplemented();
752#endif /* VBOX_WITH_GUEST_PROPS not defined */
753}
754
755// private methods
756///////////////////////////////////////////////////////////////////////////////
757
758/**
759 * Closes the current session.
760 *
761 * @param aFinalRelease called as a result of FinalRelease()
762 * @param aFromServer called as a result of Uninitialize()
763 *
764 * @note To be called only from #uninit(), #Close() or #Uninitialize().
765 * @note Locks this object for writing.
766 */
767HRESULT Session::close (bool aFinalRelease, bool aFromServer)
768{
769 LogFlowThisFuncEnter();
770 LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
771 aFinalRelease, aFromServer));
772
773 AutoCaller autoCaller(this);
774 AssertComRCReturnRC(autoCaller.rc());
775
776 AutoWriteLock alock(this);
777
778 LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType));
779
780 if (mState != SessionState_Open)
781 {
782 Assert (mState == SessionState_Spawning);
783
784 /* The session object is going to be uninitialized before it has been
785 * assigned a direct console of the machine the client requested to open
786 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
787 * only if this close reqiest comes from the server (for example, it
788 * detected that the VM process it started terminated before opening a
789 * direct session). Otherwise, it means that the client is too fast and
790 * trying to close the session before waiting for the progress object it
791 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
792 Assert (aFromServer);
793
794 mState = SessionState_Closed;
795 mType = SessionType_Null;
796#if defined(RT_OS_WINDOWS)
797 Assert (!mIPCSem && !mIPCThreadSem);
798#elif defined(RT_OS_OS2)
799 Assert (mIPCThread == NIL_RTTHREAD &&
800 mIPCThreadSem == NIL_RTSEMEVENT);
801#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
802 Assert (mIPCSem == -1);
803#else
804# error "Port me!"
805#endif
806 LogFlowThisFuncLeave();
807 return S_OK;
808 }
809
810 /* go to the closing state */
811 mState = SessionState_Closing;
812
813 if (mType == SessionType_Direct)
814 {
815 mConsole->uninit();
816 mConsole.setNull();
817 }
818 else
819 {
820 mRemoteMachine.setNull();
821 mRemoteConsole.setNull();
822 }
823
824 ComPtr<IProgress> progress;
825
826 if (!aFinalRelease && !aFromServer)
827 {
828 /*
829 * We trigger OnSessionEnd() only when the session closes itself using
830 * Close(). Note that if isFinalRelease = TRUE here, this means that
831 * the client process has already initialized the termination procedure
832 * without issuing Close() and the IPC channel is no more operational --
833 * so we cannot call the server's method (it will definitely fail). The
834 * server will instead simply detect the abnormal client death (since
835 * OnSessionEnd() is not called) and reset the machine state to Aborted.
836 */
837
838 /*
839 * while waiting for OnSessionEnd() to complete one of our methods
840 * can be called by the server (for example, Uninitialize(), if the
841 * direct session has initiated a closure just a bit before us) so
842 * we need to release the lock to avoid deadlocks. The state is already
843 * SessionState_Closing here, so it's safe.
844 */
845 alock.leave();
846
847 LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
848 HRESULT rc = mControl->OnSessionEnd (this, progress.asOutParam());
849 LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
850
851 alock.enter();
852
853 /*
854 * If we get E_UNEXPECTED this means that the direct session has already
855 * been closed, we're just too late with our notification and nothing more
856 */
857 if (mType != SessionType_Direct && rc == E_UNEXPECTED)
858 rc = S_OK;
859
860 AssertComRC (rc);
861 }
862
863 mControl.setNull();
864
865 if (mType == SessionType_Direct)
866 {
867 releaseIPCSemaphore();
868 if (!aFinalRelease && !aFromServer)
869 {
870 /*
871 * Wait for the server to grab the semaphore and destroy the session
872 * machine (allowing us to open a new session with the same machine
873 * once this method returns)
874 */
875 Assert (!!progress);
876 if (progress)
877 progress->WaitForCompletion (-1);
878 }
879 }
880
881 mState = SessionState_Closed;
882 mType = SessionType_Null;
883
884 /* release the VirtualBox instance as the very last step */
885 mVirtualBox.setNull();
886
887 LogFlowThisFuncLeave();
888 return S_OK;
889}
890
891/** @note To be called only from #AssignMachine() */
892HRESULT Session::grabIPCSemaphore()
893{
894 HRESULT rc = E_FAIL;
895
896 /* open the IPC semaphore based on the sessionId and try to grab it */
897 Bstr ipcId;
898 rc = mControl->GetIPCId (ipcId.asOutParam());
899 AssertComRCReturnRC(rc);
900
901 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
902
903#if defined(RT_OS_WINDOWS)
904
905 /*
906 * Since Session is an MTA object, this method can be executed on
907 * any thread, and this thread will not necessarily match the thread on
908 * which close() will be called later. Therefore, we need a separate
909 * thread to hold the IPC mutex and then release it in close().
910 */
911
912 mIPCThreadSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
913 AssertMsgReturn (mIPCThreadSem,
914 ("Cannot create an event sem, err=%d", ::GetLastError()),
915 E_FAIL);
916
917 void *data [3];
918 data [0] = (void *) (BSTR) ipcId;
919 data [1] = (void *) mIPCThreadSem;
920 data [2] = 0; /* will get an output from the thread */
921
922 /* create a thread to hold the IPC mutex until signalled to release it */
923 RTTHREAD tid;
924 int vrc = RTThreadCreate (&tid, IPCMutexHolderThread, (void *) data,
925 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
926 AssertRCReturn (vrc, E_FAIL);
927
928 /* wait until thread init is completed */
929 DWORD wrc = ::WaitForSingleObject (mIPCThreadSem, INFINITE);
930 AssertMsg (wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
931 Assert (data [2]);
932
933 if (wrc == WAIT_OBJECT_0 && data [2])
934 {
935 /* memorize the event sem we should signal in close() */
936 mIPCSem = (HANDLE) data [2];
937 rc = S_OK;
938 }
939 else
940 {
941 ::CloseHandle (mIPCThreadSem);
942 mIPCThreadSem = NULL;
943 rc = E_FAIL;
944 }
945
946#elif defined(RT_OS_OS2)
947
948 /* We use XPCOM where any message (including close()) can arrive on any
949 * worker thread (which will not necessarily match this thread that opens
950 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
951 * and then release it in close(). */
952
953 int vrc = RTSemEventCreate (&mIPCThreadSem);
954 AssertRCReturn (vrc, E_FAIL);
955
956 void *data [3];
957 data [0] = (void *) ipcId.raw();
958 data [1] = (void *) mIPCThreadSem;
959 data [2] = (void *) false; /* will get the thread result here */
960
961 /* create a thread to hold the IPC mutex until signalled to release it */
962 vrc = RTThreadCreate (&mIPCThread, IPCMutexHolderThread, (void *) data,
963 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
964 AssertRCReturn (vrc, E_FAIL);
965
966 /* wait until thread init is completed */
967 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
968 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
969
970 /* the thread must succeed */
971 AssertReturn((bool) data [2], E_FAIL);
972
973#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
974
975# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
976 Utf8Str ipcKey = ipcId;
977 key_t key = RTStrToUInt32(ipcKey.raw());
978 AssertMsgReturn (key != 0,
979 ("Key value of 0 is not valid for IPC semaphore"),
980 E_FAIL);
981# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
982 Utf8Str semName = ipcId;
983 char *pszSemName = NULL;
984 RTStrUtf8ToCurrentCP (&pszSemName, semName);
985 key_t key = ::ftok (pszSemName, 'V');
986 RTStrFree (pszSemName);
987# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
988
989 mIPCSem = ::semget (key, 0, 0);
990 AssertMsgReturn (mIPCSem >= 0,
991 ("Cannot open IPC semaphore, errno=%d", errno),
992 E_FAIL);
993
994 /* grab the semaphore */
995 ::sembuf sop = { 0, -1, SEM_UNDO };
996 int rv = ::semop (mIPCSem, &sop, 1);
997 AssertMsgReturn (rv == 0,
998 ("Cannot grab IPC semaphore, errno=%d", errno),
999 E_FAIL);
1000
1001#else
1002# error "Port me!"
1003#endif
1004
1005 return rc;
1006}
1007
1008/** @note To be called only from #close() */
1009void Session::releaseIPCSemaphore()
1010{
1011 /* release the IPC semaphore */
1012#if defined(RT_OS_WINDOWS)
1013
1014 if (mIPCSem && mIPCThreadSem)
1015 {
1016 /*
1017 * tell the thread holding the IPC mutex to release it;
1018 * it will close mIPCSem handle
1019 */
1020 ::SetEvent (mIPCSem);
1021 /* wait for the thread to finish */
1022 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1023 ::CloseHandle (mIPCThreadSem);
1024
1025 mIPCThreadSem = NULL;
1026 mIPCSem = NULL;
1027 }
1028
1029#elif defined(RT_OS_OS2)
1030
1031 if (mIPCThread != NIL_RTTHREAD)
1032 {
1033 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1034
1035 /* tell the thread holding the IPC mutex to release it */
1036 int vrc = RTSemEventSignal (mIPCThreadSem);
1037 AssertRC (vrc == NO_ERROR);
1038
1039 /* wait for the thread to finish */
1040 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1041 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1042
1043 mIPCThread = NIL_RTTHREAD;
1044 }
1045
1046 if (mIPCThreadSem != NIL_RTSEMEVENT)
1047 {
1048 RTSemEventDestroy (mIPCThreadSem);
1049 mIPCThreadSem = NIL_RTSEMEVENT;
1050 }
1051
1052#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1053
1054 if (mIPCSem >= 0)
1055 {
1056 ::sembuf sop = { 0, 1, SEM_UNDO };
1057 ::semop (mIPCSem, &sop, 1);
1058
1059 mIPCSem = -1;
1060 }
1061
1062#else
1063# error "Port me!"
1064#endif
1065}
1066
1067#if defined(RT_OS_WINDOWS)
1068/** VM IPC mutex holder thread */
1069DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1070{
1071 LogFlowFuncEnter();
1072
1073 Assert (pvUser);
1074 void **data = (void **) pvUser;
1075
1076 BSTR sessionId = (BSTR) data [0];
1077 HANDLE initDoneSem = (HANDLE) data [1];
1078
1079 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1080 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1081
1082 if (ipcMutex)
1083 {
1084 /* grab the mutex */
1085 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1086 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1087 if (wrc == WAIT_OBJECT_0)
1088 {
1089 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1090 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1091 if (finishSem)
1092 {
1093 data [2] = (void *) finishSem;
1094 /* signal we're done with init */
1095 ::SetEvent (initDoneSem);
1096 /* wait until we're signaled to release the IPC mutex */
1097 ::WaitForSingleObject (finishSem, INFINITE);
1098 /* release the IPC mutex */
1099 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1100 BOOL success = ::ReleaseMutex (ipcMutex);
1101 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1102 ::CloseHandle (ipcMutex);
1103 ::CloseHandle (finishSem);
1104 }
1105 }
1106 }
1107
1108 /* signal we're done */
1109 ::SetEvent (initDoneSem);
1110
1111 LogFlowFuncLeave();
1112
1113 return 0;
1114}
1115#endif
1116
1117#if defined(RT_OS_OS2)
1118/** VM IPC mutex holder thread */
1119DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1120{
1121 LogFlowFuncEnter();
1122
1123 Assert (pvUser);
1124 void **data = (void **) pvUser;
1125
1126 Utf8Str ipcId = (BSTR) data [0];
1127 RTSEMEVENT finishSem = (RTSEMEVENT) data [1];
1128
1129 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1130
1131 HMTX ipcMutex = NULLHANDLE;
1132 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1133 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1134
1135 if (arc == NO_ERROR)
1136 {
1137 /* grab the mutex */
1138 LogFlowFunc (("grabbing IPC mutex...\n"));
1139 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1140 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1141 if (arc == NO_ERROR)
1142 {
1143 /* store the answer */
1144 data [2] = (void *) true;
1145 /* signal we're done */
1146 int vrc = RTThreadUserSignal (Thread);
1147 AssertRC (vrc);
1148
1149 /* wait until we're signaled to release the IPC mutex */
1150 LogFlowFunc (("waiting for termination signal..\n"));
1151 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1152 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1153
1154 /* release the IPC mutex */
1155 LogFlowFunc (("releasing IPC mutex...\n"));
1156 arc = ::DosReleaseMutexSem (ipcMutex);
1157 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1158 }
1159
1160 ::DosCloseMutexSem (ipcMutex);
1161 }
1162
1163 /* store the answer */
1164 data [1] = (void *) false;
1165 /* signal we're done */
1166 int vrc = RTThreadUserSignal (Thread);
1167 AssertRC (vrc);
1168
1169 LogFlowFuncLeave();
1170
1171 return 0;
1172}
1173#endif
1174/* 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