VirtualBox

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

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

Main: Bstr makeover (third 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.0 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, IN_BSTR aValue, IN_BSTR aFlags,
685 BOOL aIsSetter, BSTR *aRetValue, ULONG64 *aRetTimestamp, BSTR *aRetFlags)
686{
687#ifdef VBOX_WITH_GUEST_PROPS
688 AutoCaller autoCaller(this);
689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
690
691 if (mState != SessionState_Open)
692 return setError(VBOX_E_INVALID_VM_STATE,
693 tr("Machine session is not open (session state: %s)."),
694 Global::stringifySessionState(mState));
695 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
696 CheckComArgStrNotEmptyOrNull(aName);
697 if (!aIsSetter && !VALID_PTR(aRetValue))
698 return E_POINTER;
699 if (!aIsSetter && !VALID_PTR(aRetTimestamp))
700 return E_POINTER;
701 if (!aIsSetter && !VALID_PTR(aRetFlags))
702 return E_POINTER;
703 /* aValue can be NULL for a setter call if the property is to be deleted. */
704 if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
705 return E_INVALIDARG;
706 /* aFlags can be null if it is to be left as is */
707 if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
708 return E_INVALIDARG;
709 if (!aIsSetter)
710 return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
711 else
712 return mConsole->setGuestProperty(aName, aValue, aFlags);
713#else /* VBOX_WITH_GUEST_PROPS not defined */
714 ReturnComNotImplemented();
715#endif /* VBOX_WITH_GUEST_PROPS not defined */
716}
717
718STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
719 ComSafeArrayOut(BSTR, aNames),
720 ComSafeArrayOut(BSTR, aValues),
721 ComSafeArrayOut(ULONG64, aTimestamps),
722 ComSafeArrayOut(BSTR, aFlags))
723{
724#ifdef VBOX_WITH_GUEST_PROPS
725 AutoCaller autoCaller(this);
726 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
727
728 if (mState != SessionState_Open)
729 return setError(VBOX_E_INVALID_VM_STATE,
730 tr("Machine session is not open (session state: %s)."),
731 Global::stringifySessionState(mState));
732 AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
733 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
734 return E_POINTER;
735 if (ComSafeArrayOutIsNull(aNames))
736 return E_POINTER;
737 if (ComSafeArrayOutIsNull(aValues))
738 return E_POINTER;
739 if (ComSafeArrayOutIsNull(aTimestamps))
740 return E_POINTER;
741 if (ComSafeArrayOutIsNull(aFlags))
742 return E_POINTER;
743 return mConsole->enumerateGuestProperties(aPatterns,
744 ComSafeArrayOutArg(aNames),
745 ComSafeArrayOutArg(aValues),
746 ComSafeArrayOutArg(aTimestamps),
747 ComSafeArrayOutArg(aFlags));
748#else /* VBOX_WITH_GUEST_PROPS not defined */
749 ReturnComNotImplemented();
750#endif /* VBOX_WITH_GUEST_PROPS not defined */
751}
752
753// private methods
754///////////////////////////////////////////////////////////////////////////////
755
756/**
757 * Closes the current session.
758 *
759 * @param aFinalRelease called as a result of FinalRelease()
760 * @param aFromServer called as a result of Uninitialize()
761 *
762 * @note To be called only from #uninit(), #Close() or #Uninitialize().
763 * @note Locks this object for writing.
764 */
765HRESULT Session::close(bool aFinalRelease, bool aFromServer)
766{
767 LogFlowThisFuncEnter();
768 LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
769 aFinalRelease, aFromServer));
770
771 AutoCaller autoCaller(this);
772 AssertComRCReturnRC(autoCaller.rc());
773
774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
775
776 LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
777
778 if (mState != SessionState_Open)
779 {
780 Assert(mState == SessionState_Spawning);
781
782 /* The session object is going to be uninitialized before it has been
783 * assigned a direct console of the machine the client requested to open
784 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
785 * only if this close reqiest comes from the server (for example, it
786 * detected that the VM process it started terminated before opening a
787 * direct session). Otherwise, it means that the client is too fast and
788 * trying to close the session before waiting for the progress object it
789 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
790 Assert(aFromServer);
791
792 mState = SessionState_Closed;
793 mType = SessionType_Null;
794#if defined(RT_OS_WINDOWS)
795 Assert(!mIPCSem && !mIPCThreadSem);
796#elif defined(RT_OS_OS2)
797 Assert(mIPCThread == NIL_RTTHREAD &&
798 mIPCThreadSem == NIL_RTSEMEVENT);
799#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
800 Assert(mIPCSem == -1);
801#else
802# error "Port me!"
803#endif
804 LogFlowThisFuncLeave();
805 return S_OK;
806 }
807
808 /* go to the closing state */
809 mState = SessionState_Closing;
810
811 if (mType == SessionType_Direct)
812 {
813 mConsole->uninit();
814 mConsole.setNull();
815 }
816 else
817 {
818 mRemoteMachine.setNull();
819 mRemoteConsole.setNull();
820 }
821
822 ComPtr<IProgress> progress;
823
824 if (!aFinalRelease && !aFromServer)
825 {
826 /*
827 * We trigger OnSessionEnd() only when the session closes itself using
828 * Close(). Note that if isFinalRelease = TRUE here, this means that
829 * the client process has already initialized the termination procedure
830 * without issuing Close() and the IPC channel is no more operational --
831 * so we cannot call the server's method (it will definitely fail). The
832 * server will instead simply detect the abnormal client death (since
833 * OnSessionEnd() is not called) and reset the machine state to Aborted.
834 */
835
836 /*
837 * while waiting for OnSessionEnd() to complete one of our methods
838 * can be called by the server (for example, Uninitialize(), if the
839 * direct session has initiated a closure just a bit before us) so
840 * we need to release the lock to avoid deadlocks. The state is already
841 * SessionState_Closing here, so it's safe.
842 */
843 alock.leave();
844
845 LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
846 HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
847 LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
848
849 alock.enter();
850
851 /*
852 * If we get E_UNEXPECTED this means that the direct session has already
853 * been closed, we're just too late with our notification and nothing more
854 */
855 if (mType != SessionType_Direct && rc == E_UNEXPECTED)
856 rc = S_OK;
857
858 AssertComRC(rc);
859 }
860
861 mControl.setNull();
862
863 if (mType == SessionType_Direct)
864 {
865 releaseIPCSemaphore();
866 if (!aFinalRelease && !aFromServer)
867 {
868 /*
869 * Wait for the server to grab the semaphore and destroy the session
870 * machine (allowing us to open a new session with the same machine
871 * once this method returns)
872 */
873 Assert(!!progress);
874 if (progress)
875 progress->WaitForCompletion(-1);
876 }
877 }
878
879 mState = SessionState_Closed;
880 mType = SessionType_Null;
881
882 /* release the VirtualBox instance as the very last step */
883 mVirtualBox.setNull();
884
885 LogFlowThisFuncLeave();
886 return S_OK;
887}
888
889/** @note To be called only from #AssignMachine() */
890HRESULT Session::grabIPCSemaphore()
891{
892 HRESULT rc = E_FAIL;
893
894 /* open the IPC semaphore based on the sessionId and try to grab it */
895 Bstr ipcId;
896 rc = mControl->GetIPCId(ipcId.asOutParam());
897 AssertComRCReturnRC(rc);
898
899 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
900
901#if defined(RT_OS_WINDOWS)
902
903 /*
904 * Since Session is an MTA object, this method can be executed on
905 * any thread, and this thread will not necessarily match the thread on
906 * which close() will be called later. Therefore, we need a separate
907 * thread to hold the IPC mutex and then release it in close().
908 */
909
910 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
911 AssertMsgReturn(mIPCThreadSem,
912 ("Cannot create an event sem, err=%d", ::GetLastError()),
913 E_FAIL);
914
915 void *data[3];
916 data[0] = (void*)(BSTR)ipcId;
917 data[1] = (void*)mIPCThreadSem;
918 data[2] = 0; /* will get an output from the thread */
919
920 /* create a thread to hold the IPC mutex until signalled to release it */
921 RTTHREAD tid;
922 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
923 AssertRCReturn(vrc, E_FAIL);
924
925 /* wait until thread init is completed */
926 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
927 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
928 Assert(data[2]);
929
930 if (wrc == WAIT_OBJECT_0 && data[2])
931 {
932 /* memorize the event sem we should signal in close() */
933 mIPCSem = (HANDLE)data[2];
934 rc = S_OK;
935 }
936 else
937 {
938 ::CloseHandle(mIPCThreadSem);
939 mIPCThreadSem = NULL;
940 rc = E_FAIL;
941 }
942
943#elif defined(RT_OS_OS2)
944
945 /* We use XPCOM where any message (including close()) can arrive on any
946 * worker thread (which will not necessarily match this thread that opens
947 * the mutex). Therefore, we need a separate thread to hold the IPC mutex
948 * and then release it in close(). */
949
950 int vrc = RTSemEventCreate(&mIPCThreadSem);
951 AssertRCReturn(vrc, E_FAIL);
952
953 void *data[3];
954 data[0] = (void*)ipcId.raw();
955 data[1] = (void*)mIPCThreadSem;
956 data[2] = (void*)false; /* will get the thread result here */
957
958 /* create a thread to hold the IPC mutex until signalled to release it */
959 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
960 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
961 AssertRCReturn(vrc, E_FAIL);
962
963 /* wait until thread init is completed */
964 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
965 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
966
967 /* the thread must succeed */
968 AssertReturn((bool)data[2], E_FAIL);
969
970#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
971
972# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
973 Utf8Str ipcKey = ipcId;
974 key_t key = RTStrToUInt32(ipcKey.raw());
975 AssertMsgReturn (key != 0,
976 ("Key value of 0 is not valid for IPC semaphore"),
977 E_FAIL);
978# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
979 Utf8Str semName = ipcId;
980 char *pszSemName = NULL;
981 RTStrUtf8ToCurrentCP (&pszSemName, semName);
982 key_t key = ::ftok (pszSemName, 'V');
983 RTStrFree (pszSemName);
984# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
985
986 mIPCSem = ::semget (key, 0, 0);
987 AssertMsgReturn (mIPCSem >= 0,
988 ("Cannot open IPC semaphore, errno=%d", errno),
989 E_FAIL);
990
991 /* grab the semaphore */
992 ::sembuf sop = { 0, -1, SEM_UNDO };
993 int rv = ::semop (mIPCSem, &sop, 1);
994 AssertMsgReturn (rv == 0,
995 ("Cannot grab IPC semaphore, errno=%d", errno),
996 E_FAIL);
997
998#else
999# error "Port me!"
1000#endif
1001
1002 return rc;
1003}
1004
1005/** @note To be called only from #close() */
1006void Session::releaseIPCSemaphore()
1007{
1008 /* release the IPC semaphore */
1009#if defined(RT_OS_WINDOWS)
1010
1011 if (mIPCSem && mIPCThreadSem)
1012 {
1013 /*
1014 * tell the thread holding the IPC mutex to release it;
1015 * it will close mIPCSem handle
1016 */
1017 ::SetEvent (mIPCSem);
1018 /* wait for the thread to finish */
1019 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
1020 ::CloseHandle (mIPCThreadSem);
1021
1022 mIPCThreadSem = NULL;
1023 mIPCSem = NULL;
1024 }
1025
1026#elif defined(RT_OS_OS2)
1027
1028 if (mIPCThread != NIL_RTTHREAD)
1029 {
1030 Assert (mIPCThreadSem != NIL_RTSEMEVENT);
1031
1032 /* tell the thread holding the IPC mutex to release it */
1033 int vrc = RTSemEventSignal (mIPCThreadSem);
1034 AssertRC(vrc == NO_ERROR);
1035
1036 /* wait for the thread to finish */
1037 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
1038 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
1039
1040 mIPCThread = NIL_RTTHREAD;
1041 }
1042
1043 if (mIPCThreadSem != NIL_RTSEMEVENT)
1044 {
1045 RTSemEventDestroy (mIPCThreadSem);
1046 mIPCThreadSem = NIL_RTSEMEVENT;
1047 }
1048
1049#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
1050
1051 if (mIPCSem >= 0)
1052 {
1053 ::sembuf sop = { 0, 1, SEM_UNDO };
1054 ::semop (mIPCSem, &sop, 1);
1055
1056 mIPCSem = -1;
1057 }
1058
1059#else
1060# error "Port me!"
1061#endif
1062}
1063
1064#if defined(RT_OS_WINDOWS)
1065/** VM IPC mutex holder thread */
1066DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1067{
1068 LogFlowFuncEnter();
1069
1070 Assert (pvUser);
1071 void **data = (void **) pvUser;
1072
1073 BSTR sessionId = (BSTR)data[0];
1074 HANDLE initDoneSem = (HANDLE)data[1];
1075
1076 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
1077 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
1078
1079 if (ipcMutex)
1080 {
1081 /* grab the mutex */
1082 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
1083 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
1084 if (wrc == WAIT_OBJECT_0)
1085 {
1086 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
1087 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
1088 if (finishSem)
1089 {
1090 data[2] = (void*)finishSem;
1091 /* signal we're done with init */
1092 ::SetEvent (initDoneSem);
1093 /* wait until we're signaled to release the IPC mutex */
1094 ::WaitForSingleObject (finishSem, INFINITE);
1095 /* release the IPC mutex */
1096 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
1097 BOOL success = ::ReleaseMutex (ipcMutex);
1098 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
1099 ::CloseHandle (ipcMutex);
1100 ::CloseHandle (finishSem);
1101 }
1102 }
1103 }
1104
1105 /* signal we're done */
1106 ::SetEvent (initDoneSem);
1107
1108 LogFlowFuncLeave();
1109
1110 return 0;
1111}
1112#endif
1113
1114#if defined(RT_OS_OS2)
1115/** VM IPC mutex holder thread */
1116DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
1117{
1118 LogFlowFuncEnter();
1119
1120 Assert (pvUser);
1121 void **data = (void **) pvUser;
1122
1123 Utf8Str ipcId = (BSTR)data[0];
1124 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
1125
1126 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
1127
1128 HMTX ipcMutex = NULLHANDLE;
1129 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
1130 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
1131
1132 if (arc == NO_ERROR)
1133 {
1134 /* grab the mutex */
1135 LogFlowFunc (("grabbing IPC mutex...\n"));
1136 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
1137 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
1138 if (arc == NO_ERROR)
1139 {
1140 /* store the answer */
1141 data[2] = (void*)true;
1142 /* signal we're done */
1143 int vrc = RTThreadUserSignal (Thread);
1144 AssertRC(vrc);
1145
1146 /* wait until we're signaled to release the IPC mutex */
1147 LogFlowFunc (("waiting for termination signal..\n"));
1148 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
1149 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
1150
1151 /* release the IPC mutex */
1152 LogFlowFunc (("releasing IPC mutex...\n"));
1153 arc = ::DosReleaseMutexSem (ipcMutex);
1154 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
1155 }
1156
1157 ::DosCloseMutexSem (ipcMutex);
1158 }
1159
1160 /* store the answer */
1161 data[1] = (void*)false;
1162 /* signal we're done */
1163 int vrc = RTThreadUserSignal (Thread);
1164 AssertRC(vrc);
1165
1166 LogFlowFuncLeave();
1167
1168 return 0;
1169}
1170#endif
1171/* 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