VirtualBox

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

Last change on this file since 31224 was 31070, checked in by vboxsync, 14 years ago

Main: rename ISession::close() to ISession::unlockMachine(); API documentation

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