VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/SessionImpl.cpp@ 42213

Last change on this file since 42213 was 42084, checked in by vboxsync, 13 years ago

Guest Control 2.0: Hacking in progress.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette