VirtualBox

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

Last change on this file since 74125 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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