VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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