VirtualBox

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

Last change on this file since 34079 was 33952, checked in by vboxsync, 14 years ago

Main: replaces callback mechanism of NAT redirect change event notification with event-driven model.

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