~ubuntu-branches/ubuntu/quantal/virtualbox/quantal

« back to all changes in this revision

Viewing changes to .pc/36-tarball-respin.patch/src/VBox/Main/src-client/SessionImpl.cpp

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2012-04-05 12:41:55 UTC
  • mfrom: (3.1.12 sid)
  • Revision ID: package-import@ubuntu.com-20120405124155-i7b39tv5ddwhubbe
Tags: 4.1.12-dfsg-2
* Upstream has replaced the 4.1.12 tarball with a new one that fixes a
  crash when creating host only interfaces. (Closes: #667460)
  - Add 36-tarball-respin.patch which contains the diff between the old
    and the new tarball.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: SessionImpl.cpp $ */
 
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 */
 
38
static 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
 
 
55
HRESULT Session::FinalConstruct()
 
56
{
 
57
    LogFlowThisFunc(("\n"));
 
58
 
 
59
    HRESULT rc = init();
 
60
 
 
61
    BaseFinalConstruct();
 
62
 
 
63
    return rc;
 
64
}
 
65
 
 
66
void 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
 */
 
81
HRESULT 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
 */
 
117
void 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
 
 
148
STDMETHODIMP 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
 
 
162
STDMETHODIMP 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
 
 
177
STDMETHODIMP 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
 
 
207
STDMETHODIMP 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
 
 
241
STDMETHODIMP 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
 
 
259
STDMETHODIMP 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
 
 
274
STDMETHODIMP 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
 
 
302
STDMETHODIMP 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
 
 
370
STDMETHODIMP 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
 
 
443
STDMETHODIMP 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
 
 
474
STDMETHODIMP 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
        AssertReturn(mState == SessionState_Locked ||
 
496
                      mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
 
497
 
 
498
        /* close ourselves */
 
499
        rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
 
500
    }
 
501
    else if (autoCaller.state() == InUninit)
 
502
    {
 
503
        /*
 
504
         *  We might have already entered Session::uninit() at this point,
 
505
         *  return silently
 
506
         */
 
507
        LogFlowThisFunc(("Already uninitialized.\n"));
 
508
    }
 
509
    else
 
510
    {
 
511
        LogWarningThisFunc(("UNEXPECTED uninitialization!\n"));
 
512
        rc = autoCaller.rc();
 
513
    }
 
514
 
 
515
    LogFlowThisFunc(("rc=%08X\n", rc));
 
516
    LogFlowThisFuncLeave();
 
517
 
 
518
    return rc;
 
519
}
 
520
 
 
521
STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
 
522
{
 
523
    LogFlowThisFunc(("\n"));
 
524
 
 
525
    AutoCaller autoCaller(this);
 
526
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
527
 
 
528
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
529
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
530
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
531
 
 
532
    return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
 
533
}
 
534
 
 
535
STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
 
536
{
 
537
    LogFlowThisFunc(("\n"));
 
538
 
 
539
    AutoCaller autoCaller(this);
 
540
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
541
 
 
542
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
543
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
544
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
545
 
 
546
    return mConsole->onSerialPortChange(serialPort);
 
547
}
 
548
 
 
549
STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
 
550
{
 
551
    LogFlowThisFunc(("\n"));
 
552
 
 
553
    AutoCaller autoCaller(this);
 
554
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
555
 
 
556
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
557
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
558
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
559
 
 
560
    return mConsole->onParallelPortChange(parallelPort);
 
561
}
 
562
 
 
563
STDMETHODIMP Session::OnStorageControllerChange()
 
564
{
 
565
    LogFlowThisFunc(("\n"));
 
566
 
 
567
    AutoCaller autoCaller(this);
 
568
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
569
 
 
570
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
571
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
572
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
573
 
 
574
    return mConsole->onStorageControllerChange();
 
575
}
 
576
 
 
577
STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
 
578
{
 
579
    LogFlowThisFunc(("\n"));
 
580
 
 
581
    AutoCaller autoCaller(this);
 
582
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
583
 
 
584
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
585
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
586
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
587
 
 
588
    return mConsole->onMediumChange(aMediumAttachment, aForce);
 
589
}
 
590
 
 
591
STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
 
592
{
 
593
    LogFlowThisFunc(("\n"));
 
594
 
 
595
    AutoCaller autoCaller(this);
 
596
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
597
 
 
598
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
599
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
600
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
601
 
 
602
    return mConsole->onCPUChange(aCPU, aRemove);
 
603
}
 
604
 
 
605
STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
 
606
{
 
607
    LogFlowThisFunc(("\n"));
 
608
 
 
609
    AutoCaller autoCaller(this);
 
610
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
611
 
 
612
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
613
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
614
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
615
 
 
616
    return mConsole->onCPUExecutionCapChange(aExecutionCap);
 
617
}
 
618
 
 
619
STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
 
620
{
 
621
    LogFlowThisFunc(("\n"));
 
622
 
 
623
    AutoCaller autoCaller(this);
 
624
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
625
 
 
626
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
627
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
628
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
629
 
 
630
    return mConsole->onVRDEServerChange(aRestart);
 
631
}
 
632
 
 
633
STDMETHODIMP Session::OnUSBControllerChange()
 
634
{
 
635
    LogFlowThisFunc(("\n"));
 
636
 
 
637
    AutoCaller autoCaller(this);
 
638
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
639
 
 
640
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
641
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
642
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
643
 
 
644
    return mConsole->onUSBControllerChange();
 
645
}
 
646
 
 
647
STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
 
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_Locked, VBOX_E_INVALID_VM_STATE);
 
656
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
657
 
 
658
    return mConsole->onSharedFolderChange(aGlobal);
 
659
}
 
660
 
 
661
STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
 
662
                                        IVirtualBoxErrorInfo *aError,
 
663
                                        ULONG aMaskedIfs)
 
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->onUSBDeviceAttach(aDevice, aError, aMaskedIfs);
 
675
}
 
676
 
 
677
STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
 
678
                                        IVirtualBoxErrorInfo *aError)
 
679
{
 
680
    LogFlowThisFunc(("\n"));
 
681
 
 
682
    AutoCaller autoCaller(this);
 
683
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
684
 
 
685
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
686
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
687
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
688
 
 
689
    return mConsole->onUSBDeviceDetach(aId, aError);
 
690
}
 
691
 
 
692
STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
 
693
{
 
694
    AutoCaller autoCaller(this);
 
695
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
696
 
 
697
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
698
 
 
699
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
700
 
 
701
    if (mState != SessionState_Locked)
 
702
    {
 
703
        /* the call from Machine issued when the session is open can arrive
 
704
         * after the session starts closing or gets closed. Note that when
 
705
         * aCheck is false, we return E_FAIL to indicate that aWinId we return
 
706
         * is not valid */
 
707
        *aCanShow = FALSE;
 
708
        *aWinId = 0;
 
709
        return aCheck ? S_OK : E_FAIL;
 
710
    }
 
711
 
 
712
    return mConsole->onShowWindow(aCheck, aCanShow, aWinId);
 
713
}
 
714
 
 
715
STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
 
716
{
 
717
    LogFlowThisFunc(("\n"));
 
718
 
 
719
    AutoCaller autoCaller(this);
 
720
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
721
 
 
722
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
723
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
724
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
725
 
 
726
    return mConsole->onBandwidthGroupChange(aBandwidthGroup);
 
727
}
 
728
 
 
729
STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
 
730
{
 
731
    LogFlowThisFunc(("\n"));
 
732
 
 
733
    AutoCaller autoCaller(this);
 
734
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
735
 
 
736
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
737
    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
 
738
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
739
 
 
740
    return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove);
 
741
}
 
742
 
 
743
STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
 
744
                                          BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
 
745
{
 
746
#ifdef VBOX_WITH_GUEST_PROPS
 
747
    AutoCaller autoCaller(this);
 
748
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
749
 
 
750
    if (mState != SessionState_Locked)
 
751
        return setError(VBOX_E_INVALID_VM_STATE,
 
752
                        tr("Machine is not locked by session (session state: %s)."),
 
753
                        Global::stringifySessionState(mState));
 
754
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
755
    CheckComArgStrNotEmptyOrNull(aName);
 
756
    if (!aIsSetter && !VALID_PTR(aRetValue))
 
757
        return E_POINTER;
 
758
    if (!aIsSetter && !VALID_PTR(aRetTimestamp))
 
759
        return E_POINTER;
 
760
    if (!aIsSetter && !VALID_PTR(aRetFlags))
 
761
        return E_POINTER;
 
762
    /* aValue can be NULL for a setter call if the property is to be deleted. */
 
763
    if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
 
764
        return E_INVALIDARG;
 
765
    /* aFlags can be null if it is to be left as is */
 
766
    if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
 
767
        return E_INVALIDARG;
 
768
    if (!aIsSetter)
 
769
        return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
 
770
    else
 
771
        return mConsole->setGuestProperty(aName, aValue, aFlags);
 
772
#else /* VBOX_WITH_GUEST_PROPS not defined */
 
773
    ReturnComNotImplemented();
 
774
#endif /* VBOX_WITH_GUEST_PROPS not defined */
 
775
}
 
776
 
 
777
STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
 
778
                                               ComSafeArrayOut(BSTR, aNames),
 
779
                                               ComSafeArrayOut(BSTR, aValues),
 
780
                                               ComSafeArrayOut(LONG64, aTimestamps),
 
781
                                               ComSafeArrayOut(BSTR, aFlags))
 
782
{
 
783
#ifdef VBOX_WITH_GUEST_PROPS
 
784
    AutoCaller autoCaller(this);
 
785
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
786
 
 
787
    if (mState != SessionState_Locked)
 
788
        return setError(VBOX_E_INVALID_VM_STATE,
 
789
                        tr("Machine is not locked by session (session state: %s)."),
 
790
                        Global::stringifySessionState(mState));
 
791
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
792
    if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
 
793
        return E_POINTER;
 
794
    if (ComSafeArrayOutIsNull(aNames))
 
795
        return E_POINTER;
 
796
    if (ComSafeArrayOutIsNull(aValues))
 
797
        return E_POINTER;
 
798
    if (ComSafeArrayOutIsNull(aTimestamps))
 
799
        return E_POINTER;
 
800
    if (ComSafeArrayOutIsNull(aFlags))
 
801
        return E_POINTER;
 
802
    return mConsole->enumerateGuestProperties(aPatterns,
 
803
                                              ComSafeArrayOutArg(aNames),
 
804
                                              ComSafeArrayOutArg(aValues),
 
805
                                              ComSafeArrayOutArg(aTimestamps),
 
806
                                              ComSafeArrayOutArg(aFlags));
 
807
#else /* VBOX_WITH_GUEST_PROPS not defined */
 
808
    ReturnComNotImplemented();
 
809
#endif /* VBOX_WITH_GUEST_PROPS not defined */
 
810
}
 
811
 
 
812
STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
 
813
                                        ULONG aSourceIdx, ULONG aTargetIdx,
 
814
                                        IMedium *aSource, IMedium *aTarget,
 
815
                                        BOOL aMergeForward,
 
816
                                        IMedium *aParentForTarget,
 
817
                                        ComSafeArrayIn(IMedium *, aChildrenToReparent),
 
818
                                        IProgress *aProgress)
 
819
{
 
820
    AutoCaller autoCaller(this);
 
821
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
822
 
 
823
    if (mState != SessionState_Locked)
 
824
        return setError(VBOX_E_INVALID_VM_STATE,
 
825
                        tr("Machine is not locked by session (session state: %s)."),
 
826
                        Global::stringifySessionState(mState));
 
827
    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
 
828
    CheckComArgNotNull(aMediumAttachment);
 
829
    CheckComArgSafeArrayNotNull(aChildrenToReparent);
 
830
 
 
831
    return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
 
832
                                       aTargetIdx, aSource, aTarget,
 
833
                                       aMergeForward, aParentForTarget,
 
834
                                       ComSafeArrayInArg(aChildrenToReparent),
 
835
                                       aProgress);
 
836
}
 
837
 
 
838
STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable)
 
839
{
 
840
    AutoCaller autoCaller(this);
 
841
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
842
 
 
843
    mConsole->enableVMMStatistics(aEnable);
 
844
 
 
845
    return S_OK;
 
846
}
 
847
 
 
848
// private methods
 
849
///////////////////////////////////////////////////////////////////////////////
 
850
 
 
851
/**
 
852
 *  Unlocks a machine associated with the current session.
 
853
 *
 
854
 *  @param aFinalRelease    called as a result of FinalRelease()
 
855
 *  @param aFromServer      called as a result of Uninitialize()
 
856
 *
 
857
 *  @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize().
 
858
 *  @note Locks this object for writing.
 
859
 */
 
860
HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
 
861
{
 
862
    LogFlowThisFuncEnter();
 
863
    LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n",
 
864
                      aFinalRelease, aFromServer));
 
865
 
 
866
    AutoCaller autoCaller(this);
 
867
    AssertComRCReturnRC(autoCaller.rc());
 
868
 
 
869
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
870
 
 
871
    LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType));
 
872
 
 
873
    if (mState != SessionState_Locked)
 
874
    {
 
875
        Assert(mState == SessionState_Spawning);
 
876
 
 
877
        /* The session object is going to be uninitialized before it has been
 
878
         * assigned a direct console of the machine the client requested to open
 
879
         * a remote session to using IVirtualBox:: openRemoteSession(). It is OK
 
880
         * only if this close request comes from the server (for example, it
 
881
         * detected that the VM process it started terminated before opening a
 
882
         * direct session). Otherwise, it means that the client is too fast and
 
883
         * trying to close the session before waiting for the progress object it
 
884
         * got from IVirtualBox:: openRemoteSession() to complete, so assert. */
 
885
        Assert(aFromServer);
 
886
 
 
887
        mState = SessionState_Unlocked;
 
888
        mType = SessionType_Null;
 
889
#if defined(RT_OS_WINDOWS)
 
890
        Assert(!mIPCSem && !mIPCThreadSem);
 
891
#elif defined(RT_OS_OS2)
 
892
        Assert(mIPCThread == NIL_RTTHREAD &&
 
893
               mIPCThreadSem == NIL_RTSEMEVENT);
 
894
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
895
        Assert(mIPCSem == -1);
 
896
#else
 
897
# error "Port me!"
 
898
#endif
 
899
        LogFlowThisFuncLeave();
 
900
        return S_OK;
 
901
    }
 
902
 
 
903
    /* go to the closing state */
 
904
    mState = SessionState_Unlocking;
 
905
 
 
906
    if (mType == SessionType_WriteLock)
 
907
    {
 
908
        mConsole->uninit();
 
909
        mConsole.setNull();
 
910
    }
 
911
    else
 
912
    {
 
913
        mRemoteMachine.setNull();
 
914
        mRemoteConsole.setNull();
 
915
    }
 
916
 
 
917
    ComPtr<IProgress> progress;
 
918
 
 
919
    if (!aFinalRelease && !aFromServer)
 
920
    {
 
921
        /*
 
922
         *  We trigger OnSessionEnd() only when the session closes itself using
 
923
         *  Close(). Note that if isFinalRelease = TRUE here, this means that
 
924
         *  the client process has already initialized the termination procedure
 
925
         *  without issuing Close() and the IPC channel is no more operational --
 
926
         *  so we cannot call the server's method (it will definitely fail). The
 
927
         *  server will instead simply detect the abnormal client death (since
 
928
         *  OnSessionEnd() is not called) and reset the machine state to Aborted.
 
929
         */
 
930
 
 
931
        /*
 
932
         *  while waiting for OnSessionEnd() to complete one of our methods
 
933
         *  can be called by the server (for example, Uninitialize(), if the
 
934
         *  direct session has initiated a closure just a bit before us) so
 
935
         *  we need to release the lock to avoid deadlocks. The state is already
 
936
         *  SessionState_Closing here, so it's safe.
 
937
         */
 
938
        alock.leave();
 
939
 
 
940
        LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n"));
 
941
        HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam());
 
942
        LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc));
 
943
 
 
944
        alock.enter();
 
945
 
 
946
        /*
 
947
         *  If we get E_UNEXPECTED this means that the direct session has already
 
948
         *  been closed, we're just too late with our notification and nothing more
 
949
         *
 
950
         *  bird: Seems E_ACCESSDENIED is what gets returned these days; see
 
951
         *        VirtualBoxBase::addCaller.
 
952
         */
 
953
        if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
 
954
            rc = S_OK;
 
955
 
 
956
#ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */
 
957
        AssertComRC(rc);
 
958
#endif
 
959
    }
 
960
 
 
961
    mControl.setNull();
 
962
 
 
963
    if (mType == SessionType_WriteLock)
 
964
    {
 
965
        releaseIPCSemaphore();
 
966
        if (!aFinalRelease && !aFromServer)
 
967
        {
 
968
            /*
 
969
             *  Wait for the server to grab the semaphore and destroy the session
 
970
             *  machine (allowing us to open a new session with the same machine
 
971
             *  once this method returns)
 
972
             */
 
973
            Assert(!!progress);
 
974
            if (progress)
 
975
                progress->WaitForCompletion(-1);
 
976
        }
 
977
    }
 
978
 
 
979
    mState = SessionState_Unlocked;
 
980
    mType = SessionType_Null;
 
981
 
 
982
    /* release the VirtualBox instance as the very last step */
 
983
    mVirtualBox.setNull();
 
984
 
 
985
    LogFlowThisFuncLeave();
 
986
    return S_OK;
 
987
}
 
988
 
 
989
/** @note To be called only from #AssignMachine() */
 
990
HRESULT Session::grabIPCSemaphore()
 
991
{
 
992
    HRESULT rc = E_FAIL;
 
993
 
 
994
    /* open the IPC semaphore based on the sessionId and try to grab it */
 
995
    Bstr ipcId;
 
996
    rc = mControl->GetIPCId(ipcId.asOutParam());
 
997
    AssertComRCReturnRC(rc);
 
998
 
 
999
    LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
 
1000
 
 
1001
#if defined(RT_OS_WINDOWS)
 
1002
 
 
1003
    /*
 
1004
     *  Since Session is an MTA object, this method can be executed on
 
1005
     *  any thread, and this thread will not necessarily match the thread on
 
1006
     *  which close() will be called later. Therefore, we need a separate
 
1007
     *  thread to hold the IPC mutex and then release it in close().
 
1008
     */
 
1009
 
 
1010
    mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
 
1011
    AssertMsgReturn(mIPCThreadSem,
 
1012
                    ("Cannot create an event sem, err=%d", ::GetLastError()),
 
1013
                    E_FAIL);
 
1014
 
 
1015
    void *data[3];
 
1016
    data[0] = (void*)(BSTR)ipcId.raw();
 
1017
    data[1] = (void*)mIPCThreadSem;
 
1018
    data[2] = 0; /* will get an output from the thread */
 
1019
 
 
1020
    /* create a thread to hold the IPC mutex until signalled to release it */
 
1021
    RTTHREAD tid;
 
1022
    int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
 
1023
    AssertRCReturn(vrc, E_FAIL);
 
1024
 
 
1025
    /* wait until thread init is completed */
 
1026
    DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
 
1027
    AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
 
1028
    Assert(data[2]);
 
1029
 
 
1030
    if (wrc == WAIT_OBJECT_0 && data[2])
 
1031
    {
 
1032
        /* memorize the event sem we should signal in close() */
 
1033
        mIPCSem = (HANDLE)data[2];
 
1034
        rc = S_OK;
 
1035
    }
 
1036
    else
 
1037
    {
 
1038
        ::CloseHandle(mIPCThreadSem);
 
1039
        mIPCThreadSem = NULL;
 
1040
        rc = E_FAIL;
 
1041
    }
 
1042
 
 
1043
#elif defined(RT_OS_OS2)
 
1044
 
 
1045
    /* We use XPCOM where any message (including close()) can arrive on any
 
1046
     * worker thread (which will not necessarily match this thread that opens
 
1047
     * the mutex). Therefore, we need a separate thread to hold the IPC mutex
 
1048
     * and then release it in close(). */
 
1049
 
 
1050
    int vrc = RTSemEventCreate(&mIPCThreadSem);
 
1051
    AssertRCReturn(vrc, E_FAIL);
 
1052
 
 
1053
    void *data[3];
 
1054
    data[0] = (void*)ipcId.raw();
 
1055
    data[1] = (void*)mIPCThreadSem;
 
1056
    data[2] = (void*)false; /* will get the thread result here */
 
1057
 
 
1058
    /* create a thread to hold the IPC mutex until signalled to release it */
 
1059
    vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
 
1060
                         0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
 
1061
    AssertRCReturn(vrc, E_FAIL);
 
1062
 
 
1063
    /* wait until thread init is completed */
 
1064
    vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
 
1065
    AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
 
1066
 
 
1067
    /* the thread must succeed */
 
1068
    AssertReturn((bool)data[2], E_FAIL);
 
1069
 
 
1070
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
1071
 
 
1072
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
 
1073
    Utf8Str ipcKey = ipcId;
 
1074
    key_t key = RTStrToUInt32(ipcKey.c_str());
 
1075
    AssertMsgReturn (key != 0,
 
1076
                    ("Key value of 0 is not valid for IPC semaphore"),
 
1077
                    E_FAIL);
 
1078
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
1079
    Utf8Str semName = ipcId;
 
1080
    char *pszSemName = NULL;
 
1081
    RTStrUtf8ToCurrentCP (&pszSemName, semName);
 
1082
    key_t key = ::ftok (pszSemName, 'V');
 
1083
    RTStrFree (pszSemName);
 
1084
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
1085
 
 
1086
    mIPCSem = ::semget (key, 0, 0);
 
1087
    AssertMsgReturn (mIPCSem >= 0,
 
1088
                    ("Cannot open IPC semaphore, errno=%d", errno),
 
1089
                    E_FAIL);
 
1090
 
 
1091
    /* grab the semaphore */
 
1092
    ::sembuf sop = { 0,  -1, SEM_UNDO };
 
1093
    int rv = ::semop (mIPCSem, &sop, 1);
 
1094
    AssertMsgReturn (rv == 0,
 
1095
                    ("Cannot grab IPC semaphore, errno=%d", errno),
 
1096
                    E_FAIL);
 
1097
 
 
1098
#else
 
1099
# error "Port me!"
 
1100
#endif
 
1101
 
 
1102
    return rc;
 
1103
}
 
1104
 
 
1105
/** @note To be called only from #close() */
 
1106
void Session::releaseIPCSemaphore()
 
1107
{
 
1108
    /* release the IPC semaphore */
 
1109
#if defined(RT_OS_WINDOWS)
 
1110
 
 
1111
    if (mIPCSem && mIPCThreadSem)
 
1112
    {
 
1113
        /*
 
1114
         *  tell the thread holding the IPC mutex to release it;
 
1115
         *  it will close mIPCSem handle
 
1116
         */
 
1117
        ::SetEvent (mIPCSem);
 
1118
        /* wait for the thread to finish */
 
1119
        ::WaitForSingleObject (mIPCThreadSem, INFINITE);
 
1120
        ::CloseHandle (mIPCThreadSem);
 
1121
 
 
1122
        mIPCThreadSem = NULL;
 
1123
        mIPCSem = NULL;
 
1124
    }
 
1125
 
 
1126
#elif defined(RT_OS_OS2)
 
1127
 
 
1128
    if (mIPCThread != NIL_RTTHREAD)
 
1129
    {
 
1130
        Assert (mIPCThreadSem != NIL_RTSEMEVENT);
 
1131
 
 
1132
        /* tell the thread holding the IPC mutex to release it */
 
1133
        int vrc = RTSemEventSignal (mIPCThreadSem);
 
1134
        AssertRC(vrc == NO_ERROR);
 
1135
 
 
1136
        /* wait for the thread to finish */
 
1137
        vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
 
1138
        Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
 
1139
 
 
1140
        mIPCThread = NIL_RTTHREAD;
 
1141
    }
 
1142
 
 
1143
    if (mIPCThreadSem != NIL_RTSEMEVENT)
 
1144
    {
 
1145
        RTSemEventDestroy (mIPCThreadSem);
 
1146
        mIPCThreadSem = NIL_RTSEMEVENT;
 
1147
    }
 
1148
 
 
1149
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
1150
 
 
1151
    if (mIPCSem >= 0)
 
1152
    {
 
1153
        ::sembuf sop = { 0, 1, SEM_UNDO };
 
1154
        ::semop (mIPCSem, &sop, 1);
 
1155
 
 
1156
        mIPCSem = -1;
 
1157
    }
 
1158
 
 
1159
#else
 
1160
# error "Port me!"
 
1161
#endif
 
1162
}
 
1163
 
 
1164
#if defined(RT_OS_WINDOWS)
 
1165
/** VM IPC mutex holder thread */
 
1166
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
 
1167
{
 
1168
    LogFlowFuncEnter();
 
1169
 
 
1170
    Assert (pvUser);
 
1171
    void **data = (void **) pvUser;
 
1172
 
 
1173
    BSTR sessionId = (BSTR)data[0];
 
1174
    HANDLE initDoneSem = (HANDLE)data[1];
 
1175
 
 
1176
    HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
 
1177
    AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
 
1178
 
 
1179
    if (ipcMutex)
 
1180
    {
 
1181
        /* grab the mutex */
 
1182
        DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
 
1183
        AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
 
1184
        if (wrc == WAIT_OBJECT_0)
 
1185
        {
 
1186
            HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
 
1187
            AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
 
1188
            if (finishSem)
 
1189
            {
 
1190
                data[2] = (void*)finishSem;
 
1191
                /* signal we're done with init */
 
1192
                ::SetEvent (initDoneSem);
 
1193
                /* wait until we're signaled to release the IPC mutex */
 
1194
                ::WaitForSingleObject (finishSem, INFINITE);
 
1195
                /* release the IPC mutex */
 
1196
                LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
 
1197
                BOOL success = ::ReleaseMutex (ipcMutex);
 
1198
                AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
 
1199
                ::CloseHandle (ipcMutex);
 
1200
                ::CloseHandle (finishSem);
 
1201
            }
 
1202
        }
 
1203
    }
 
1204
 
 
1205
    /* signal we're done */
 
1206
    ::SetEvent (initDoneSem);
 
1207
 
 
1208
    LogFlowFuncLeave();
 
1209
 
 
1210
    return 0;
 
1211
}
 
1212
#endif
 
1213
 
 
1214
#if defined(RT_OS_OS2)
 
1215
/** VM IPC mutex holder thread */
 
1216
DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
 
1217
{
 
1218
    LogFlowFuncEnter();
 
1219
 
 
1220
    Assert (pvUser);
 
1221
    void **data = (void **) pvUser;
 
1222
 
 
1223
    Utf8Str ipcId = (BSTR)data[0];
 
1224
    RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
 
1225
 
 
1226
    LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
 
1227
 
 
1228
    HMTX ipcMutex = NULLHANDLE;
 
1229
    APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
 
1230
    AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
 
1231
 
 
1232
    if (arc == NO_ERROR)
 
1233
    {
 
1234
        /* grab the mutex */
 
1235
        LogFlowFunc (("grabbing IPC mutex...\n"));
 
1236
        arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
 
1237
        AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
 
1238
        if (arc == NO_ERROR)
 
1239
        {
 
1240
            /* store the answer */
 
1241
            data[2] = (void*)true;
 
1242
            /* signal we're done */
 
1243
            int vrc = RTThreadUserSignal (Thread);
 
1244
            AssertRC(vrc);
 
1245
 
 
1246
            /* wait until we're signaled to release the IPC mutex */
 
1247
            LogFlowFunc (("waiting for termination signal..\n"));
 
1248
            vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
 
1249
            Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
 
1250
 
 
1251
            /* release the IPC mutex */
 
1252
            LogFlowFunc (("releasing IPC mutex...\n"));
 
1253
            arc = ::DosReleaseMutexSem (ipcMutex);
 
1254
            AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
 
1255
        }
 
1256
 
 
1257
        ::DosCloseMutexSem (ipcMutex);
 
1258
    }
 
1259
 
 
1260
    /* store the answer */
 
1261
    data[1] = (void*)false;
 
1262
    /* signal we're done */
 
1263
    int vrc = RTThreadUserSignal (Thread);
 
1264
    AssertRC(vrc);
 
1265
 
 
1266
    LogFlowFuncLeave();
 
1267
 
 
1268
    return 0;
 
1269
}
 
1270
#endif
 
1271
/* vi: set tabstop=4 shiftwidth=4 expandtab: */