VirtualBox

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

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

back out Main/Session+Console hack introducing full/reduced console

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