VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/SessionImpl.cpp@ 35421

Last change on this file since 35421 was 35368, checked in by vboxsync, 14 years ago

Main: source re-org.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 KB
Line 
1/* $Id: SessionImpl.cpp 35368 2010-12-30 13:38:23Z 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::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
709{
710 LogFlowThisFunc(("\n"));
711
712 AutoCaller autoCaller(this);
713 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
714
715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
716 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
717 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
718
719 return mConsole->onBandwidthGroupChange(aBandwidthGroup);
720}
721
722STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
723 BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
724{
725#ifdef VBOX_WITH_GUEST_PROPS
726 AutoCaller autoCaller(this);
727 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
728
729 if (mState != SessionState_Locked)
730 return setError(VBOX_E_INVALID_VM_STATE,
731 tr("Machine is not locked by session (session state: %s)."),
732 Global::stringifySessionState(mState));
733 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
734 CheckComArgStrNotEmptyOrNull(aName);
735 if (!aIsSetter && !VALID_PTR(aRetValue))
736 return E_POINTER;
737 if (!aIsSetter && !VALID_PTR(aRetTimestamp))
738 return E_POINTER;
739 if (!aIsSetter && !VALID_PTR(aRetFlags))
740 return E_POINTER;
741 /* aValue can be NULL for a setter call if the property is to be deleted. */
742 if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
743 return E_INVALIDARG;
744 /* aFlags can be null if it is to be left as is */
745 if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
746 return E_INVALIDARG;
747 if (!aIsSetter)
748 return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
749 else
750 return mConsole->setGuestProperty(aName, aValue, aFlags);
751#else /* VBOX_WITH_GUEST_PROPS not defined */
752 ReturnComNotImplemented();
753#endif /* VBOX_WITH_GUEST_PROPS not defined */
754}
755
756STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
757 ComSafeArrayOut(BSTR, aNames),
758 ComSafeArrayOut(BSTR, aValues),
759 ComSafeArrayOut(LONG64, aTimestamps),
760 ComSafeArrayOut(BSTR, aFlags))
761{
762#ifdef VBOX_WITH_GUEST_PROPS
763 AutoCaller autoCaller(this);
764 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
765
766 if (mState != SessionState_Locked)
767 return setError(VBOX_E_INVALID_VM_STATE,
768 tr("Machine is not locked by session (session state: %s)."),
769 Global::stringifySessionState(mState));
770 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
771 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
772 return E_POINTER;
773 if (ComSafeArrayOutIsNull(aNames))
774 return E_POINTER;
775 if (ComSafeArrayOutIsNull(aValues))
776 return E_POINTER;
777 if (ComSafeArrayOutIsNull(aTimestamps))
778 return E_POINTER;
779 if (ComSafeArrayOutIsNull(aFlags))
780 return E_POINTER;
781 return mConsole->enumerateGuestProperties(aPatterns,
782 ComSafeArrayOutArg(aNames),
783 ComSafeArrayOutArg(aValues),
784 ComSafeArrayOutArg(aTimestamps),
785 ComSafeArrayOutArg(aFlags));
786#else /* VBOX_WITH_GUEST_PROPS not defined */
787 ReturnComNotImplemented();
788#endif /* VBOX_WITH_GUEST_PROPS not defined */
789}
790
791STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
792 ULONG aSourceIdx, ULONG aTargetIdx,
793 IMedium *aSource, IMedium *aTarget,
794 BOOL aMergeForward,
795 IMedium *aParentForTarget,
796 ComSafeArrayIn(IMedium *, aChildrenToReparent),
797 IProgress *aProgress)
798{
799 AutoCaller autoCaller(this);
800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
801
802 if (mState != SessionState_Locked)
803 return setError(VBOX_E_INVALID_VM_STATE,
804 tr("Machine is not locked by session (session state: %s)."),
805 Global::stringifySessionState(mState));
806 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
807 CheckComArgNotNull(aMediumAttachment);
808 CheckComArgSafeArrayNotNull(aChildrenToReparent);
809
810 return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
811 aTargetIdx, aSource, aTarget,
812 aMergeForward, aParentForTarget,
813 ComSafeArrayInArg(aChildrenToReparent),
814 aProgress);
815}
816
817
818// private methods
819///////////////////////////////////////////////////////////////////////////////
820
821/**
822 * Unlocks a machine associated with the current session.
823 *
824 * @param aFinalRelease called as a result of FinalRelease()
825 * @param aFromServer called as a result of Uninitialize()
826 *
827 * @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize().
828 * @note Locks this object for writing.
829 */
830HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
831{
832 LogFlowThisFuncEnter();
833 LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
834 aFinalRelease, aFromServer));
835
836 AutoCaller autoCaller(this);
837 AssertComRCReturnRC(autoCaller.rc());
838
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
842
843 if (mState != SessionState_Locked)
844 {
845 Assert(mState == SessionState_Spawning);
846
847 /* The session object is going to be uninitialized before it has been
848 * assigned a direct console of the machine the client requested to open
849 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
850 * only if this close request comes from the server (for example, it
851 * detected that the VM process it started terminated before opening a
852 * direct session). Otherwise, it means that the client is too fast and
853 * trying to close the session before waiting for the progress object it
854 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
855 Assert(aFromServer);
856
857 mState = SessionState_Unlocked;
858 mType = SessionType_Null;
859#if defined(RT_OS_WINDOWS)
860 Assert(!mIPCSem && !mIPCThreadSem);
861#elif defined(RT_OS_OS2)
862 Assert(mIPCThread == NIL_RTTHREAD &&
863 mIPCThreadSem == NIL_RTSEMEVENT);
864#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
865 Assert(mIPCSem == -1);
866#else
867# error "Port me!"
868#endif
869 LogFlowThisFuncLeave();
870 return S_OK;
871 }
872
873 /* go to the closing state */
874 mState = SessionState_Unlocking;
875
876 if (mType == SessionType_WriteLock)
877 {
878 mConsole->uninit();
879 mConsole.setNull();
880 }
881 else
882 {
883 mRemoteMachine.setNull();
884 mRemoteConsole.setNull();
885 }
886
887 ComPtr<IProgress> progress;
888
889 if (!aFinalRelease && !aFromServer)
890 {
891 /*
892 * We trigger OnSessionEnd() only when the session closes itself using
893 * Close(). Note that if isFinalRelease = TRUE here, this means that
894 * the client process has already initialized the termination procedure
895 * without issuing Close() and the IPC channel is no more operational --
896 * so we cannot call the server's method (it will definitely fail). The
897 * server will instead simply detect the abnormal client death (since
898 * OnSessionEnd() is not called) and reset the machine state to Aborted.
899 */
900
901 /*
902 * while waiting for OnSessionEnd() to complete one of our methods
903 * can be called by the server (for example, Uninitialize(), if the
904 * direct session has initiated a closure just a bit before us) so
905 * we need to release the lock to avoid deadlocks. The state is already
906 * SessionState_Closing here, so it's safe.
907 */
908 alock.leave();
909
910 LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
911 HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
912 LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
913
914 alock.enter();
915
916 /*
917 * If we get E_UNEXPECTED this means that the direct session has already
918 * been closed, we're just too late with our notification and nothing more
919 *
920 * bird: Seems E_ACCESSDENIED is what gets returned these days; see
921 * VirtualBoxBase::addCaller.
922 */
923 if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
924 rc = S_OK;
925
926#ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */
927 AssertComRC(rc);
928#endif
929 }
930
931 mControl.setNull();
932
933 if (mType == SessionType_WriteLock)
934 {
935 releaseIPCSemaphore();
936 if (!aFinalRelease && !aFromServer)
937 {
938 /*
939 * Wait for the server to grab the semaphore and destroy the session
940 * machine (allowing us to open a new session with the same machine
941 * once this method returns)
942 */
943 Assert(!!progress);
944 if (progress)
945 progress->WaitForCompletion(-1);
946 }
947 }
948
949 mState = SessionState_Unlocked;
950 mType = SessionType_Null;
951
952 /* release the VirtualBox instance as the very last step */
953 mVirtualBox.setNull();
954
955 LogFlowThisFuncLeave();
956 return S_OK;
957}
958
959/** @note To be called only from #AssignMachine() */
960HRESULT Session::grabIPCSemaphore()
961{
962 HRESULT rc = E_FAIL;
963
964 /* open the IPC semaphore based on the sessionId and try to grab it */
965 Bstr ipcId;
966 rc = mControl->GetIPCId(ipcId.asOutParam());
967 AssertComRCReturnRC(rc);
968
969 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
970
971#if defined(RT_OS_WINDOWS)
972
973 /*
974 * Since Session is an MTA object, this method can be executed on
975 * any thread, and this thread will not necessarily match the thread on
976 * which close() will be called later. Therefore, we need a separate
977 * thread to hold the IPC mutex and then release it in close().
978 */
979
980 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
981 AssertMsgReturn(mIPCThreadSem,
982 ("Cannot create an event sem, err=%d", ::GetLastError()),
983 E_FAIL);
984
985 void *data[3];
986 data[0] = (void*)(BSTR)ipcId.raw();
987 data[1] = (void*)mIPCThreadSem;
988 data[2] = 0; /* will get an output from the thread */
989
990 /* create a thread to hold the IPC mutex until signalled to release it */
991 RTTHREAD tid;
992 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
993 AssertRCReturn(vrc, E_FAIL);
994
995 /* wait until thread init is completed */
996 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
997 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
998 Assert(data[2]);
999
1000 if (wrc == WAIT_OBJECT_0 && data[2])
1001 {
1002 /* memorize the event sem we should signal in close() */
1003 mIPCSem = (HANDLE)data[2];
1004 rc = S_OK;
1005 }
1006 else
1007 {
1008 ::CloseHandle(mIPCThreadSem);
1009 mIPCThreadSem = NULL;
1010 rc = E_FAIL;
1011 }
1012
1013#elif defined(RT_OS_OS2)
1014
1015 /* We use XPCOM where any message (including close()) can arrive on any
1016 * worker thread (which will not necessarily match this thread that opens
1017 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
1018 * and then release it in close(). */
1019
1020 int vrc = RTSemEventCreate(&mIPCThreadSem);
1021 AssertRCReturn(vrc, E_FAIL);
1022
1023 void *data[3];
1024 data[0] = (void*)ipcId.raw();
1025 data[1] = (void*)mIPCThreadSem;
1026 data[2] = (void*)false; /* will get the thread result here */
1027
1028 /* create a thread to hold the IPC mutex until signalled to release it */
1029 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
1030 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
1031 AssertRCReturn(vrc, E_FAIL);
1032
1033 /* wait until thread init is completed */
1034 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1035 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
1036
1037 /* the thread must succeed */
1038 AssertReturn((bool)data[2], E_FAIL);
1039
1040#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1041
1042# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
1043 Utf8Str ipcKey = ipcId;
1044 key_t key = RTStrToUInt32(ipcKey.c_str());
1045 AssertMsgReturn (key != 0,
1046 ("Key value of 0 is not valid for IPC semaphore"),
1047 E_FAIL);
1048# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1049 Utf8Str semName = ipcId;
1050 char *pszSemName = NULL;
1051 RTStrUtf8ToCurrentCP (&pszSemName, semName);
1052 key_t key = ::ftok (pszSemName, 'V');
1053 RTStrFree (pszSemName);
1054# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1055
1056 mIPCSem = ::semget (key, 0, 0);
1057 AssertMsgReturn (mIPCSem >= 0,
1058 ("Cannot open IPC semaphore, errno=%d", errno),
1059 E_FAIL);
1060
1061 /* grab the semaphore */
1062 ::sembuf sop = { 0, -1, SEM_UNDO };
1063 int rv = ::semop (mIPCSem, &sop, 1);
1064 AssertMsgReturn (rv == 0,
1065 ("Cannot grab IPC semaphore, errno=%d", errno),
1066 E_FAIL);
1067
1068#else
1069# error "Port me!"
1070#endif
1071
1072 return rc;
1073}
1074
1075/** @note To be called only from #close() */
1076void Session::releaseIPCSemaphore()
1077{
1078 /* release the IPC semaphore */
1079#if defined(RT_OS_WINDOWS)
1080
1081 if (mIPCSem && mIPCThreadSem)
1082 {
1083 /*
1084 * tell the thread holding the IPC mutex to release it;
1085 * it will close mIPCSem handle
1086 */
1087 ::SetEvent (mIPCSem);
1088 /* wait for the thread to finish */
1089 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1090 ::CloseHandle (mIPCThreadSem);
1091
1092 mIPCThreadSem = NULL;
1093 mIPCSem = NULL;
1094 }
1095
1096#elif defined(RT_OS_OS2)
1097
1098 if (mIPCThread != NIL_RTTHREAD)
1099 {
1100 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1101
1102 /* tell the thread holding the IPC mutex to release it */
1103 int vrc = RTSemEventSignal (mIPCThreadSem);
1104 AssertRC(vrc == NO_ERROR);
1105
1106 /* wait for the thread to finish */
1107 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1108 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1109
1110 mIPCThread = NIL_RTTHREAD;
1111 }
1112
1113 if (mIPCThreadSem != NIL_RTSEMEVENT)
1114 {
1115 RTSemEventDestroy (mIPCThreadSem);
1116 mIPCThreadSem = NIL_RTSEMEVENT;
1117 }
1118
1119#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1120
1121 if (mIPCSem >= 0)
1122 {
1123 ::sembuf sop = { 0, 1, SEM_UNDO };
1124 ::semop (mIPCSem, &sop, 1);
1125
1126 mIPCSem = -1;
1127 }
1128
1129#else
1130# error "Port me!"
1131#endif
1132}
1133
1134#if defined(RT_OS_WINDOWS)
1135/** VM IPC mutex holder thread */
1136DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1137{
1138 LogFlowFuncEnter();
1139
1140 Assert (pvUser);
1141 void **data = (void **) pvUser;
1142
1143 BSTR sessionId = (BSTR)data[0];
1144 HANDLE initDoneSem = (HANDLE)data[1];
1145
1146 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1147 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1148
1149 if (ipcMutex)
1150 {
1151 /* grab the mutex */
1152 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1153 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1154 if (wrc == WAIT_OBJECT_0)
1155 {
1156 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1157 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1158 if (finishSem)
1159 {
1160 data[2] = (void*)finishSem;
1161 /* signal we're done with init */
1162 ::SetEvent (initDoneSem);
1163 /* wait until we're signaled to release the IPC mutex */
1164 ::WaitForSingleObject (finishSem, INFINITE);
1165 /* release the IPC mutex */
1166 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1167 BOOL success = ::ReleaseMutex (ipcMutex);
1168 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1169 ::CloseHandle (ipcMutex);
1170 ::CloseHandle (finishSem);
1171 }
1172 }
1173 }
1174
1175 /* signal we're done */
1176 ::SetEvent (initDoneSem);
1177
1178 LogFlowFuncLeave();
1179
1180 return 0;
1181}
1182#endif
1183
1184#if defined(RT_OS_OS2)
1185/** VM IPC mutex holder thread */
1186DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1187{
1188 LogFlowFuncEnter();
1189
1190 Assert (pvUser);
1191 void **data = (void **) pvUser;
1192
1193 Utf8Str ipcId = (BSTR)data[0];
1194 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1195
1196 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1197
1198 HMTX ipcMutex = NULLHANDLE;
1199 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1200 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1201
1202 if (arc == NO_ERROR)
1203 {
1204 /* grab the mutex */
1205 LogFlowFunc (("grabbing IPC mutex...\n"));
1206 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1207 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1208 if (arc == NO_ERROR)
1209 {
1210 /* store the answer */
1211 data[2] = (void*)true;
1212 /* signal we're done */
1213 int vrc = RTThreadUserSignal (Thread);
1214 AssertRC(vrc);
1215
1216 /* wait until we're signaled to release the IPC mutex */
1217 LogFlowFunc (("waiting for termination signal..\n"));
1218 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1219 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1220
1221 /* release the IPC mutex */
1222 LogFlowFunc (("releasing IPC mutex...\n"));
1223 arc = ::DosReleaseMutexSem (ipcMutex);
1224 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1225 }
1226
1227 ::DosCloseMutexSem (ipcMutex);
1228 }
1229
1230 /* store the answer */
1231 data[1] = (void*)false;
1232 /* signal we're done */
1233 int vrc = RTThreadUserSignal (Thread);
1234 AssertRC(vrc);
1235
1236 LogFlowFuncLeave();
1237
1238 return 0;
1239}
1240#endif
1241/* 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