VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp@ 18499

Last change on this file since 18499 was 11725, checked in by vboxsync, 16 years ago

#3076: Merged in the branch with the alternate driver authentication method. (34468:HEAD)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 34.9 KB
Line 
1/* $Id: SUPSvcGrant.cpp 11725 2008-08-27 22:21:47Z vboxsync $ */
2/** @file
3 * VirtualBox Support Service - The Grant Service.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#define LOG_GROUP LOG_GROUP_SUP
35#include "SUPSvcInternal.h"
36
37#include <VBox/log.h>
38#include <iprt/asm.h>
39#include <iprt/err.h>
40#include <iprt/assert.h>
41#include <iprt/critsect.h>
42#include <iprt/mem.h>
43#include <iprt/semaphore.h>
44#include <iprt/thread.h>
45#include <iprt/time.h>
46#include <iprt/localipc.h>
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/** Pointer to a client instance. */
53typedef struct SUPSVCGRANTSESSION *PSUPSVCGRANTSESSION;
54/** Pointer to a Grant service instance. */
55typedef struct SUPSVCGRANT *PSUPSVCGRANT;
56
57
58/**
59 * Grant service session data.
60 */
61typedef struct SUPSVCGRANTSESSION
62{
63 /** Pointer to the next client in the list. */
64 PSUPSVCGRANTSESSION pNext;
65 /** Pointer to the previous client in the list. */
66 PSUPSVCGRANTSESSION pPrev;
67 /** Pointer to the parent (the service instance). */
68 PSUPSVCGRANT volatile pParent;
69 /** The local ipc client handle. */
70 RTLOCALIPCSESSION volatile hSession;
71 /** Indicate that the thread should terminate ASAP. */
72 bool volatile fTerminate;
73 /** The thread handle. */
74 RTTHREAD hThread;
75
76} SUPSVCGRANTSESSION;
77
78
79/**
80 * State grant service machine.
81 */
82typedef enum SUPSVCGRANTSTATE
83{
84 /** The invalid zero entry. */
85 kSupSvcGrantState_Invalid = 0,
86 /** Creating - the thread is being started.
87 * Next: Paused or Butchered. */
88 kSupSvcGrantState_Creating,
89 /** Paused - the thread is blocked on it's user event semaphore.
90 * Next: Resuming, Terminating or Butchered.
91 * Prev: Creating, Pausing */
92 kSupSvcGrantState_Paused,
93 /** Resuming - the thread is being unblocked and ushered into RTLocalIpcServiceListen.
94 * Next: Listen or Butchered.
95 * Prev: Paused */
96 kSupSvcGrantState_Resuming,
97 /** Listen - the thread is in RTLocalIpcServerListen or setting up an incoming session.
98 * Next: Pausing or Butchered.
99 * Prev: Resuming */
100 kSupSvcGrantState_Listen,
101 /** Pausing - Cancelling the listen and dropping any incoming sessions.
102 * Next: Paused or Butchered.
103 * Prev: Listen */
104 kSupSvcGrantState_Pausing,
105 /** Butchered - The thread has quit because something when terribly wrong.
106 * Next: Destroyed
107 * Prev: Any. */
108 kSupSvcGrantState_Butchered,
109 /** Pausing - Cancelling the listen and dropping any incoming sessions.
110 * Next: Destroyed
111 * Prev: Paused */
112 kSupSvcGrantState_Terminating,
113 /** Destroyed - the instance is invalid.
114 * Prev: Butchered or Terminating */
115 kSupSvcGrantState_Destroyed,
116 /** The end of valid state values. */
117 kSupSvcGrantState_End,
118 /** The usual 32-bit blowup hack. */
119 kSupSvcGrantState_32BitHack = 0x7fffffff
120} SUPSVCGRANTSTATE;
121
122
123/**
124 * Grant service instance data.
125 */
126typedef struct SUPSVCGRANT
127{
128 /** The local ipc server handle. */
129 RTLOCALIPCSERVER hServer;
130
131 /** Critical section serializing access to the session list, the state,
132 * the response event, the session event, and the thread event. */
133 RTCRITSECT CritSect;
134 /** The service thread will signal this event when it has changed to
135 * the 'paused' or 'running' state. */
136 RTSEMEVENT hResponseEvent;
137 /** Event that's signaled on session termination. */
138 RTSEMEVENT hSessionEvent;
139 /** The handle to the service thread. */
140 RTTHREAD hThread;
141 /** Head of the session list. */
142 PSUPSVCGRANTSESSION volatile pSessionHead;
143 /** The service state. */
144 SUPSVCGRANTSTATE volatile enmState;
145
146 /** Critical section serializing access to the SUPR3HardenedVerify APIs. */
147 RTCRITSECT VerifyCritSect;
148} SUPSVCGRANT;
149
150
151/*******************************************************************************
152* Internal Functions *
153*******************************************************************************/
154static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState);
155
156
157
158
159/**
160 * Services a client session.
161 *
162 * @returns VINF_SUCCESS.
163 *
164 * @param hThread The thread handle.
165 * @param pvSession Pointer to the session instance data.
166 */
167static DECLCALLBACK(int) supSvcGrantSessionThread(RTTHREAD hThread, void *pvSession)
168{
169 PSUPSVCGRANTSESSION pThis = (PSUPSVCGRANTSESSION)pvSession;
170 RTLOCALIPCSESSION hSession = pThis->hSession;
171 Log(("supSvcGrantSessionThread(%p):\n", pThis));
172
173 /*
174 * Process client requests untill it quits or we're cancelled on termination.
175 */
176 while (!ASMAtomicUoReadBool(&pThis->fTerminate))
177 {
178 RTThreadSleep(1000);
179 /** @todo */
180 }
181
182 /*
183 * Clean up the session.
184 */
185 PSUPSVCGRANT pParent = (PSUPSVCGRANT)ASMAtomicReadPtr((void * volatile *)&pThis->pParent);
186 if (pParent)
187 RTCritSectEnter(&pParent->CritSect);
188 else
189 Log(("supSvcGrantSessionThread(%p): No parent\n", pThis));
190
191 ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession);
192 if (hSession != NIL_RTLOCALIPCSESSION)
193 RTLocalIpcSessionClose(hSession);
194 else
195 Log(("supSvcGrantSessionThread(%p): No session handle\n", pThis));
196
197 if (pParent)
198 {
199 RTSemEventSignal(pParent->hSessionEvent);
200 RTCritSectLeave(&pParent->CritSect);
201 }
202 Log(("supSvcGrantSessionThread(%p): exits\n"));
203 return VINF_SUCCESS;
204}
205
206
207/**
208 * Cleans up a session.
209 *
210 * This is called while inside the grant service critical section.
211 *
212 * @param pThis The session to destroy.
213 * @param pParent The parent.
214 */
215static void supSvcGrantSessionDestroy(PSUPSVCGRANTSESSION pThis, PSUPSVCGRANT pParent)
216{
217 /*
218 * Unlink it.
219 */
220 if (pThis->pNext)
221 {
222 Assert(pThis->pNext->pPrev == pThis);
223 pThis->pNext->pPrev = pThis->pPrev;
224 }
225
226 if (pThis->pPrev)
227 {
228 Assert(pThis->pPrev->pNext == pThis);
229 pThis->pPrev->pNext = pThis->pNext;
230 }
231 else if (pParent->pSessionHead == pThis)
232 pParent->pSessionHead = pThis->pNext;
233
234 /*
235 * Free the resources associated with it.
236 */
237 pThis->hThread = NIL_RTTHREAD;
238 pThis->pNext = NULL;
239 pThis->pPrev = NULL;
240
241 RTLOCALIPCSESSION hSession;
242 ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession);
243 if (hSession != NIL_RTLOCALIPCSESSION)
244 RTLocalIpcSessionClose(hSession);
245
246 RTMemFree(pThis);
247}
248
249
250/**
251 * Cleans up zombie sessions, locked.
252 *
253 * @param pThis Pointer to the grant service instance data.
254 */
255static void supSvcGrantCleanUpSessionsLocked(PSUPSVCGRANT pThis)
256{
257 /*
258 * Iterate untill be make it all the way thru the list.
259 *
260 * Only use the thread state as and indicator on whether we can destroy
261 * the session or not.
262 */
263 PSUPSVCGRANTSESSION pCur;
264 do
265 {
266 for (pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
267 {
268 int rc = RTThreadWait(pCur->hThread, 0, NULL);
269 if (RT_SUCCESS(rc))
270 {
271 supSvcGrantSessionDestroy(pCur, pThis);
272 break;
273 }
274
275 Assert(rc == VERR_TIMEOUT);
276 Assert(pCur->hThread != NIL_RTTHREAD);
277 Assert(pCur->pNext != pThis->pSessionHead);
278 }
279 } while (pCur);
280}
281
282
283/**
284 * Cleans up zombie sessions.
285 *
286 * @returns VINF_SUCCESS, VBox error code on internal error.
287 *
288 * @param pThis Pointer to the grant service instance data.
289 * @param fOwnCritSect Whether we own the crit sect already. The state is preserved.
290 */
291static int supSvcGrantCleanUpSessions(PSUPSVCGRANT pThis, bool fOwnCritSect)
292{
293 int rc = RTCritSectEnter(&pThis->CritSect);
294 if (RT_FAILURE(rc))
295 {
296 supSvcLogError("supSvcGrantCleanUpSessions: RTCritSectEnter returns %Rrc", rc);
297 return rc;
298 }
299
300 supSvcGrantCleanUpSessionsLocked(pThis);
301
302 RTCritSectLeave(&pThis->CritSect);
303 return VINF_SUCCESS;
304}
305
306
307/**
308 * Gets the state name.
309 *
310 * @returns The state name string (read only).
311 * @param enmState The state.
312 */
313static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState)
314{
315 switch (enmState)
316 {
317 case kSupSvcGrantState_Invalid: return "Invalid";
318 case kSupSvcGrantState_Creating: return "Creating";
319 case kSupSvcGrantState_Paused: return "Paused";
320 case kSupSvcGrantState_Resuming: return "Resuming";
321 case kSupSvcGrantState_Listen: return "Listen";
322 case kSupSvcGrantState_Pausing: return "Pausing";
323 case kSupSvcGrantState_Butchered: return "Butchered";
324 case kSupSvcGrantState_Terminating: return "Terminating";
325 case kSupSvcGrantState_Destroyed: return "Destroyed";
326 default: return "?Unknown?";
327 }
328}
329
330
331/**
332 * Attempts to flip into the butchered state.
333 *
334 * @returns rc.
335 * @param pThis The instance data.
336 * @param fOwnCritSect Whether we own the crit sect already.
337 * @param pszFailed What failed.
338 * @param rc What to return (lazy bird).
339 */
340static int supSvcGrantThreadButchered(PSUPSVCGRANT pThis, bool fOwnCritSect, const char *pszFailed, int rc)
341{
342 int rc2 = VINF_SUCCESS;
343 if (!fOwnCritSect)
344 rc2 = RTCritSectEnter(&pThis->CritSect);
345 if (RT_SUCCESS(rc2))
346 {
347 supSvcLogError("supSvcGrantThread(%s): Butchered; %Rrc: %s",
348 supSvcGrantStateName(pThis->enmState), rc, pszFailed);
349 pThis->enmState = kSupSvcGrantState_Butchered;
350
351 RTCritSectLeave(&pThis->CritSect);
352 }
353 return rc;
354}
355
356
357/**
358 * Creates a new session.
359 *
360 * @returns VINF_SUCCESS on success, VBox error code on internal error.
361 *
362 * @param pThis Pointer to the grant service instance data.
363 * @param hSession The client session handle.
364 */
365static int supSvcGrantThreadCreateSession(PSUPSVCGRANT pThis, RTLOCALIPCSESSION hSession)
366{
367 /*
368 * Allocate and initialize a new session instance before entering the critsect.
369 */
370 PSUPSVCGRANTSESSION pSession = (PSUPSVCGRANTSESSION)RTMemAlloc(sizeof(*pSession));
371 if (!pSession)
372 {
373 supSvcLogError("supSvcGrantThreadListen: failed to allocate session");
374 return VINF_SUCCESS; /* not fatal? */
375 }
376 pSession->pPrev = NULL;
377 pSession->pNext = NULL;
378 pSession->pParent = pThis;
379 pSession->hSession = hSession;
380 pSession->fTerminate = false;
381 pSession->hThread = NIL_RTTHREAD;
382
383 /*
384 * Enter the critsect, check the state, link it and fire off the session thread.
385 */
386 int rc = RTCritSectEnter(&pThis->CritSect);
387 if (RT_SUCCESS(rc))
388 {
389 /* check the state */
390 SUPSVCGRANTSTATE enmState = pThis->enmState;
391 if (enmState == kSupSvcGrantState_Listen)
392 {
393 /* link it */
394 pSession->pNext = pThis->pSessionHead;
395 if (pThis->pSessionHead)
396 pThis->pSessionHead->pPrev = pSession;
397 pThis->pSessionHead = pSession;
398
399 /* fire up the thread */
400 Log(("supSvcGrantThreadListen: starting session %p\n", pSession));
401 rc = RTThreadCreate(&pSession->hThread, supSvcGrantSessionThread, pSession, 0,
402 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "SESSION");
403 if (RT_SUCCESS(rc))
404 {
405 rc = RTCritSectLeave(&pThis->CritSect);
406 if (RT_FAILURE(rc))
407 return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectLeave", rc);
408
409 /*
410 * Successfully handled the client.
411 */
412 return VINF_SUCCESS;
413 }
414
415 /* bail out */
416 supSvcLogError("supSvcGrantThreadListen: RTThreadCreate returns %Rrc", rc);
417 }
418 else
419 Log(("supSvcGrantThreadListen: dropping connection, state %s\n", supSvcGrantStateName(enmState)));
420
421 RTCritSectLeave(&pThis->CritSect);
422 rc = VINF_SUCCESS;
423 }
424 else
425 supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectEnter", rc);
426 RTLocalIpcSessionClose(hSession);
427 RTMemFree(pSession);
428 return rc;
429}
430
431
432/**
433 * Listen for a client session and kicks off the service thread for it.
434 *
435 * @returns VINF_SUCCESS on normal state change, failure if something gets screwed up.
436 *
437 * @param pThis Pointer to the grant service instance data.
438 */
439static int supSvcGrantThreadListen(PSUPSVCGRANT pThis)
440{
441 /*
442 * Wait for a client to connect and create a new session.
443 */
444 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
445 int rc = RTLocalIpcServerListen(pThis->hServer, &hClientSession);
446 if (RT_FAILURE(rc))
447 {
448 if (rc == VERR_CANCELLED)
449 LogFlow(("supSvcGrantThreadListen: cancelled\n"));
450 else if (rc == VERR_TRY_AGAIN)
451 /* for testing */;
452 else
453 return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTLocalIpcServerListen", rc);
454 return VINF_SUCCESS;
455 }
456
457 return supSvcGrantThreadCreateSession(pThis, hClientSession);
458}
459
460
461/**
462 * Grant service thread.
463 *
464 * This thread is the one listening for clients and kicks off
465 * the session threads and stuff.
466 *
467 * @returns VINF_SUCCESS on normal exit, VBox error status on failure.
468 * @param hThread The thread handle.
469 * @param pvThis Pointer to the grant service instance data.
470 */
471static DECLCALLBACK(int) supSvcGrantThread(RTTHREAD hThread, void *pvThis)
472{
473 PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvThis;
474
475 /*
476 * The state loop.
477 */
478 for (;;)
479 {
480 /*
481 * Switch on the current state (requires critsect).
482 */
483 int rc = RTCritSectEnter(&pThis->CritSect);
484 if (RT_FAILURE(rc))
485 {
486 supSvcLogError("supSvcGrantThread - RTCritSectEnter returns %Rrc", rc);
487 return rc;
488 }
489
490 SUPSVCGRANTSTATE enmState = pThis->enmState;
491 LogFlow(("supSvcGrantThread: switching %s\n", supSvcGrantStateName(enmState)));
492 switch (enmState)
493 {
494 case kSupSvcGrantState_Creating:
495 case kSupSvcGrantState_Pausing:
496 pThis->enmState = kSupSvcGrantState_Paused;
497 rc = RTSemEventSignal(pThis->hResponseEvent);
498 if (RT_FAILURE(rc))
499 return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc);
500 /* fall thru */
501
502 case kSupSvcGrantState_Paused:
503 RTCritSectLeave(&pThis->CritSect);
504
505 rc = RTThreadUserWait(hThread, 60*1000); /* wake up once in a while (paranoia) */
506 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
507 return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect*/, "RTThreadUserWait", rc);
508 break;
509
510 case kSupSvcGrantState_Resuming:
511 pThis->enmState = kSupSvcGrantState_Listen;
512 rc = RTSemEventSignal(pThis->hResponseEvent);
513 if (RT_FAILURE(rc))
514 return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc);
515 /* fall thru */
516
517 case kSupSvcGrantState_Listen:
518 RTCritSectLeave(&pThis->CritSect);
519 rc = supSvcGrantThreadListen(pThis);
520 if (RT_FAILURE(rc))
521 {
522 Log(("supSvcGrantThread: supSvcGrantDoListening returns %Rrc, exiting\n", rc));
523 return rc;
524 }
525 break;
526
527 case kSupSvcGrantState_Terminating:
528 RTCritSectLeave(&pThis->CritSect);
529 Log(("supSvcGrantThread: Done\n"));
530 return VINF_SUCCESS;
531
532 case kSupSvcGrantState_Butchered:
533 default:
534 return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "Bad state", VERR_INTERNAL_ERROR);
535 }
536
537 /*
538 * Massage the session list between clients and states.
539 */
540 rc = supSvcGrantCleanUpSessions(pThis, false /* fOwnCritSect */);
541 if (RT_FAILURE(rc))
542 return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "supSvcGrantCleanUpSessions", rc);
543 }
544}
545
546
547/**
548 * Waits for the service thread to respond to a state change.
549 *
550 * @returns VINF_SUCCESS on success, VERR_TIMEOUT if it doesn't respond in time, other error code on internal error.
551 *
552 * @param pThis Pointer to the grant service instance data.
553 * @param enmCurState The current state.
554 * @param enmNewState The new state we're waiting for it to enter.
555 */
556static int supSvcGrantWait(PSUPSVCGRANT pThis, SUPSVCGRANTSTATE enmCurState, SUPSVCGRANTSTATE enmNewState)
557{
558 LogFlow(("supSvcGrantWait(,%s,%s): enter\n",
559 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)));
560
561 /*
562 * Wait a short while for the response event to be set.
563 */
564 RTSemEventWait(pThis->hResponseEvent, 1000);
565 int rc = RTCritSectEnter(&pThis->CritSect);
566 if (RT_SUCCESS(rc))
567 {
568 if (pThis->enmState == enmNewState)
569 {
570 RTCritSectLeave(&pThis->CritSect);
571 rc = VINF_SUCCESS;
572 }
573 else if (pThis->enmState == enmCurState)
574 {
575 /*
576 * Wait good while longer.
577 */
578 RTCritSectLeave(&pThis->CritSect);
579 rc = RTSemEventWait(pThis->hResponseEvent, 59*1000); /* 59 sec */
580 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
581 {
582 rc = RTCritSectEnter(&pThis->CritSect);
583 if (RT_SUCCESS(rc))
584 {
585 /*
586 * Check the state whether we've succeeded.
587 */
588 SUPSVCGRANTSTATE enmState = pThis->enmState;
589 if (enmState == enmNewState)
590 rc = VINF_SUCCESS;
591 else if (enmState == enmCurState)
592 {
593 supSvcLogError("supSvcGrantWait(,%s,%s) - the thread doesn't respond in a timely manner, failing.",
594 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
595 rc = VERR_TIMEOUT;
596 }
597 else
598 {
599 supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState),
600 supSvcGrantStateName(enmNewState), supSvcGrantStateName(enmState));
601 AssertMsgFailed(("%s\n", supSvcGrantStateName(enmState)));
602 rc = VERR_INTERNAL_ERROR;
603 }
604
605 RTCritSectLeave(&pThis->CritSect);
606 }
607 else
608 supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
609 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
610 }
611 else
612 supSvcLogError("supSvcGrantWait(,%s,%s) - RTSemEventWait returns %Rrc",
613 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
614 }
615 else
616 {
617 supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState),
618 supSvcGrantStateName(enmNewState), supSvcGrantStateName(pThis->enmState));
619 AssertMsgFailed(("%s\n", supSvcGrantStateName(pThis->enmState)));
620 RTCritSectLeave(&pThis->CritSect);
621 rc = VERR_INTERNAL_ERROR;
622 }
623 }
624 else
625 supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
626 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
627
628 Log(("supSvcGrantWait(,%s,%s): returns %Rrc\n",
629 supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState), rc));
630 return rc;
631}
632
633
634/** @copydoc SUPSVCSERVICE::pfnCreate */
635DECLCALLBACK(int) supSvcGrantCreate(void **ppvInstance)
636{
637 LogFlowFuncEnter();
638
639 /*
640 * Allocate and initialize the session data.
641 */
642 PSUPSVCGRANT pThis = (PSUPSVCGRANT)RTMemAlloc(sizeof(*pThis));
643 if (!pThis)
644 {
645 supSvcLogError("supSvcGrantCreate - no memory");
646 return VERR_NO_MEMORY;
647 }
648 bool fFreeIt = true;
649 pThis->pSessionHead = NULL;
650 pThis->enmState = kSupSvcGrantState_Creating;
651 int rc = RTCritSectInit(&pThis->VerifyCritSect);
652 if (RT_SUCCESS(rc))
653 {
654 rc = RTCritSectInit(&pThis->CritSect);
655 if (RT_SUCCESS(rc))
656 {
657 rc = RTSemEventCreate(&pThis->hResponseEvent);
658 if (RT_SUCCESS(rc))
659 {
660 rc = RTSemEventCreate(&pThis->hSessionEvent);
661 if (RT_SUCCESS(rc))
662 {
663 /*
664 * Create the local IPC instance and then finally fire up the thread.
665 */
666 rc = RTLocalIpcServerCreate(&pThis->hServer, SUPSVC_GRANT_SERVICE_NAME, RTLOCALIPC_FLAGS_MULTI_SESSION);
667 if (RT_SUCCESS(rc))
668 {
669 rc = RTThreadCreate(&pThis->hThread, supSvcGrantThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GRANT");
670 if (RT_SUCCESS(rc))
671 {
672 rc = supSvcGrantWait(pThis, kSupSvcGrantState_Creating, kSupSvcGrantState_Paused);
673 if (RT_SUCCESS(rc))
674 {
675 /*
676 * Successfully created the grant service!
677 */
678 Log(("supSvcGrantCreate: returns VINF_SUCCESS (pThis=%p)\n", pThis));
679 *ppvInstance = pThis;
680 return VINF_SUCCESS;
681 }
682
683 /*
684 * The thread FAILED to start in a timely manner!
685 */
686 RTCritSectEnter(&pThis->CritSect);
687 pThis->enmState = kSupSvcGrantState_Terminating;
688 RTCritSectLeave(&pThis->CritSect);
689
690 RTThreadUserSignal(pThis->hThread);
691
692 int cTries = 10;
693 int rc2 = RTThreadWait(pThis->hThread, 20000, NULL);
694 if (RT_FAILURE(rc2))
695 {
696 /* poke it a few more times before giving up. */
697 while (--cTries > 0)
698 {
699 RTThreadUserSignal(pThis->hThread);
700 RTLocalIpcServerCancel(pThis->hServer);
701 if (RTThreadWait(pThis->hThread, 1000, NULL) != VERR_TIMEOUT)
702 break;
703 }
704 }
705 fFreeIt = cTries <= 0;
706 }
707 else
708 supSvcLogError("supSvcGrantCreate - RTThreadCreate returns %Rrc", rc);
709 RTLocalIpcServerDestroy(pThis->hServer);
710 pThis->hServer = NIL_RTLOCALIPCSERVER;
711 }
712 else
713 supSvcLogError("supSvcGrantCreate - RTLocalIpcServiceCreate returns %Rrc", rc);
714 RTSemEventDestroy(pThis->hSessionEvent);
715 pThis->hSessionEvent = NIL_RTSEMEVENT;
716 }
717 else
718 supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc);
719 RTSemEventDestroy(pThis->hResponseEvent);
720 pThis->hResponseEvent = NIL_RTSEMEVENT;
721 }
722 else
723 supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc);
724 RTCritSectDelete(&pThis->CritSect);
725 }
726 else
727 supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc);
728 RTCritSectDelete(&pThis->VerifyCritSect);
729 }
730 else
731 supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc);
732 if (fFreeIt)
733 RTMemFree(pThis);
734 Log(("supSvcGrantCreate: returns %Rrc\n", rc));
735 return rc;
736}
737
738
739/** @copydoc SUPSVCSERVICE::pfnStart */
740DECLCALLBACK(void) supSvcGrantStart(void *pvInstance)
741{
742 PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
743
744 /*
745 * Change the state and signal the thread.
746 */
747 int rc = RTCritSectEnter(&pThis->CritSect);
748 if (RT_SUCCESS(rc))
749 {
750 bool fInCritSect = true;
751 SUPSVCGRANTSTATE enmState = pThis->enmState;
752 if (enmState == kSupSvcGrantState_Paused)
753 {
754 pThis->enmState = kSupSvcGrantState_Resuming;
755 rc = RTThreadUserSignal(pThis->hThread);
756 if (RT_SUCCESS(rc))
757 {
758 /*
759 * Wait for the bugger to respond (no need to bitch here).
760 */
761 RTCritSectLeave(&pThis->CritSect);
762 supSvcGrantWait(pThis, kSupSvcGrantState_Resuming, kSupSvcGrantState_Listen);
763 fInCritSect = false;
764 }
765 }
766 else
767 supSvcLogError("supSvcGrantStart - Incorrect state %s!", supSvcGrantStateName(enmState));
768 if (fInCritSect)
769 RTCritSectLeave(&pThis->CritSect);
770 }
771 else
772 {
773 supSvcLogError("supSvcGrantStart - RTCritSectEnter returns %Rrc!", rc);
774 AssertRCReturnVoid(rc);
775 }
776}
777
778
779/** @copydoc SUPSVCSERVICE::pfnTryStop */
780DECLCALLBACK(int) supSvcGrantTryStop(void *pvInstance)
781{
782 PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
783
784 /*
785 * Don't give up immediately.
786 */
787 uint64_t u64StartTS = RTTimeMilliTS();
788 int rc;
789 for (;;)
790 {
791 /*
792 * First check the state to make sure the thing is actually running.
793 * If the critsect is butchered, just pretend success.
794 */
795 rc = RTCritSectEnter(&pThis->CritSect);
796 if (RT_FAILURE(rc))
797 {
798 supSvcLogError("supSvcGrantTryStop - RTCritSectEnter returns %Rrc", rc);
799 AssertRC(rc);
800 return VINF_SUCCESS;
801 }
802 SUPSVCGRANTSTATE enmState = pThis->enmState;
803 if (enmState != kSupSvcGrantState_Listen)
804 {
805 supSvcLogError("supSvcGrantTryStop - Not running, state: %s", supSvcGrantStateName(enmState));
806 RTCritSectLeave(&pThis->CritSect);
807 return VINF_SUCCESS;
808 }
809
810 /*
811 * If there are no clients, usher the thread into the paused state.
812 */
813 supSvcGrantCleanUpSessionsLocked(pThis);
814 if (!pThis->pSessionHead)
815 {
816 rc = RTThreadUserReset(pThis->hThread);
817 pThis->enmState = kSupSvcGrantState_Pausing;
818 int rc2 = RTLocalIpcServerCancel(pThis->hServer);
819 int rc3 = RTCritSectLeave(&pThis->CritSect);
820 if (RT_SUCCESS(rc) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3))
821 supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused);
822 else
823 {
824 if (RT_FAILURE(rc))
825 supSvcLogError("supSvcGrantTryStop - RTThreadUserReset returns %Rrc", rc);
826 if (RT_FAILURE(rc2))
827 supSvcLogError("supSvcGrantTryStop - RTLocalIpcServerCancel returns %Rrc", rc);
828 if (RT_FAILURE(rc3))
829 supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc);
830 }
831 return VINF_SUCCESS;
832 }
833
834 /*
835 * Check the time limit, otherwise wait for a client event.
836 */
837 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS;
838 if (u64Elapsed >= 60*1000) /* 1 min */
839 {
840 unsigned cSessions = 0;
841 for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
842 cSessions++;
843 RTCritSectLeave(&pThis->CritSect);
844
845 supSvcLogError("supSvcGrantTryStop - %u active sessions after waiting %u ms", cSessions, (unsigned)u64Elapsed);
846 return VERR_TRY_AGAIN;
847 }
848
849 rc = RTCritSectLeave(&pThis->CritSect);
850 if (RT_FAILURE(rc))
851 {
852 supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc);
853 return VINF_SUCCESS;
854 }
855
856 rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed);
857 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
858 {
859 supSvcLogError("supSvcGrantTryStop - RTSemEventWait returns %Rrc", rc);
860 return VINF_SUCCESS;
861 }
862 }
863}
864
865
866/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */
867DECLCALLBACK(void) supSvcGrantStopAndDestroy(void *pvInstance, bool fRunning)
868{
869 PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
870 int rc;
871
872 /*
873 * Attempt to stop the service, cancelling blocked server and client calls.
874 */
875 RTCritSectEnter(&pThis->CritSect);
876
877 SUPSVCGRANTSTATE enmState = pThis->enmState;
878 AssertMsg(fRunning == (pThis->enmState == kSupSvcGrantState_Listen),
879 ("%RTbool %s\n", fRunning, supSvcGrantStateName(enmState)));
880
881 if (enmState == kSupSvcGrantState_Listen)
882 {
883 RTThreadUserReset(pThis->hThread);
884 pThis->enmState = kSupSvcGrantState_Paused;
885 for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
886 ASMAtomicWriteBool(&pCur->fTerminate, true);
887
888 /* try cancel local ipc operations that might be pending */
889 RTLocalIpcServerCancel(pThis->hServer);
890 for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
891 {
892 RTLOCALIPCSESSION hSession;
893 ASMAtomicReadHandle(&pCur->hSession, &hSession);
894 if (hSession != NIL_RTLOCALIPCSESSION)
895 RTLocalIpcSessionCancel(hSession);
896 }
897
898 /*
899 * Wait for the thread to respond (outside the crit sect).
900 */
901 RTCritSectLeave(&pThis->CritSect);
902 supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused);
903 RTCritSectEnter(&pThis->CritSect);
904
905 /*
906 * Wait for any lingering sessions to exit.
907 */
908 supSvcGrantCleanUpSessionsLocked(pThis);
909 if (pThis->pSessionHead)
910 {
911 uint64_t u64StartTS = RTTimeMilliTS();
912 do
913 {
914 /* Destroy the sessions since cancelling didn't do the trick. */
915 for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
916 {
917 RTLOCALIPCSESSION hSession;
918 ASMAtomicXchgHandle(&pCur->hSession, NIL_RTLOCALIPCSESSION, &hSession);
919 if (hSession != NIL_RTLOCALIPCSESSION)
920 {
921 rc = RTLocalIpcSessionClose(hSession);
922 AssertRC(rc);
923 if (RT_FAILURE(rc))
924 supSvcLogError("supSvcGrantStopAndDestroy: RTLocalIpcSessionClose(%p) returns %Rrc",
925 (uintptr_t)hSession, rc);
926 }
927 }
928
929 /* Check the time. */
930 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS;
931 if (u64Elapsed >= 60*1000) /* 1 min */
932 break;
933
934 /* wait */
935 RTCritSectLeave(&pThis->CritSect);
936 rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed);
937 RTCritSectEnter(&pThis->CritSect);
938 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
939 break;
940
941 /* cleanup and check again */
942 supSvcGrantCleanUpSessionsLocked(pThis);
943 } while (pThis->pSessionHead);
944 }
945 }
946
947 /*
948 * Tell the service thread to terminate and wait for it to do so.
949 */
950 pThis->enmState = kSupSvcGrantState_Terminating;
951 RTLOCALIPCSERVER hServer;
952 ASMAtomicXchgHandle(&pThis->hServer, NIL_RTLOCALIPCSERVER, &hServer);
953 RTThreadUserSignal(pThis->hThread);
954
955 RTCritSectLeave(&pThis->CritSect);
956
957 rc = RTThreadWait(pThis->hThread, 20*1000, NULL);
958 if (RT_FAILURE(rc) && rc == VERR_TIMEOUT)
959 {
960 RTThreadUserSignal(pThis->hThread);
961 RTLocalIpcServerDestroy(hServer);
962 hServer = NIL_RTLOCALIPCSERVER;
963
964 rc = RTThreadWait(pThis->hThread, 40*1000, NULL);
965 if (RT_FAILURE(rc))
966 supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(40 sec) returns %Rrc", rc);
967 }
968 else if (RT_FAILURE(rc))
969 supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(20 sec) returns %Rrc", rc);
970 pThis->hThread = NIL_RTTHREAD;
971
972 /*
973 * Kill the parent pointers of any lingering sessions.
974 */
975 RTCritSectEnter(&pThis->CritSect);
976 pThis->enmState = kSupSvcGrantState_Destroyed;
977
978 supSvcGrantCleanUpSessionsLocked(pThis);
979 unsigned cSessions = 0;
980 for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
981 ASMAtomicWritePtr((void * volatile *)&pCur->pParent, NULL);
982
983 RTCritSectLeave(&pThis->CritSect);
984 if (cSessions)
985 supSvcLogError("supSvcGrantStopAndDestroy: %d session failed to terminate!", cSessions);
986
987 /*
988 * Free the resource.
989 */
990 RTLocalIpcServerDestroy(hServer);
991
992 RTSemEventDestroy(pThis->hResponseEvent);
993 pThis->hResponseEvent = NIL_RTSEMEVENT;
994
995 RTSemEventDestroy(pThis->hSessionEvent);
996 pThis->hSessionEvent = NIL_RTSEMEVENT;
997
998 RTCritSectDelete(&pThis->VerifyCritSect);
999 RTCritSectDelete(&pThis->CritSect);
1000
1001 RTMemFree(pThis);
1002
1003 Log(("supSvcGrantStopAndDestroy: done (rc=%Rrc)\n", rc));
1004}
1005
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use