VirtualBox

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

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

Main: Bstr makeover (second attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull?() usage

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