ClientTokenHolder.cpp revision 5637660ed0c2a4a3a114e6d2d4c8294f2fd5f18f
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** @file
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * VirtualBox API client token holder (in the client process)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Copyright (C) 2006-2013 Oracle Corporation
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * available from http://www.virtualbox.org. This file is free software;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * you can redistribute it and/or modify it under the terms of the GNU
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * General Public License (GPL) as published by the Free Software
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <iprt/asm.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <iprt/assert.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <iprt/log.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <iprt/semaphore.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <iprt/process.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# include <errno.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# include <sys/types.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# include <sys/stat.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# include <sys/ipc.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# include <sys/sem.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include <VBox/com/defs.h>
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include "ClientTokenHolder.h"
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#include "SessionImpl.h"
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** client token holder thread */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncSession::ClientTokenHolder::ClientTokenHolder()
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
6dd8f5023a9ba7588212331db90059553136fe33vboxsync AssertReleaseFailed();
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
9127c416edfd6f9266e387f7abd7aa9904eecbc9vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncSession::ClientTokenHolder::~ClientTokenHolder()
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* release the client token */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#if defined(RT_OS_WINDOWS)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
9127c416edfd6f9266e387f7abd7aa9904eecbc9vboxsync if (mSem && mThreadSem)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * tell the thread holding the token to release it;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * it will close mSem handle
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::SetEvent(mSem);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* wait for the thread to finish */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::WaitForSingleObject(mThreadSem, INFINITE);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::CloseHandle(mThreadSem);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThreadSem = NULL;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mSem = NULL;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThread = NIL_RTTHREAD;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#elif defined(RT_OS_OS2)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (mThread != NIL_RTTHREAD)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Assert(mSem != NIL_RTSEMEVENT);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* tell the thread holding the token to release it */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync int vrc = RTSemEventSignal(mSem);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertRC(vrc == NO_ERROR);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* wait for the thread to finish */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThread = NIL_RTTHREAD;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (mSem != NIL_RTSEMEVENT)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync RTSemEventDestroy(mSem);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mSem = NIL_RTSEMEVENT;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (mSem >= 0)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::sembuf sop = { 0, 1, SEM_UNDO };
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::semop(mSem, &sop, 1);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mSem = -1;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#else
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync# error "Port me!"
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncSession::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) :
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mClientTokenId(strTokenId)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mSem = CTHSEMARG;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThread = NIL_RTTHREAD;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#if defined(RT_OS_WINDOWS)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThreadSem = CTHTHREADSEMARG;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Bstr bstrTokenId(strTokenId);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Since there is no guarantee that the constructor and destructor will be
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * called in the same thread, we need a separate thread to hold the token.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertMsgReturnVoid(mThreadSem,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ("Cannot create an event sem, err=%d", ::GetLastError()));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync void *data[3];
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync data[0] = (void*)(BSTR)bstrTokenId.raw();
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync data[1] = (void*)mThreadSem;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync data[2] = 0; /* will get an output from the thread */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* create a thread to hold the token until signalled to release it */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertRCReturnVoid(vrc);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* wait until thread init is completed */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Assert(data[2]);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (wrc == WAIT_OBJECT_0 && data[2])
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* memorize the event sem we should signal in close() */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mSem = (HANDLE)data[2];
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync else
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync ::CloseHandle(mThreadSem);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync mThreadSem = NULL;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#elif defined(RT_OS_OS2)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Bstr bstrTokenId(strTokenId);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Since there is no guarantee that the constructor and destructor will be
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * called in the same thread, we need a separate thread to hold the token.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
int vrc = RTSemEventCreate(&mSem);
AssertRCReturnVoid(vrc);
void *data[3];
data[0] = (void*)bstrTokenId.raw();
data[1] = (void*)mSem;
data[2] = (void*)false; /* will get the thread result here */
/* create a thread to hold the token until signalled to release it */
vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data,
0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
AssertRCReturnVoid(vrc);
/* wait until thread init is completed */
vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
/* the thread must succeed */
AssertReturnVoid((bool)data[2]);
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
key_t key = RTStrToUInt32(strTokenId.c_str());
AssertMsgReturnVoid(key != 0,
("Key value of 0 is not valid for client token"));
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
char *pszSemName = NULL;
RTStrUtf8ToCurrentCP(&pszSemName, strTokenId);
key_t key = ::ftok(pszSemName, 'V');
RTStrFree(pszSemName);
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
int s = ::semget(key, 0, 0);
AssertMsgReturnVoid(s >= 0,
("Cannot open semaphore, errno=%d", errno));
/* grab the semaphore */
::sembuf sop = { 0, -1, SEM_UNDO };
int rv = ::semop(s, &sop, 1);
AssertMsgReturnVoid(rv == 0,
("Cannot grab semaphore, errno=%d", errno));
mSem = s;
#else
# error "Port me!"
#endif
}
bool Session::ClientTokenHolder::isReady()
{
return mSem != CTHSEMARG;
}
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
/** client token holder thread */
DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser)
{
LogFlowFuncEnter();
Assert(pvUser);
void **data = (void **)pvUser;
# if defined(RT_OS_WINDOWS)
BSTR sessionId = (BSTR)data[0];
HANDLE initDoneSem = (HANDLE)data[1];
HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);
AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError()));
if (mutex)
{
/* grab the token */
DWORD wrc = ::WaitForSingleObject(mutex, 0);
AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc));
if (wrc == WAIT_OBJECT_0)
{
HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
if (finishSem)
{
data[2] = (void*)finishSem;
/* signal we're done with init */
::SetEvent(initDoneSem);
/* wait until we're signaled to release the token */
::WaitForSingleObject(finishSem, INFINITE);
/* release the token */
LogFlow(("ClientTokenHolderThread(): releasing token...\n"));
BOOL success = ::ReleaseMutex(mutex);
AssertMsg(success, ("cannot release token, err=%d\n", ::GetLastError()));
::CloseHandle(mutex);
::CloseHandle(finishSem);
}
}
}
/* signal we're done */
::SetEvent(initDoneSem);
# elif defined(RT_OS_OS2)
Utf8Str sessionId = (BSTR)data[0];
RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem));
HMTX mutex = NULLHANDLE;
APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex);
AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc));
if (arc == NO_ERROR)
{
/* grab the token */
LogFlowFunc(("grabbing token...\n"));
arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN);
AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc));
if (arc == NO_ERROR)
{
/* store the answer */
data[2] = (void*)true;
/* signal we're done */
int vrc = RTThreadUserSignal(Thread);
AssertRC(vrc);
/* wait until we're signaled to release the token */
LogFlowFunc(("waiting for termination signal..\n"));
vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT);
Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
/* release the token */
LogFlowFunc(("releasing token...\n"));
arc = ::DosReleaseMutexSem(mutex);
AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc));
}
::DosCloseMutexSem(mutex);
}
/* store the answer */
data[1] = (void*)false;
/* signal we're done */
int vrc = RTThreadUserSignal(Thread);
AssertRC(vrc);
# else
# error "Port me!"
# endif
LogFlowFuncLeave();
return 0;
}
#endif
/* vi: set tabstop=4 shiftwidth=4 expandtab: */