VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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