VirtualBox

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

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

Main/Session+Console: By default, create a console with no sub-objects (saves resources). VM processes need to explicitly ask for a full console when before opening the session.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette