VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ClientToken.cpp@ 50213

Last change on this file since 50213 was 48431, checked in by vboxsync, 11 years ago

Main/Machine+Session: New generic client session watcher implementation based on token objects, works on all platforms and is used for now on XPCOM. Additionally a better error message when several API clients are racing for a lock, previously it could be quite confusing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.1 KB
Line 
1/** @file
2 *
3 * VirtualBox API client session crash token handling
4 */
5
6/*
7 * Copyright (C) 2004-2013 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#include <iprt/asm.h>
19#include <iprt/assert.h>
20#include <iprt/log.h>
21#include <iprt/semaphore.h>
22#include <iprt/process.h>
23
24#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
25# include <errno.h>
26# include <sys/types.h>
27# include <sys/stat.h>
28# include <sys/ipc.h>
29# include <sys/sem.h>
30#endif
31
32#include <VBox/com/defs.h>
33
34#include <vector>
35
36#include "VirtualBoxBase.h"
37#include "AutoCaller.h"
38#include "ClientToken.h"
39#include "MachineImpl.h"
40
41Machine::ClientToken::ClientToken()
42{
43 AssertReleaseFailed();
44}
45
46Machine::ClientToken::~ClientToken()
47{
48#if defined(RT_OS_WINDOWS)
49 if (mClientToken)
50 ::CloseHandle(mClientToken);
51#elif defined(RT_OS_OS2)
52 if (mClientToken != NULLHANDLE)
53 ::DosCloseMutexSem(mClientToken);
54#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
55 if (mClientToken >= 0)
56 ::semctl(mClientToken, 0, IPC_RMID);
57# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
58 mClientTokenId = "0";
59# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
60#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
61 /* release the token, uses reference counting */
62 if (mClientToken)
63 {
64 if (!mClientTokenPassed)
65 mClientToken->Release();
66 mClientToken = NULL;
67 }
68#else
69# error "Port me!"
70#endif
71 mClientToken = CTTOKENARG;
72}
73
74Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine,
75 SessionMachine *pSessionMachine) :
76 mMachine(pMachine)
77{
78#if defined(RT_OS_WINDOWS)
79 NOREF(pSessionMachine);
80 Bstr tokenId = pMachine->mData->m_strConfigFileFull;
81 for (size_t i = 0; i < tokenId.length(); i++)
82 if (tokenId.raw()[i] == '\\')
83 tokenId.raw()[i] = '/';
84 mClientToken = ::CreateMutex(NULL, FALSE, tokenId.raw());
85 mClientTokenId = tokenId;
86 AssertMsg(mClientToken,
87 ("Cannot create token '%s', err=%d",
88 mClientTokenId.c_str(), ::GetLastError()));
89#elif defined(RT_OS_OS2)
90 NOREF(pSessionMachine);
91 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
92 pMachine->mData->mUuid.raw());
93 mClientTokenId = ipcSem;
94 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE);
95 AssertMsg(arc == NO_ERROR,
96 ("Cannot create token '%s', arc=%ld",
97 ipcSem.c_str(), arc));
98#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
99 NOREF(pSessionMachine);
100# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
101# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
102 /** @todo Check that this still works correctly. */
103 AssertCompileSize(key_t, 8);
104# else
105 AssertCompileSize(key_t, 4);
106# endif
107 key_t key;
108 mClientToken = -1;
109 mClientTokenId = "0";
110 for (uint32_t i = 0; i < 1 << 24; i++)
111 {
112 key = ((uint32_t)'V' << 24) | i;
113 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
114 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
115 {
116 mClientToken = sem;
117 if (sem >= 0)
118 mClientTokenId = BstrFmt("%u", key);
119 break;
120 }
121 }
122# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
123 Utf8Str semName = pMachine->mData->m_strConfigFileFull;
124 char *pszSemName = NULL;
125 RTStrUtf8ToCurrentCP(&pszSemName, semName);
126 key_t key = ::ftok(pszSemName, 'V');
127 RTStrFree(pszSemName);
128
129 mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
130# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
131
132 int errnoSave = errno;
133 if (mClientToken < 0 && errnoSave == ENOSYS)
134 {
135 mMachine->setError(E_FAIL,
136 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
137 "support for SysV IPC. Check the host kernel configuration for "
138 "CONFIG_SYSVIPC=y"));
139 mClientToken = CTTOKENARG;
140 return;
141 }
142 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
143 * the token */
144 if (mClientToken < 0 && errnoSave == ENOSPC)
145 {
146#ifdef RT_OS_LINUX
147 mMachine->setError(E_FAIL,
148 tr("Cannot create IPC semaphore because the system limit for the "
149 "maximum number of semaphore sets (SEMMNI), or the system wide "
150 "maximum number of semaphores (SEMMNS) would be exceeded. The "
151 "current set of SysV IPC semaphores can be determined from "
152 "the file /proc/sysvipc/sem"));
153#else
154 mMachine->setError(E_FAIL,
155 tr("Cannot create IPC semaphore because the system-imposed limit "
156 "on the maximum number of allowed semaphores or semaphore "
157 "identifiers system-wide would be exceeded"));
158#endif
159 mClientToken = CTTOKENARG;
160 return;
161 }
162 AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave));
163 /* set the initial value to 1 */
164 int rv = ::semctl(mClientToken, 0, SETVAL, 1);
165 errnoSave = errno;
166 if (rv != 0)
167 {
168 ::semctl(mClientToken, 0, IPC_RMID);
169 mClientToken = CTTOKENARG;
170 AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave));
171 }
172#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
173 ComObjPtr<MachineToken> pToken;
174 HRESULT rc = pToken.createObject();
175 if (SUCCEEDED(rc))
176 {
177 rc = pToken->init(pSessionMachine);
178 if (SUCCEEDED(rc))
179 {
180 mClientToken = pToken;
181 if (mClientToken)
182 {
183 rc = mClientToken->AddRef();
184 if (FAILED(rc))
185 mClientToken = NULL;
186 }
187 }
188 }
189 pToken.setNull();
190 mClientTokenPassed = false;
191 /* mClientTokenId isn't really used */
192 mClientTokenId = pMachine->mData->m_strConfigFileFull;
193 AssertMsg(mClientToken,
194 ("Cannot create token '%s', rc=%Rhrc",
195 mClientTokenId.c_str(), rc));
196#else
197# error "Port me!"
198#endif
199}
200
201bool Machine::ClientToken::isReady()
202{
203 return mClientToken != CTTOKENARG;
204}
205
206void Machine::ClientToken::getId(Utf8Str &strId)
207{
208 strId = mClientTokenId;
209}
210
211CTTOKENTYPE Machine::ClientToken::getToken()
212{
213#ifdef VBOX_WITH_GENERIC_SESSION_WATCHER
214 mClientTokenPassed = true;
215#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
216 return mClientToken;
217}
218
219#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
220bool Machine::ClientToken::release()
221{
222 bool terminated = false;
223
224#if defined(RT_OS_WINDOWS)
225 AssertMsg(mClientToken, ("semaphore must be created"));
226
227 /* release the token */
228 ::ReleaseMutex(mClientToken);
229 terminated = true;
230#elif defined(RT_OS_OS2)
231 AssertMsg(mClientToken, ("semaphore must be created"));
232
233 /* release the token */
234 ::DosReleaseMutexSem(mClientToken);
235 terminated = true;
236#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
237 AssertMsg(mClientToken >= 0, ("semaphore must be created"));
238 int val = ::semctl(mClientToken, 0, GETVAL);
239 if (val > 0)
240 {
241 /* the semaphore is signaled, meaning the session is terminated */
242 terminated = true;
243 }
244#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
245 /** @todo r=klaus never tested, this code is not reached */
246 AssertMsg(mClientToken, ("token must be created"));
247 /* release the token, uses reference counting */
248 if (mClientToken)
249 {
250 if (!mClientTokenPassed)
251 mClientToken->Release();
252 mClientToken = NULL;
253 }
254 terminated = true;
255#else
256# error "Port me!"
257#endif
258 return terminated;
259}
260#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
261
262/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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