VirtualBox

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

Last change on this file since 47363 was 44529, checked in by vboxsync, 12 years ago

header (C) fixes

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