VirtualBox

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

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

Added 'restart' parameter to OnVRDPServerChange notification.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.1 KB
Line 
1/** @file
2 *
3 * VBox Client Session COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
19# include <errno.h>
20# include <sys/types.h>
21# include <sys/stat.h>
22# include <sys/ipc.h>
23# include <sys/sem.h>
24#endif
25
26#include "SessionImpl.h"
27#include "ConsoleImpl.h"
28#include "Global.h"
29
30#include "AutoCaller.h"
31#include "Logging.h"
32
33#include <VBox/err.h>
34#include <iprt/process.h>
35
36#if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)
37/** VM IPC mutex holder thread */
38static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser);
39#endif
40
41/**
42 * Local macro to check whether the session is open and return an error if not.
43 * @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this
44 * macro.
45 */
46#define CHECK_OPEN() \
47 do { \
48 if (mState != SessionState_Open) \
49 return setError(E_UNEXPECTED, tr ("The session is not open (session state: %s)"), Global::stringifySessionState(mState)); \
50 } while (0)
51
52// constructor / destructor
53/////////////////////////////////////////////////////////////////////////////
54
55HRESULT Session::FinalConstruct()
56{
57 LogFlowThisFunc(("\n"));
58
59 return init();
60}
61
62void Session::FinalRelease()
63{
64 LogFlowThisFunc(("\n"));
65
66 uninit(true /* aFinalRelease */);
67}
68
69// public initializer/uninitializer for internal purposes only
70/////////////////////////////////////////////////////////////////////////////
71
72/**
73 * Initializes the Session object.
74 */
75HRESULT Session::init()
76{
77 /* Enclose the state transition NotReady->InInit->Ready */
78 AutoInitSpan autoInitSpan(this);
79 AssertReturn(autoInitSpan.isOk(), E_FAIL);
80
81 LogFlowThisFuncEnter();
82
83 mState = SessionState_Closed;
84 mType = SessionType_Null;
85
86#if defined(RT_OS_WINDOWS)
87 mIPCSem = NULL;
88 mIPCThreadSem = NULL;
89#elif defined(RT_OS_OS2)
90 mIPCThread = NIL_RTTHREAD;
91 mIPCThreadSem = NIL_RTSEMEVENT;
92#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
93 mIPCSem = -1;
94#else
95# error "Port me!"
96#endif
97
98 /* Confirm a successful initialization when it's the case */
99 autoInitSpan.setSucceeded();
100
101 LogFlowThisFuncLeave();
102
103 return S_OK;
104}
105
106/**
107 * Uninitializes the Session object.
108 *
109 * @note Locks this object for writing.
110 */
111void Session::uninit(bool aFinalRelease)
112{
113 LogFlowThisFuncEnter();
114 LogFlowThisFunc(("aFinalRelease=%d\n", aFinalRelease));
115
116 /* Enclose the state transition Ready->InUninit->NotReady */
117 AutoUninitSpan autoUninitSpan(this);
118 if (autoUninitSpan.uninitDone())
119 {
120 LogFlowThisFunc(("Already uninitialized.\n"));
121 LogFlowThisFuncLeave();
122 return;
123 }
124
125 /* close() needs write lock */
126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
127
128 if (mState != SessionState_Closed)
129 {
130 Assert(mState == SessionState_Open ||
131 mState == SessionState_Spawning);
132
133 HRESULT rc = close(aFinalRelease, false /* aFromServer */);
134 AssertComRC(rc);
135 }
136
137 LogFlowThisFuncLeave();
138}
139
140// ISession properties
141/////////////////////////////////////////////////////////////////////////////
142
143STDMETHODIMP Session::COMGETTER(State)(SessionState_T *aState)
144{
145 CheckComArgOutPointerValid(aState);
146
147 AutoCaller autoCaller(this);
148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
149
150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
151
152 *aState = mState;
153
154 return S_OK;
155}
156
157STDMETHODIMP Session::COMGETTER(Type)(SessionType_T *aType)
158{
159 CheckComArgOutPointerValid(aType);
160
161 AutoCaller autoCaller(this);
162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
163
164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
165
166 CHECK_OPEN();
167
168 *aType = mType;
169 return S_OK;
170}
171
172STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine)
173{
174 CheckComArgOutPointerValid(aMachine);
175
176 AutoCaller autoCaller(this);
177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
178
179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
180
181 CHECK_OPEN();
182
183 HRESULT rc = 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(BOOL aRestart)
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(aRestart);
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
749STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
750 ULONG aSourceIdx, ULONG aTargetIdx,
751 IMedium *aSource, IMedium *aTarget,
752 BOOL aMergeForward,
753 IMedium *aParentForTarget,
754 ComSafeArrayIn(IMedium *, aChildrenToReparent),
755 IProgress *aProgress)
756{
757 AutoCaller autoCaller(this);
758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
759
760 if (mState != SessionState_Open)
761 return setError(VBOX_E_INVALID_VM_STATE,
762 tr("Machine session is not open (session state: %s)."),
763 Global::stringifySessionState(mState));
764 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
765 CheckComArgNotNull(aMediumAttachment);
766 CheckComArgSafeArrayNotNull(aChildrenToReparent);
767
768 return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
769 aTargetIdx, aSource, aTarget,
770 aMergeForward, aParentForTarget,
771 ComSafeArrayInArg(aChildrenToReparent),
772 aProgress);
773}
774
775
776// private methods
777///////////////////////////////////////////////////////////////////////////////
778
779/**
780 * Closes the current session.
781 *
782 * @param aFinalRelease called as a result of FinalRelease()
783 * @param aFromServer called as a result of Uninitialize()
784 *
785 * @note To be called only from #uninit(), #Close() or #Uninitialize().
786 * @note Locks this object for writing.
787 */
788HRESULT Session::close(bool aFinalRelease, bool aFromServer)
789{
790 LogFlowThisFuncEnter();
791 LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
792 aFinalRelease, aFromServer));
793
794 AutoCaller autoCaller(this);
795 AssertComRCReturnRC(autoCaller.rc());
796
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798
799 LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
800
801 if (mState != SessionState_Open)
802 {
803 Assert(mState == SessionState_Spawning);
804
805 /* The session object is going to be uninitialized before it has been
806 * assigned a direct console of the machine the client requested to open
807 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
808 * only if this close reqiest comes from the server (for example, it
809 * detected that the VM process it started terminated before opening a
810 * direct session). Otherwise, it means that the client is too fast and
811 * trying to close the session before waiting for the progress object it
812 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
813 Assert(aFromServer);
814
815 mState = SessionState_Closed;
816 mType = SessionType_Null;
817#if defined(RT_OS_WINDOWS)
818 Assert(!mIPCSem && !mIPCThreadSem);
819#elif defined(RT_OS_OS2)
820 Assert(mIPCThread == NIL_RTTHREAD &&
821 mIPCThreadSem == NIL_RTSEMEVENT);
822#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
823 Assert(mIPCSem == -1);
824#else
825# error "Port me!"
826#endif
827 LogFlowThisFuncLeave();
828 return S_OK;
829 }
830
831 /* go to the closing state */
832 mState = SessionState_Closing;
833
834 if (mType == SessionType_Direct)
835 {
836 mConsole->uninit();
837 mConsole.setNull();
838 }
839 else
840 {
841 mRemoteMachine.setNull();
842 mRemoteConsole.setNull();
843 }
844
845 ComPtr<IProgress> progress;
846
847 if (!aFinalRelease && !aFromServer)
848 {
849 /*
850 * We trigger OnSessionEnd() only when the session closes itself using
851 * Close(). Note that if isFinalRelease = TRUE here, this means that
852 * the client process has already initialized the termination procedure
853 * without issuing Close() and the IPC channel is no more operational --
854 * so we cannot call the server's method (it will definitely fail). The
855 * server will instead simply detect the abnormal client death (since
856 * OnSessionEnd() is not called) and reset the machine state to Aborted.
857 */
858
859 /*
860 * while waiting for OnSessionEnd() to complete one of our methods
861 * can be called by the server (for example, Uninitialize(), if the
862 * direct session has initiated a closure just a bit before us) so
863 * we need to release the lock to avoid deadlocks. The state is already
864 * SessionState_Closing here, so it's safe.
865 */
866 alock.leave();
867
868 LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
869 HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
870 LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
871
872 alock.enter();
873
874 /*
875 * If we get E_UNEXPECTED this means that the direct session has already
876 * been closed, we're just too late with our notification and nothing more
877 */
878 if (mType != SessionType_Direct && rc == E_UNEXPECTED)
879 rc = S_OK;
880
881 AssertComRC(rc);
882 }
883
884 mControl.setNull();
885
886 if (mType == SessionType_Direct)
887 {
888 releaseIPCSemaphore();
889 if (!aFinalRelease && !aFromServer)
890 {
891 /*
892 * Wait for the server to grab the semaphore and destroy the session
893 * machine (allowing us to open a new session with the same machine
894 * once this method returns)
895 */
896 Assert(!!progress);
897 if (progress)
898 progress->WaitForCompletion(-1);
899 }
900 }
901
902 mState = SessionState_Closed;
903 mType = SessionType_Null;
904
905 /* release the VirtualBox instance as the very last step */
906 mVirtualBox.setNull();
907
908 LogFlowThisFuncLeave();
909 return S_OK;
910}
911
912/** @note To be called only from #AssignMachine() */
913HRESULT Session::grabIPCSemaphore()
914{
915 HRESULT rc = E_FAIL;
916
917 /* open the IPC semaphore based on the sessionId and try to grab it */
918 Bstr ipcId;
919 rc = mControl->GetIPCId(ipcId.asOutParam());
920 AssertComRCReturnRC(rc);
921
922 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
923
924#if defined(RT_OS_WINDOWS)
925
926 /*
927 * Since Session is an MTA object, this method can be executed on
928 * any thread, and this thread will not necessarily match the thread on
929 * which close() will be called later. Therefore, we need a separate
930 * thread to hold the IPC mutex and then release it in close().
931 */
932
933 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
934 AssertMsgReturn(mIPCThreadSem,
935 ("Cannot create an event sem, err=%d", ::GetLastError()),
936 E_FAIL);
937
938 void *data[3];
939 data[0] = (void*)(BSTR)ipcId;
940 data[1] = (void*)mIPCThreadSem;
941 data[2] = 0; /* will get an output from the thread */
942
943 /* create a thread to hold the IPC mutex until signalled to release it */
944 RTTHREAD tid;
945 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
946 AssertRCReturn(vrc, E_FAIL);
947
948 /* wait until thread init is completed */
949 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
950 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
951 Assert(data[2]);
952
953 if (wrc == WAIT_OBJECT_0 && data[2])
954 {
955 /* memorize the event sem we should signal in close() */
956 mIPCSem = (HANDLE)data[2];
957 rc = S_OK;
958 }
959 else
960 {
961 ::CloseHandle(mIPCThreadSem);
962 mIPCThreadSem = NULL;
963 rc = E_FAIL;
964 }
965
966#elif defined(RT_OS_OS2)
967
968 /* We use XPCOM where any message (including close()) can arrive on any
969 * worker thread (which will not necessarily match this thread that opens
970 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
971 * and then release it in close(). */
972
973 int vrc = RTSemEventCreate(&mIPCThreadSem);
974 AssertRCReturn(vrc, E_FAIL);
975
976 void *data[3];
977 data[0] = (void*)ipcId.raw();
978 data[1] = (void*)mIPCThreadSem;
979 data[2] = (void*)false; /* will get the thread result here */
980
981 /* create a thread to hold the IPC mutex until signalled to release it */
982 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
983 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
984 AssertRCReturn(vrc, E_FAIL);
985
986 /* wait until thread init is completed */
987 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
988 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
989
990 /* the thread must succeed */
991 AssertReturn((bool)data[2], E_FAIL);
992
993#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
994
995# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
996 Utf8Str ipcKey = ipcId;
997 key_t key = RTStrToUInt32(ipcKey.raw());
998 AssertMsgReturn (key != 0,
999 ("Key value of 0 is not valid for IPC semaphore"),
1000 E_FAIL);
1001# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1002 Utf8Str semName = ipcId;
1003 char *pszSemName = NULL;
1004 RTStrUtf8ToCurrentCP (&pszSemName, semName);
1005 key_t key = ::ftok (pszSemName, 'V');
1006 RTStrFree (pszSemName);
1007# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
1008
1009 mIPCSem = ::semget (key, 0, 0);
1010 AssertMsgReturn (mIPCSem >= 0,
1011 ("Cannot open IPC semaphore, errno=%d", errno),
1012 E_FAIL);
1013
1014 /* grab the semaphore */
1015 ::sembuf sop = { 0, -1, SEM_UNDO };
1016 int rv = ::semop (mIPCSem, &sop, 1);
1017 AssertMsgReturn (rv == 0,
1018 ("Cannot grab IPC semaphore, errno=%d", errno),
1019 E_FAIL);
1020
1021#else
1022# error "Port me!"
1023#endif
1024
1025 return rc;
1026}
1027
1028/** @note To be called only from #close() */
1029void Session::releaseIPCSemaphore()
1030{
1031 /* release the IPC semaphore */
1032#if defined(RT_OS_WINDOWS)
1033
1034 if (mIPCSem && mIPCThreadSem)
1035 {
1036 /*
1037 * tell the thread holding the IPC mutex to release it;
1038 * it will close mIPCSem handle
1039 */
1040 ::SetEvent (mIPCSem);
1041 /* wait for the thread to finish */
1042 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1043 ::CloseHandle (mIPCThreadSem);
1044
1045 mIPCThreadSem = NULL;
1046 mIPCSem = NULL;
1047 }
1048
1049#elif defined(RT_OS_OS2)
1050
1051 if (mIPCThread != NIL_RTTHREAD)
1052 {
1053 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1054
1055 /* tell the thread holding the IPC mutex to release it */
1056 int vrc = RTSemEventSignal (mIPCThreadSem);
1057 AssertRC(vrc == NO_ERROR);
1058
1059 /* wait for the thread to finish */
1060 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1061 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1062
1063 mIPCThread = NIL_RTTHREAD;
1064 }
1065
1066 if (mIPCThreadSem != NIL_RTSEMEVENT)
1067 {
1068 RTSemEventDestroy (mIPCThreadSem);
1069 mIPCThreadSem = NIL_RTSEMEVENT;
1070 }
1071
1072#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1073
1074 if (mIPCSem >= 0)
1075 {
1076 ::sembuf sop = { 0, 1, SEM_UNDO };
1077 ::semop (mIPCSem, &sop, 1);
1078
1079 mIPCSem = -1;
1080 }
1081
1082#else
1083# error "Port me!"
1084#endif
1085}
1086
1087#if defined(RT_OS_WINDOWS)
1088/** VM IPC mutex holder thread */
1089DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1090{
1091 LogFlowFuncEnter();
1092
1093 Assert (pvUser);
1094 void **data = (void **) pvUser;
1095
1096 BSTR sessionId = (BSTR)data[0];
1097 HANDLE initDoneSem = (HANDLE)data[1];
1098
1099 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1100 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1101
1102 if (ipcMutex)
1103 {
1104 /* grab the mutex */
1105 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1106 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1107 if (wrc == WAIT_OBJECT_0)
1108 {
1109 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1110 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1111 if (finishSem)
1112 {
1113 data[2] = (void*)finishSem;
1114 /* signal we're done with init */
1115 ::SetEvent (initDoneSem);
1116 /* wait until we're signaled to release the IPC mutex */
1117 ::WaitForSingleObject (finishSem, INFINITE);
1118 /* release the IPC mutex */
1119 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1120 BOOL success = ::ReleaseMutex (ipcMutex);
1121 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1122 ::CloseHandle (ipcMutex);
1123 ::CloseHandle (finishSem);
1124 }
1125 }
1126 }
1127
1128 /* signal we're done */
1129 ::SetEvent (initDoneSem);
1130
1131 LogFlowFuncLeave();
1132
1133 return 0;
1134}
1135#endif
1136
1137#if defined(RT_OS_OS2)
1138/** VM IPC mutex holder thread */
1139DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1140{
1141 LogFlowFuncEnter();
1142
1143 Assert (pvUser);
1144 void **data = (void **) pvUser;
1145
1146 Utf8Str ipcId = (BSTR)data[0];
1147 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1148
1149 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1150
1151 HMTX ipcMutex = NULLHANDLE;
1152 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1153 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1154
1155 if (arc == NO_ERROR)
1156 {
1157 /* grab the mutex */
1158 LogFlowFunc (("grabbing IPC mutex...\n"));
1159 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1160 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1161 if (arc == NO_ERROR)
1162 {
1163 /* store the answer */
1164 data[2] = (void*)true;
1165 /* signal we're done */
1166 int vrc = RTThreadUserSignal (Thread);
1167 AssertRC(vrc);
1168
1169 /* wait until we're signaled to release the IPC mutex */
1170 LogFlowFunc (("waiting for termination signal..\n"));
1171 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1172 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1173
1174 /* release the IPC mutex */
1175 LogFlowFunc (("releasing IPC mutex...\n"));
1176 arc = ::DosReleaseMutexSem (ipcMutex);
1177 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1178 }
1179
1180 ::DosCloseMutexSem (ipcMutex);
1181 }
1182
1183 /* store the answer */
1184 data[1] = (void*)false;
1185 /* signal we're done */
1186 int vrc = RTThreadUserSignal (Thread);
1187 AssertRC(vrc);
1188
1189 LogFlowFuncLeave();
1190
1191 return 0;
1192}
1193#endif
1194/* 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