VirtualBox

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

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

API/Machine,Session: add a force mount parameter to MountMedium and the associated other places

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