VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ClientTokenHolder.cpp@ 67414

Last change on this file since 67414 was 63239, checked in by vboxsync, 8 years ago

Main: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.6 KB
Line 
1/* $Id: ClientTokenHolder.cpp 63239 2016-08-10 09:39:08Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox API client session token holder (in the client process)
5 */
6
7/*
8 * Copyright (C) 2006-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/assert.h>
21#include <iprt/log.h>
22#include <iprt/semaphore.h>
23#include <iprt/process.h>
24
25#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
26# include <errno.h>
27# include <sys/types.h>
28# include <sys/stat.h>
29# include <sys/ipc.h>
30# include <sys/sem.h>
31#endif
32
33#include <VBox/com/defs.h>
34
35#include "ClientTokenHolder.h"
36#include "SessionImpl.h"
37
38
39#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
40/** client token holder thread */
41static DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD hThreadSelf, void *pvUser);
42#endif
43
44
45Session::ClientTokenHolder::ClientTokenHolder()
46{
47 AssertReleaseFailed();
48}
49
50Session::ClientTokenHolder::~ClientTokenHolder()
51{
52 /* release the client token */
53#if defined(RT_OS_WINDOWS)
54
55 if (mSem && mThreadSem)
56 {
57 /*
58 * tell the thread holding the token to release it;
59 * it will close mSem handle
60 */
61 ::SetEvent(mSem);
62 /* wait for the thread to finish */
63 ::WaitForSingleObject(mThreadSem, INFINITE);
64 ::CloseHandle(mThreadSem);
65
66 mThreadSem = NULL;
67 mSem = NULL;
68 mThread = NIL_RTTHREAD;
69 }
70
71#elif defined(RT_OS_OS2)
72
73 if (mThread != NIL_RTTHREAD)
74 {
75 Assert(mSem != NIL_RTSEMEVENT);
76
77 /* tell the thread holding the token to release it */
78 int vrc = RTSemEventSignal(mSem);
79 AssertRC(vrc == NO_ERROR);
80
81 /* wait for the thread to finish */
82 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
83 Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
84
85 mThread = NIL_RTTHREAD;
86 }
87
88 if (mSem != NIL_RTSEMEVENT)
89 {
90 RTSemEventDestroy(mSem);
91 mSem = NIL_RTSEMEVENT;
92 }
93
94#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
95
96 if (mSem >= 0)
97 {
98 ::sembuf sop = { 0, 1, SEM_UNDO };
99 ::semop(mSem, &sop, 1);
100
101 mSem = -1;
102 }
103
104#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
105
106 if (!mToken.isNull())
107 {
108 mToken->Abandon();
109 mToken.setNull();
110 }
111
112#else
113# error "Port me!"
114#endif
115}
116
117#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
118Session::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) :
119 mClientTokenId(strTokenId)
120#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
121Session::ClientTokenHolder::ClientTokenHolder(IToken *aToken) :
122 mToken(aToken)
123#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
124{
125#ifdef CTHSEMTYPE
126 mSem = CTHSEMARG;
127#endif
128#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
129 mThread = NIL_RTTHREAD;
130#endif
131
132#if defined(RT_OS_WINDOWS)
133 mThreadSem = CTHTHREADSEMARG;
134
135 Bstr bstrTokenId(strTokenId);
136
137 /*
138 * Since there is no guarantee that the constructor and destructor will be
139 * called in the same thread, we need a separate thread to hold the token.
140 */
141
142 mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
143 AssertMsgReturnVoid(mThreadSem,
144 ("Cannot create an event sem, err=%d", ::GetLastError()));
145
146 void *data[3];
147 data[0] = (void*)(BSTR)bstrTokenId.raw();
148 data[1] = (void*)mThreadSem;
149 data[2] = 0; /* will get an output from the thread */
150
151 /* create a thread to hold the token until signalled to release it */
152 int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
153 AssertRCReturnVoid(vrc);
154
155 /* wait until thread init is completed */
156 DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE);
157 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
158 Assert(data[2]);
159
160 if (wrc == WAIT_OBJECT_0 && data[2])
161 {
162 /* memorize the event sem we should signal in close() */
163 mSem = (HANDLE)data[2];
164 }
165 else
166 {
167 ::CloseHandle(mThreadSem);
168 mThreadSem = NULL;
169 }
170#elif defined(RT_OS_OS2)
171 Bstr bstrTokenId(strTokenId);
172
173 /*
174 * Since there is no guarantee that the constructor and destructor will be
175 * called in the same thread, we need a separate thread to hold the token.
176 */
177
178 int vrc = RTSemEventCreate(&mSem);
179 AssertRCReturnVoid(vrc);
180
181 void *data[3];
182 data[0] = (void*)bstrTokenId.raw();
183 data[1] = (void*)mSem;
184 data[2] = (void*)false; /* will get the thread result here */
185
186 /* create a thread to hold the token until signalled to release it */
187 vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data,
188 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
189 AssertRCReturnVoid(vrc);
190 /* wait until thread init is completed */
191 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
192 AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
193
194 /* the thread must succeed */
195 AssertReturnVoid((bool)data[2]);
196
197#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
198
199# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
200 key_t key = RTStrToUInt32(strTokenId.c_str());
201 AssertMsgReturnVoid(key != 0,
202 ("Key value of 0 is not valid for client token"));
203# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
204 char *pszSemName = NULL;
205 RTStrUtf8ToCurrentCP(&pszSemName, strTokenId);
206 key_t key = ::ftok(pszSemName, 'V');
207 RTStrFree(pszSemName);
208# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
209 int s = ::semget(key, 0, 0);
210 AssertMsgReturnVoid(s >= 0,
211 ("Cannot open semaphore, errno=%d", errno));
212
213 /* grab the semaphore */
214 ::sembuf sop = { 0, -1, SEM_UNDO };
215 int rv = ::semop(s, &sop, 1);
216 AssertMsgReturnVoid(rv == 0,
217 ("Cannot grab semaphore, errno=%d", errno));
218 mSem = s;
219
220#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
221
222 /* nothing to do */
223
224#else
225# error "Port me!"
226#endif
227}
228
229bool Session::ClientTokenHolder::isReady()
230{
231#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
232 return mSem != CTHSEMARG;
233#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
234 return !mToken.isNull();
235#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
236}
237
238#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
239/** client token holder thread */
240DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD hThreadSelf, void *pvUser)
241{
242 RT_NOREF(hThreadSelf);
243 LogFlowFuncEnter();
244
245 Assert(pvUser);
246
247 void **data = (void **)pvUser;
248
249# if defined(RT_OS_WINDOWS)
250 BSTR sessionId = (BSTR)data[0];
251 HANDLE initDoneSem = (HANDLE)data[1];
252
253 HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);
254 AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError()));
255
256 if (mutex)
257 {
258 /* grab the token */
259 DWORD wrc = ::WaitForSingleObject(mutex, 0);
260 AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc));
261 if (wrc == WAIT_OBJECT_0)
262 {
263 HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
264 AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
265 if (finishSem)
266 {
267 data[2] = (void*)finishSem;
268 /* signal we're done with init */
269 ::SetEvent(initDoneSem);
270 /* wait until we're signaled to release the token */
271 ::WaitForSingleObject(finishSem, INFINITE);
272 /* release the token */
273 LogFlow(("ClientTokenHolderThread(): releasing token...\n"));
274 BOOL fRc = ::ReleaseMutex(mutex);
275 AssertMsg(fRc, ("cannot release token, err=%d\n", ::GetLastError())); NOREF(fRc);
276 ::CloseHandle(mutex);
277 ::CloseHandle(finishSem);
278 }
279 }
280 }
281
282 /* signal we're done */
283 ::SetEvent(initDoneSem);
284# elif defined(RT_OS_OS2)
285 Utf8Str sessionId = (BSTR)data[0];
286 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
287
288 LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem));
289
290 HMTX mutex = NULLHANDLE;
291 APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex);
292 AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc));
293
294 if (arc == NO_ERROR)
295 {
296 /* grab the token */
297 LogFlowFunc(("grabbing token...\n"));
298 arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN);
299 AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc));
300 if (arc == NO_ERROR)
301 {
302 /* store the answer */
303 data[2] = (void*)true;
304 /* signal we're done */
305 int vrc = RTThreadUserSignal(Thread);
306 AssertRC(vrc);
307
308 /* wait until we're signaled to release the token */
309 LogFlowFunc(("waiting for termination signal..\n"));
310 vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT);
311 Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
312
313 /* release the token */
314 LogFlowFunc(("releasing token...\n"));
315 arc = ::DosReleaseMutexSem(mutex);
316 AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc));
317 }
318 ::DosCloseMutexSem(mutex);
319 }
320
321 /* store the answer */
322 data[1] = (void*)false;
323 /* signal we're done */
324 int vrc = RTThreadUserSignal(Thread);
325 AssertRC(vrc);
326# else
327# error "Port me!"
328# endif
329
330 LogFlowFuncLeave();
331
332 return 0;
333}
334#endif
335
336/* 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