ClientToken.cpp revision 5637660ed0c2a4a3a114e6d2d4c8294f2fd5f18f
/** @file
*
* VirtualBox API client crash token handling
*/
/*
* Copyright (C) 2004-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <iprt/asm.h>
#include <iprt/assert.h>
#include <iprt/log.h>
#include <iprt/semaphore.h>
#include <iprt/process.h>
#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
# include <errno.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/ipc.h>
# include <sys/sem.h>
#endif
#include <VBox/com/defs.h>
#include <vector>
#include "VirtualBoxBase.h"
#include "AutoCaller.h"
#include "ClientToken.h"
#include "MachineImpl.h"
Machine::ClientToken::ClientToken()
{
AssertReleaseFailed();
}
Machine::ClientToken::~ClientToken()
{
#if defined(RT_OS_WINDOWS)
if (mClientToken)
::CloseHandle(mClientToken);
#elif defined(RT_OS_OS2)
if (mClientToken != NULLHANDLE)
::DosCloseMutexSem(mClientToken);
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
if (mClientToken >= 0)
::semctl(mClientToken, 0, IPC_RMID);
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
mClientTokenId = "0";
# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
#else
# error "Port me!"
#endif
mClientToken = CTTOKENARG;
}
Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine) :
mMachine(pMachine)
{
#if defined(RT_OS_WINDOWS)
Bstr tokenId = pMachine->mData->m_strConfigFileFull;
for (size_t i = 0; i < tokenId.length(); i++)
if (tokenId.raw()[i] == '\\')
tokenId.raw()[i] = '/';
mClientToken = ::CreateMutex(NULL, FALSE, tokenId.raw());
mClientTokenId = tokenId;
AssertMsg(mClientToken,
("Cannot create token '%s', err=%d",
mClientTokenId.c_str(), ::GetLastError()));
#elif defined(RT_OS_OS2)
Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
pMachine->mData->mUuid.raw());
mClientTokenId = ipcSem;
APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE);
AssertMsg(arc == NO_ERROR,
("Cannot create token '%s', arc=%ld",
ipcSem.c_str(), arc));
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
/** @todo Check that this still works correctly. */
AssertCompileSize(key_t, 8);
# else
AssertCompileSize(key_t, 4);
# endif
key_t key;
mClientToken = -1;
mClientTokenId = "0";
for (uint32_t i = 0; i < 1 << 24; i++)
{
key = ((uint32_t)'V' << 24) | i;
int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
if (sem >= 0 || (errno != EEXIST && errno != EACCES))
{
mClientToken = sem;
if (sem >= 0)
mClientTokenId = BstrFmt("%u", key);
break;
}
}
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
Utf8Str semName = pMachine->mData->m_strConfigFileFull;
char *pszSemName = NULL;
RTStrUtf8ToCurrentCP(&pszSemName, semName);
key_t key = ::ftok(pszSemName, 'V');
RTStrFree(pszSemName);
mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
int errnoSave = errno;
if (mClientToken < 0 && errnoSave == ENOSYS)
{
mMachine->setError(E_FAIL,
tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
"support for SysV IPC. Check the host kernel configuration for "
"CONFIG_SYSVIPC=y"));
mClientToken = CTTOKENARG;
return;
}
/* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
* the token */
if (mClientToken < 0 && errnoSave == ENOSPC)
{
#ifdef RT_OS_LINUX
mMachine->setError(E_FAIL,
tr("Cannot create IPC semaphore because the system limit for the "
"maximum number of semaphore sets (SEMMNI), or the system wide "
"maximum number of semaphores (SEMMNS) would be exceeded. The "
"current set of SysV IPC semaphores can be determined from "
"the file /proc/sysvipc/sem"));
#else
mMachine->setError(E_FAIL,
tr("Cannot create IPC semaphore because the system-imposed limit "
"on the maximum number of allowed semaphores or semaphore "
"identifiers system-wide would be exceeded"));
#endif
mClientToken = CTTOKENARG;
return;
}
AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave));
/* set the initial value to 1 */
int rv = ::semctl(mClientToken, 0, SETVAL, 1);
errnoSave = errno;
if (rv != 0)
{
::semctl(mClientToken, 0, IPC_RMID);
mClientToken = CTTOKENARG;
AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave));
}
#else
# error "Port me!"
#endif
}
bool Machine::ClientToken::isReady()
{
return mClientToken != CTTOKENARG;
}
void Machine::ClientToken::getId(Utf8Str &strId)
{
strId = mClientTokenId;
}
CTTOKENTYPE Machine::ClientToken::getToken()
{
return mClientToken;
}
bool Machine::ClientToken::release()
{
bool terminated = false;
#if defined(RT_OS_WINDOWS)
AssertMsg(mClientToken, ("semaphore must be created"));
/* release the token */
::ReleaseMutex(mClientToken);
terminated = true;
#elif defined(RT_OS_OS2)
AssertMsg(mClientToken, ("semaphore must be created"));
/* release the token */
::DosReleaseMutexSem(mClientToken);
terminated = true;
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
AssertMsg(mClientToken >= 0, ("semaphore must be created"));
int val = ::semctl(mClientToken, 0, GETVAL);
if (val > 0)
{
/* the semaphore is signaled, meaning the session is terminated */
terminated = true;
}
#else
# error "Port me!"
#endif
return terminated;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */