VirtualBox

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

Last change on this file since 30106 was 29953, checked in by vboxsync, 15 years ago

SessionImpl.cpp: Don't use ComAssertComRC because it's evil - it doesn't work the same way in all builds wrt error info. Also it is not nice to assert here just because some remote VM process died.

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