VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/IntNetSwitch/main.cpp@ 97086

最後變更 在這個檔案從97086是 97084,由 vboxsync 提交於 2 年 前

NetworkServices/IntNetSwitch: Some fixes and cleanups, bugref:10297

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 21.6 KB
 
1/* $Id: main.cpp 97084 2022-10-11 06:51:17Z vboxsync $ */
2/** @file
3 * Internal networking - Wrapper for the R0 network service.
4 *
5 * This is a bit hackish as we're mixing context here, however it is
6 * very useful when making changes to the internal networking service.
7 */
8
9/*
10 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.alldomusa.eu.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * SPDX-License-Identifier: GPL-3.0-only
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#define IN_INTNET_TESTCASE
36#define IN_INTNET_R3
37#include "IntNetSwitchInternal.h"
38
39#include <VBox/err.h>
40#include <VBox/vmm/vmm.h>
41#include <iprt/asm.h>
42#include <iprt/critsect.h>
43#include <iprt/initterm.h>
44#include <iprt/mem.h>
45#include <iprt/message.h>
46#include <iprt/string.h>
47#include <iprt/thread.h>
48#include <iprt/semaphore.h>
49#include <iprt/time.h>
50
51#include <xpc/xpc.h>
52
53
54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
57
58/**
59 * Registered object.
60 * This takes care of reference counting and tracking data for access checks.
61 */
62typedef struct SUPDRVOBJ
63{
64 /** Pointer to the next in the global list. */
65 struct SUPDRVOBJ * volatile pNext;
66 /** Pointer to the object destructor.
67 * This may be set to NULL if the image containing the destructor get unloaded. */
68 PFNSUPDRVDESTRUCTOR pfnDestructor;
69 /** User argument 1. */
70 void *pvUser1;
71 /** User argument 2. */
72 void *pvUser2;
73 /** The total sum of all per-session usage. */
74 uint32_t volatile cUsage;
75} SUPDRVOBJ, *PSUPDRVOBJ;
76
77
78/**
79 * The per-session object usage record.
80 */
81typedef struct SUPDRVUSAGE
82{
83 /** Pointer to the next in the list. */
84 struct SUPDRVUSAGE * volatile pNext;
85 /** Pointer to the object we're recording usage for. */
86 PSUPDRVOBJ pObj;
87 /** The usage count. */
88 uint32_t volatile cUsage;
89} SUPDRVUSAGE, *PSUPDRVUSAGE;
90
91
92/**
93 * Device extension.
94 */
95typedef struct SUPDRVDEVEXT
96{
97 /** Number of references to this service. */
98 uint32_t volatile cRefs;
99 /** Critical section to serialize the initialization, usage counting and objects. */
100 RTCRITSECT CritSect;
101 /** List of registered objects. Protected by the spinlock. */
102 PSUPDRVOBJ volatile pObjs;
103} SUPDRVDEVEXT;
104typedef SUPDRVDEVEXT *PSUPDRVDEVEXT;
105
106
107/**
108 * Per session data.
109 * This is mainly for memory tracking.
110 */
111typedef struct SUPDRVSESSION
112{
113 PSUPDRVDEVEXT pDevExt;
114 /** List of generic usage records. (protected by SUPDRVDEVEXT::CritSect) */
115 PSUPDRVUSAGE volatile pUsage;
116 /** The XPC connection handle for this session. */
117 xpc_connection_t hXpcCon;
118 /** The receive wait thread. */
119 RTTHREAD hThrdRecv;
120 /** The intnet interface handle to wait on. */
121 INTNETIFHANDLE hIfWait;
122 /** The event semaphore for the receive thread. */
123 RTSEMEVENT hEvtRecv;
124 /** Flag whether to terminate the receive thread. */
125 bool volatile fTerminate;
126} SUPDRVSESSION;
127
128
129/*********************************************************************************************************************************
130* Global Variables *
131*********************************************************************************************************************************/
132static SUPDRVDEVEXT g_DevExt;
133
134
135INTNETR3DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType,
136 PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
137{
138 RT_NOREF(enmType);
139
140 PSUPDRVOBJ pObj = (PSUPDRVOBJ)RTMemAllocZ(sizeof(*pObj));
141 if (!pObj)
142 return NULL;
143 pObj->cUsage = 1;
144 pObj->pfnDestructor = pfnDestructor;
145 pObj->pvUser1 = pvUser1;
146 pObj->pvUser2 = pvUser2;
147
148 /*
149 * Insert the object and create the session usage record.
150 */
151 PSUPDRVUSAGE pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage));
152 if (!pUsage)
153 {
154 RTMemFree(pObj);
155 return NULL;
156 }
157
158 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
159 RTCritSectEnter(&pDevExt->CritSect);
160
161 /* The object. */
162 pObj->pNext = pDevExt->pObjs;
163 pDevExt->pObjs = pObj;
164
165 /* The session record. */
166 pUsage->cUsage = 1;
167 pUsage->pObj = pObj;
168 pUsage->pNext = pSession->pUsage;
169 pSession->pUsage = pUsage;
170
171 RTCritSectLeave(&pDevExt->CritSect);
172 return pObj;
173}
174
175
176INTNETR3DECL(int) SUPR0ObjAddRefEx(void *pvObj, PSUPDRVSESSION pSession, bool fNoBlocking)
177{
178 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
179 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
180 int rc = VINF_SUCCESS;
181 PSUPDRVUSAGE pUsage;
182
183 RT_NOREF(fNoBlocking);
184
185 RTCritSectEnter(&pDevExt->CritSect);
186
187 /*
188 * Reference the object.
189 */
190 ASMAtomicIncU32(&pObj->cUsage);
191
192 /*
193 * Look for the session record.
194 */
195 for (pUsage = pSession->pUsage; pUsage; pUsage = pUsage->pNext)
196 {
197 if (pUsage->pObj == pObj)
198 break;
199 }
200
201 if (pUsage)
202 pUsage->cUsage++;
203 else
204 {
205 /* create a new session record. */
206 pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage));
207 if (RT_LIKELY(pUsage))
208 {
209 pUsage->cUsage = 1;
210 pUsage->pObj = pObj;
211 pUsage->pNext = pSession->pUsage;
212 pSession->pUsage = pUsage;
213 }
214 else
215 {
216 ASMAtomicDecU32(&pObj->cUsage);
217 rc = VERR_TRY_AGAIN;
218 }
219 }
220
221 RTCritSectLeave(&pDevExt->CritSect);
222 return rc;
223}
224
225
226INTNETR3DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
227{
228 return SUPR0ObjAddRefEx(pvObj, pSession, false);
229}
230
231
232INTNETR3DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
233{
234 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
235 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
236 int rc = VERR_INVALID_PARAMETER;
237 PSUPDRVUSAGE pUsage;
238 PSUPDRVUSAGE pUsagePrev;
239
240 /*
241 * Acquire the spinlock and look for the usage record.
242 */
243 RTCritSectEnter(&pDevExt->CritSect);
244
245 for (pUsagePrev = NULL, pUsage = pSession->pUsage;
246 pUsage;
247 pUsagePrev = pUsage, pUsage = pUsage->pNext)
248 {
249 if (pUsage->pObj == pObj)
250 {
251 rc = VINF_SUCCESS;
252 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
253 if (pUsage->cUsage > 1)
254 {
255 pObj->cUsage--;
256 pUsage->cUsage--;
257 }
258 else
259 {
260 /*
261 * Free the session record.
262 */
263 if (pUsagePrev)
264 pUsagePrev->pNext = pUsage->pNext;
265 else
266 pSession->pUsage = pUsage->pNext;
267 RTMemFree(pUsage);
268
269 /* What about the object? */
270 if (pObj->cUsage > 1)
271 pObj->cUsage--;
272 else
273 {
274 /*
275 * Object is to be destroyed, unlink it.
276 */
277 rc = VINF_OBJECT_DESTROYED;
278 if (pDevExt->pObjs == pObj)
279 pDevExt->pObjs = pObj->pNext;
280 else
281 {
282 PSUPDRVOBJ pObjPrev;
283 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
284 if (pObjPrev->pNext == pObj)
285 {
286 pObjPrev->pNext = pObj->pNext;
287 break;
288 }
289 Assert(pObjPrev);
290 }
291 }
292 }
293 break;
294 }
295 }
296
297 RTCritSectLeave(&pDevExt->CritSect);
298
299 /*
300 * Call the destructor and free the object if required.
301 */
302 if (rc == VINF_OBJECT_DESTROYED)
303 {
304 if (pObj->pfnDestructor)
305 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
306 RTMemFree(pObj);
307 }
308
309 return rc;
310}
311
312
313INTNETR3DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
314{
315 RT_NOREF(pvObj, pSession, pszObjName);
316 return VINF_SUCCESS;
317}
318
319
320INTNETR3DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
321{
322 RT_NOREF(pSession);
323
324 /*
325 * This is used to allocate and map the send/receive buffers into the callers process space, meaning
326 * we have to mmap it with the shareable attribute.
327 */
328 void *pv = mmap(NULL, cb, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
329 if (pv == MAP_FAILED)
330 return VERR_NO_MEMORY;
331
332 *ppvR0 = (RTR0PTR)pv;
333 if (ppvR3)
334 *ppvR3 = pv;
335 return VINF_SUCCESS;
336}
337
338
339INTNETR3DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
340{
341 RT_NOREF(pSession);
342
343 PINTNETBUF pBuf = (PINTNETBUF)uPtr; /// @todo Hack hack hack!
344 munmap((void *)uPtr, pBuf->cbBuf);
345 return VINF_SUCCESS;
346}
347
348
349/**
350 * Destroys the given internal network XPC connection session freeing all allocated resources.
351 *
352 * @returns Reference count of the device extension..
353 * @param pSession The ession to destroy.
354 */
355static uint32_t intnetR3SessionDestroy(PSUPDRVSESSION pSession)
356{
357 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
358 uint32_t cRefs = ASMAtomicDecU32(&pDevExt->cRefs);
359 xpc_connection_set_context(pSession->hXpcCon, NULL);
360 xpc_connection_cancel(pSession->hXpcCon);
361 pSession->hXpcCon = NULL;
362 xpc_transaction_end();
363
364 /* Tear down the receive wait thread. */
365 ASMAtomicXchgBool(&pSession->fTerminate, true);
366 RTSemEventSignal(pSession->hEvtRecv);
367
368 if (pSession->hThrdRecv != NIL_RTTHREAD)
369 {
370 int rc = RTThreadWait(pSession->hThrdRecv, 5000, NULL);
371 AssertRC(rc);
372 pSession->hThrdRecv = NIL_RTTHREAD;
373 }
374
375 RTSemEventDestroy(pSession->hEvtRecv);
376 pSession->hEvtRecv = NIL_RTSEMEVENT;
377
378 if (pSession->pUsage)
379 {
380 PSUPDRVUSAGE pUsage;
381 RTCritSectEnter(&pDevExt->CritSect);
382
383 while ((pUsage = pSession->pUsage) != NULL)
384 {
385 PSUPDRVOBJ pObj = pUsage->pObj;
386 pSession->pUsage = pUsage->pNext;
387
388 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
389 if (pUsage->cUsage < pObj->cUsage)
390 {
391 pObj->cUsage -= pUsage->cUsage;
392 }
393 else
394 {
395 /* Destroy the object and free the record. */
396 if (pDevExt->pObjs == pObj)
397 pDevExt->pObjs = pObj->pNext;
398 else
399 {
400 PSUPDRVOBJ pObjPrev;
401 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
402 if (pObjPrev->pNext == pObj)
403 {
404 pObjPrev->pNext = pObj->pNext;
405 break;
406 }
407 Assert(pObjPrev);
408 }
409
410 RTCritSectLeave(&pDevExt->CritSect);
411
412 if (pObj->pfnDestructor)
413 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
414 RTMemFree(pObj);
415
416 RTCritSectEnter(&pDevExt->CritSect);
417 }
418
419 /* free it and continue. */
420 RTMemFree(pUsage);
421 }
422
423 RTCritSectLeave(&pDevExt->CritSect);
424 AssertMsg(!pSession->pUsage, ("Some buster reregistered an object during desturction!\n"));
425 }
426
427 RTMemFree(pSession);
428 return cRefs;
429}
430
431
432/**
433 * Asynchronous I/O thread for handling receive.
434 *
435 * @returns VINF_SUCCESS (ignored).
436 * @param hThreadSelf Thread handle.
437 * @param pvUser Pointer to a DRVINTNET structure.
438 */
439static DECLCALLBACK(int) intnetR3RecvThread(RTTHREAD hThreadSelf, void *pvUser)
440{
441 RT_NOREF(hThreadSelf);
442 PSUPDRVSESSION pSession = (PSUPDRVSESSION)pvUser;
443
444 for (;;)
445 {
446 if (pSession->fTerminate)
447 break;
448
449 RTSemEventWait(pSession->hEvtRecv, RT_INDEFINITE_WAIT);
450 if (pSession->fTerminate)
451 break;
452
453 int rc = IntNetR0IfWait(pSession->hIfWait, pSession, 30000); /* 30s - don't wait forever, timeout now and then. */
454 if (RT_SUCCESS(rc))
455 {
456 /* Send an empty message. */
457 xpc_object_t hObjPoke = xpc_dictionary_create(NULL, NULL, 0);
458 xpc_connection_send_message(pSession->hXpcCon, hObjPoke);
459 }
460 else if ( rc != VERR_TIMEOUT
461 && rc != VERR_INTERRUPTED)
462 {
463 LogFlow(("intnetR3RecvThread: returns %Rrc\n", rc));
464 return rc;
465 }
466 }
467
468 return VINF_SUCCESS;
469}
470
471
472static void intnetR3RequestProcess(xpc_connection_t hCon, xpc_object_t hObj, PSUPDRVSESSION pSession)
473{
474 int rc = VINF_SUCCESS;
475 uint64_t iReq = xpc_dictionary_get_uint64(hObj, "req-id");
476 size_t cbReq = 0;
477 const void *pvReq = xpc_dictionary_get_data(hObj, "req", &cbReq);
478 union
479 {
480 INTNETOPENREQ OpenReq;
481 INTNETIFCLOSEREQ IfCloseReq;
482 INTNETIFGETBUFFERPTRSREQ IfGetBufferPtrsReq;
483 INTNETIFSETPROMISCUOUSMODEREQ IfSetPromiscuousModeReq;
484 INTNETIFSETMACADDRESSREQ IfSetMacAddressReq;
485 INTNETIFSETACTIVEREQ IfSetActiveReq;
486 INTNETIFSENDREQ IfSendReq;
487 INTNETIFWAITREQ IfWaitReq;
488 INTNETIFABORTWAITREQ IfAbortWaitReq;
489 } ReqReply;
490
491 memcpy(&ReqReply, pvReq, RT_MIN(sizeof(ReqReply), cbReq));
492 size_t cbReply = 0;
493
494 if (pvReq)
495 {
496 switch (iReq)
497 {
498 case VMMR0_DO_INTNET_OPEN:
499 {
500 if (cbReq == sizeof(INTNETOPENREQ))
501 {
502 rc = IntNetR0OpenReq(pSession, &ReqReply.OpenReq);
503 cbReply = sizeof(INTNETOPENREQ);
504 }
505 else
506 rc = VERR_INVALID_PARAMETER;
507 break;
508 }
509 case VMMR0_DO_INTNET_IF_CLOSE:
510 {
511 if (cbReq == sizeof(INTNETIFCLOSEREQ))
512 {
513 rc = IntNetR0IfCloseReq(pSession, &ReqReply.IfCloseReq);
514 cbReply = sizeof(INTNETIFCLOSEREQ);
515 }
516 else
517 rc = VERR_INVALID_PARAMETER;
518 break;
519 }
520 case VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS:
521 {
522 if (cbReq == sizeof(INTNETIFGETBUFFERPTRSREQ))
523 {
524 rc = IntNetR0IfGetBufferPtrsReq(pSession, &ReqReply.IfGetBufferPtrsReq);
525 /* This is special as we need to return a shared memory segment. */
526 xpc_object_t hObjReply = xpc_dictionary_create_reply(hObj);
527 xpc_dictionary_set_int64(hObjReply, "rc", rc);
528 xpc_object_t hObjShMem = xpc_shmem_create(ReqReply.IfGetBufferPtrsReq.pRing3Buf, ReqReply.IfGetBufferPtrsReq.pRing3Buf->cbBuf);
529 xpc_dictionary_set_value(hObjReply, "buf-ptr", hObjShMem);
530 xpc_release(hObjShMem);
531 xpc_connection_send_message(hCon, hObjReply);
532 return;
533 }
534 else
535 rc = VERR_INVALID_PARAMETER;
536 break;
537 }
538 case VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE:
539 {
540 if (cbReq == sizeof(INTNETIFSETPROMISCUOUSMODEREQ))
541 {
542 rc = IntNetR0IfSetPromiscuousModeReq(pSession, &ReqReply.IfSetPromiscuousModeReq);
543 cbReply = sizeof(INTNETIFSETPROMISCUOUSMODEREQ);
544 }
545 else
546 rc = VERR_INVALID_PARAMETER;
547 break;
548 }
549 case VMMR0_DO_INTNET_IF_SET_MAC_ADDRESS:
550 {
551 if (cbReq == sizeof(INTNETIFSETMACADDRESSREQ))
552 {
553 rc = IntNetR0IfSetMacAddressReq(pSession, &ReqReply.IfSetMacAddressReq);
554 cbReply = sizeof(INTNETIFSETMACADDRESSREQ);
555 }
556 else
557 rc = VERR_INVALID_PARAMETER;
558 break;
559 }
560 case VMMR0_DO_INTNET_IF_SET_ACTIVE:
561 {
562 if (cbReq == sizeof(INTNETIFSETACTIVEREQ))
563 {
564 rc = IntNetR0IfSetActiveReq(pSession, &ReqReply.IfSetActiveReq);
565 cbReply = sizeof(INTNETIFSETACTIVEREQ);
566 }
567 else
568 rc = VERR_INVALID_PARAMETER;
569 break;
570 }
571 case VMMR0_DO_INTNET_IF_SEND:
572 {
573 if (cbReq == sizeof(INTNETIFSENDREQ))
574 {
575 rc = IntNetR0IfSendReq(pSession, &ReqReply.IfSendReq);
576 cbReply = sizeof(INTNETIFSENDREQ);
577 }
578 else
579 rc = VERR_INVALID_PARAMETER;
580 break;
581 }
582 case VMMR0_DO_INTNET_IF_WAIT:
583 {
584 if (cbReq == sizeof(INTNETIFWAITREQ))
585 {
586 pSession->hIfWait = ReqReply.IfWaitReq.hIf; /* Asynchronous. */
587 rc = RTSemEventSignal(pSession->hEvtRecv);
588 return;
589 }
590 else
591 rc = VERR_INVALID_PARAMETER;
592 break;
593 }
594 case VMMR0_DO_INTNET_IF_ABORT_WAIT:
595 {
596 if (cbReq == sizeof(INTNETIFABORTWAITREQ))
597 {
598 rc = IntNetR0IfAbortWaitReq(pSession, &ReqReply.IfAbortWaitReq);
599 cbReply = sizeof(INTNETIFABORTWAITREQ);
600 }
601 else
602 rc = VERR_INVALID_PARAMETER;
603 break;
604 }
605 default:
606 rc = VERR_INVALID_PARAMETER;
607 }
608 }
609
610 xpc_object_t hObjReply = xpc_dictionary_create_reply(hObj);
611 xpc_dictionary_set_int64(hObjReply, "rc", rc);
612 xpc_dictionary_set_data(hObjReply, "reply", &ReqReply, cbReply);
613 xpc_connection_send_message(hCon, hObjReply);
614}
615
616
617DECLCALLBACK(void) xpcConnHandler(xpc_connection_t hXpcCon)
618{
619 xpc_connection_set_event_handler(hXpcCon, ^(xpc_object_t hObj) {
620 PSUPDRVSESSION pSession = (PSUPDRVSESSION)xpc_connection_get_context(hXpcCon);
621
622 if (xpc_get_type(hObj) == XPC_TYPE_ERROR)
623 {
624 if (hObj == XPC_ERROR_CONNECTION_INVALID)
625 intnetR3SessionDestroy(pSession);
626 else if (hObj == XPC_ERROR_TERMINATION_IMMINENT)
627 {
628 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
629
630 uint32_t cRefs = intnetR3SessionDestroy(pSession);
631 if (!cRefs)
632 {
633 /* Last one cleans up the global data. */
634 RTCritSectDelete(&pDevExt->CritSect);
635 }
636 }
637 }
638 else
639 intnetR3RequestProcess(hXpcCon, hObj, pSession);
640 });
641
642 PSUPDRVSESSION pSession = (PSUPDRVSESSION)RTMemAllocZ(sizeof(*pSession));
643 if (pSession)
644 {
645 pSession->pDevExt = &g_DevExt;
646 pSession->hXpcCon = hXpcCon;
647
648 int rc = RTSemEventCreate(&pSession->hEvtRecv);
649 if (RT_SUCCESS(rc))
650 {
651 rc = RTThreadCreate(&pSession->hThrdRecv, intnetR3RecvThread, pSession, 0,
652 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "INTNET-RECV");
653 if (RT_SUCCESS(rc))
654 {
655 xpc_connection_set_context(hXpcCon, pSession);
656 xpc_connection_activate(hXpcCon);
657 xpc_transaction_begin();
658 ASMAtomicIncU32(&g_DevExt.cRefs);
659 return;
660 }
661
662 RTSemEventDestroy(pSession->hEvtRecv);
663 }
664
665 RTMemFree(pSession);
666 }
667}
668
669
670int main(int argc, char **argv)
671{
672 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
673 if (RT_SUCCESS(rc))
674 {
675 IntNetR0Init();
676
677 g_DevExt.pObjs = NULL;
678 rc = RTCritSectInit(&g_DevExt.CritSect);
679 if (RT_SUCCESS(rc))
680 xpc_main(xpcConnHandler); /* Never returns. */
681
682 exit(EXIT_FAILURE);
683 }
684
685 return RTMsgInitFailure(rc);
686}
687
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette