VirtualBox

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

Last change on this file since 33934 was 33825, checked in by vboxsync, 14 years ago

Main,NAT: Managing port-forwarding at runtime. (xTracker/#4835).

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