VirtualBox

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

Last change on this file since 30308 was 30134, checked in by vboxsync, 15 years ago

Session::close: Seems E_ACCESSDENIED is returned by OnSessionEnd instead of E_UNEXPECTED...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.0 KB
Line 
1/* $Id: SessionImpl.cpp 30134 2010-06-09 18:22:31Z 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 * bird: Seems E_ACCESSDENIED is what gets returned these days; see
895 * VirtualBoxBase::addCaller.
896 */
897 if (mType != SessionType_Direct && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
898 rc = S_OK;
899
900 AssertComRC(rc);
901 }
902
903 mControl.setNull();
904
905 if (mType == SessionType_Direct)
906 {
907 releaseIPCSemaphore();
908 if (!aFinalRelease && !aFromServer)
909 {
910 /*
911 * Wait for the server to grab the semaphore and destroy the session
912 * machine (allowing us to open a new session with the same machine
913 * once this method returns)
914 */
915 Assert(!!progress);
916 if (progress)
917 progress->WaitForCompletion(-1);
918 }
919 }
920
921 mState = SessionState_Closed;
922 mType = SessionType_Null;
923
924 /* release the VirtualBox instance as the very last step */
925 mVirtualBox.setNull();
926
927 LogFlowThisFuncLeave();
928 return S_OK;
929}
930
931/** @note To be called only from #AssignMachine() */
932HRESULT Session::grabIPCSemaphore()
933{
934 HRESULT rc = E_FAIL;
935
936 /* open the IPC semaphore based on the sessionId and try to grab it */
937 Bstr ipcId;
938 rc = mControl->GetIPCId(ipcId.asOutParam());
939 AssertComRCReturnRC(rc);
940
941 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
942
943#if defined(RT_OS_WINDOWS)
944
945 /*
946 * Since Session is an MTA object, this method can be executed on
947 * any thread, and this thread will not necessarily match the thread on
948 * which close() will be called later. Therefore, we need a separate
949 * thread to hold the IPC mutex and then release it in close().
950 */
951
952 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
953 AssertMsgReturn(mIPCThreadSem,
954 ("Cannot create an event sem, err=%d", ::GetLastError()),
955 E_FAIL);
956
957 void *data[3];
958 data[0] = (void*)(BSTR)ipcId;
959 data[1] = (void*)mIPCThreadSem;
960 data[2] = 0; /* will get an output from the thread */
961
962 /* create a thread to hold the IPC mutex until signalled to release it */
963 RTTHREAD tid;
964 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
965 AssertRCReturn(vrc, E_FAIL);
966
967 /* wait until thread init is completed */
968 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
969 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
970 Assert(data[2]);
971
972 if (wrc == WAIT_OBJECT_0 && data[2])
973 {
974 /* memorize the event sem we should signal in close() */
975 mIPCSem = (HANDLE)data[2];
976 rc = S_OK;
977 }
978 else
979 {
980 ::CloseHandle(mIPCThreadSem);
981 mIPCThreadSem = NULL;
982 rc = E_FAIL;
983 }
984
985#elif defined(RT_OS_OS2)
986
987 /* We use XPCOM where any message (including close()) can arrive on any
988 * worker thread (which will not necessarily match this thread that opens
989 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
990 * and then release it in close(). */
991
992 int vrc = RTSemEventCreate(&mIPCThreadSem);
993 AssertRCReturn(vrc, E_FAIL);
994
995 void *data[3];
996 data[0] = (void*)ipcId.raw();
997 data[1] = (void*)mIPCThreadSem;
998 data[2] = (void*)false; /* will get the thread result here */
999
1000 /* create a thread to hold the IPC mutex until signalled to release it */
1001 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
1002 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
1003 AssertRCReturn(vrc, E_FAIL);
1004
1005 /* wait until thread init is completed */
1006 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1007 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
1008
1009 /* the thread must succeed */
1010 AssertReturn((bool)data[2], E_FAIL);
1011
1012#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1013
1014# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
1015 Utf8Str ipcKey = ipcId;
1016 key_t key = RTStrToUInt32(ipcKey.raw());
1017 AssertMsgReturn (key != 0,
1018 ("Key value of 0 is not valid for IPC semaphore"),
1019 E_FAIL);
1020# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1021 Utf8Str semName = ipcId;
1022 char *pszSemName = NULL;
1023 RTStrUtf8ToCurrentCP (&pszSemName, semName);
1024 key_t key = ::ftok (pszSemName, 'V');
1025 RTStrFree (pszSemName);
1026# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1027
1028 mIPCSem = ::semget (key, 0, 0);
1029 AssertMsgReturn (mIPCSem >= 0,
1030 ("Cannot open IPC semaphore, errno=%d", errno),
1031 E_FAIL);
1032
1033 /* grab the semaphore */
1034 ::sembuf sop = { 0, -1, SEM_UNDO };
1035 int rv = ::semop (mIPCSem, &sop, 1);
1036 AssertMsgReturn (rv == 0,
1037 ("Cannot grab IPC semaphore, errno=%d", errno),
1038 E_FAIL);
1039
1040#else
1041# error "Port me!"
1042#endif
1043
1044 return rc;
1045}
1046
1047/** @note To be called only from #close() */
1048void Session::releaseIPCSemaphore()
1049{
1050 /* release the IPC semaphore */
1051#if defined(RT_OS_WINDOWS)
1052
1053 if (mIPCSem && mIPCThreadSem)
1054 {
1055 /*
1056 * tell the thread holding the IPC mutex to release it;
1057 * it will close mIPCSem handle
1058 */
1059 ::SetEvent (mIPCSem);
1060 /* wait for the thread to finish */
1061 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1062 ::CloseHandle (mIPCThreadSem);
1063
1064 mIPCThreadSem = NULL;
1065 mIPCSem = NULL;
1066 }
1067
1068#elif defined(RT_OS_OS2)
1069
1070 if (mIPCThread != NIL_RTTHREAD)
1071 {
1072 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1073
1074 /* tell the thread holding the IPC mutex to release it */
1075 int vrc = RTSemEventSignal (mIPCThreadSem);
1076 AssertRC(vrc == NO_ERROR);
1077
1078 /* wait for the thread to finish */
1079 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1080 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1081
1082 mIPCThread = NIL_RTTHREAD;
1083 }
1084
1085 if (mIPCThreadSem != NIL_RTSEMEVENT)
1086 {
1087 RTSemEventDestroy (mIPCThreadSem);
1088 mIPCThreadSem = NIL_RTSEMEVENT;
1089 }
1090
1091#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1092
1093 if (mIPCSem >= 0)
1094 {
1095 ::sembuf sop = { 0, 1, SEM_UNDO };
1096 ::semop (mIPCSem, &sop, 1);
1097
1098 mIPCSem = -1;
1099 }
1100
1101#else
1102# error "Port me!"
1103#endif
1104}
1105
1106#if defined(RT_OS_WINDOWS)
1107/** VM IPC mutex holder thread */
1108DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1109{
1110 LogFlowFuncEnter();
1111
1112 Assert (pvUser);
1113 void **data = (void **) pvUser;
1114
1115 BSTR sessionId = (BSTR)data[0];
1116 HANDLE initDoneSem = (HANDLE)data[1];
1117
1118 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1119 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1120
1121 if (ipcMutex)
1122 {
1123 /* grab the mutex */
1124 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1125 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1126 if (wrc == WAIT_OBJECT_0)
1127 {
1128 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1129 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1130 if (finishSem)
1131 {
1132 data[2] = (void*)finishSem;
1133 /* signal we're done with init */
1134 ::SetEvent (initDoneSem);
1135 /* wait until we're signaled to release the IPC mutex */
1136 ::WaitForSingleObject (finishSem, INFINITE);
1137 /* release the IPC mutex */
1138 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1139 BOOL success = ::ReleaseMutex (ipcMutex);
1140 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1141 ::CloseHandle (ipcMutex);
1142 ::CloseHandle (finishSem);
1143 }
1144 }
1145 }
1146
1147 /* signal we're done */
1148 ::SetEvent (initDoneSem);
1149
1150 LogFlowFuncLeave();
1151
1152 return 0;
1153}
1154#endif
1155
1156#if defined(RT_OS_OS2)
1157/** VM IPC mutex holder thread */
1158DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1159{
1160 LogFlowFuncEnter();
1161
1162 Assert (pvUser);
1163 void **data = (void **) pvUser;
1164
1165 Utf8Str ipcId = (BSTR)data[0];
1166 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1167
1168 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1169
1170 HMTX ipcMutex = NULLHANDLE;
1171 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1172 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1173
1174 if (arc == NO_ERROR)
1175 {
1176 /* grab the mutex */
1177 LogFlowFunc (("grabbing IPC mutex...\n"));
1178 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1179 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1180 if (arc == NO_ERROR)
1181 {
1182 /* store the answer */
1183 data[2] = (void*)true;
1184 /* signal we're done */
1185 int vrc = RTThreadUserSignal (Thread);
1186 AssertRC(vrc);
1187
1188 /* wait until we're signaled to release the IPC mutex */
1189 LogFlowFunc (("waiting for termination signal..\n"));
1190 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1191 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1192
1193 /* release the IPC mutex */
1194 LogFlowFunc (("releasing IPC mutex...\n"));
1195 arc = ::DosReleaseMutexSem (ipcMutex);
1196 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1197 }
1198
1199 ::DosCloseMutexSem (ipcMutex);
1200 }
1201
1202 /* store the answer */
1203 data[1] = (void*)false;
1204 /* signal we're done */
1205 int vrc = RTThreadUserSignal (Thread);
1206 AssertRC(vrc);
1207
1208 LogFlowFuncLeave();
1209
1210 return 0;
1211}
1212#endif
1213/* 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