VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.cpp

Last change on this file 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: 20.4 KB
Line 
1/* $Id: VBoxIPC.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxIPC - IPC thread, acts as a (purely) local IPC server.
4 * Multiple sessions are supported, whereas every session
5 * has its own thread for processing requests.
6 */
7
8/*
9 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/critsect.h>
37#include <iprt/errcore.h>
38#include <iprt/ldr.h>
39#include <iprt/list.h>
40#include <iprt/localipc.h>
41#include <iprt/log.h>
42#include <iprt/mem.h>
43#include <iprt/process.h>
44#include <iprt/win/windows.h>
45
46#include "VBoxTray.h"
47#include "VBoxTrayMsg.h"
48#include "VBoxHelpers.h"
49#include "VBoxIPC.h"
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55/**
56 * IPC context data.
57 */
58typedef struct VBOXIPCCONTEXT
59{
60 /** Pointer to the service environment. */
61 const VBOXSERVICEENV *pEnv;
62 /** Handle for the local IPC server. */
63 RTLOCALIPCSERVER hServer;
64 /** Critical section serializing access to the session list, the state,
65 * the response event, the session event, and the thread event. */
66 RTCRITSECT CritSect;
67 /** List of all active IPC sessions. */
68 RTLISTANCHOR SessionList;
69
70} VBOXIPCCONTEXT, *PVBOXIPCCONTEXT;
71
72/** Function pointer for GetLastInputInfo(). */
73typedef BOOL (WINAPI *PFNGETLASTINPUTINFO)(PLASTINPUTINFO);
74
75/**
76 * IPC per-session thread data.
77 */
78typedef struct VBOXIPCSESSION
79{
80 /** The list node required to be part of the
81 * IPC session list. */
82 RTLISTNODE Node;
83 /** Pointer to the IPC context data. */
84 PVBOXIPCCONTEXT volatile pCtx;
85 /** The local ipc client handle. */
86 RTLOCALIPCSESSION volatile hSession;
87 /** Indicate that the thread should terminate ASAP. */
88 bool volatile fTerminate;
89 /** The thread handle. */
90 RTTHREAD hThread;
91
92} VBOXIPCSESSION, *PVBOXIPCSESSION;
93
94
95/*********************************************************************************************************************************
96* Global Variables *
97*********************************************************************************************************************************/
98static VBOXIPCCONTEXT g_Ctx = { NULL, NIL_RTLOCALIPCSERVER };
99static PFNGETLASTINPUTINFO g_pfnGetLastInputInfo = NULL;
100
101
102/*********************************************************************************************************************************
103* Internal Functions *
104*********************************************************************************************************************************/
105static int vboxIPCSessionStop(PVBOXIPCSESSION pSession);
106
107
108
109/**
110 * Handles VBOXTRAYIPCMSGTYPE_RESTART.
111 */
112static int vboxIPCHandleVBoxTrayRestart(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
113{
114 RT_NOREF(pSession, pHdr);
115
116 /** @todo Not implemented yet; don't return an error here. */
117 return VINF_SUCCESS;
118}
119
120/**
121 * Handles VBOXTRAYIPCMSGTYPE_SHOW_BALLOON_MSG.
122 */
123static int vboxIPCHandleShowBalloonMsg(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
124{
125 /*
126 * Unmarshal and validate the data.
127 */
128 union
129 {
130 uint8_t abBuf[_4K];
131 VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T s;
132 } Payload;
133 AssertReturn(pHdr->cbPayload >= RT_UOFFSETOF_DYN(VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T, szzStrings[2]), VERR_INVALID_PARAMETER);
134 AssertReturn(pHdr->cbPayload < sizeof(Payload), VERR_BUFFER_OVERFLOW);
135
136 int rc = RTLocalIpcSessionRead(pSession->hSession, &Payload, pHdr->cbPayload, NULL /*pcbRead - exact, blocking*/);
137 if (RT_FAILURE(rc))
138 return rc;
139
140 /* String lengths: */
141 AssertReturn( Payload.s.cchMsg + 1 + Payload.s.cchTitle + 1 + RT_UOFFSETOF(VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T, szzStrings)
142 <= pHdr->cbPayload, VERR_INVALID_PARAMETER);
143
144 /* Message text: */
145 const char *pszMsg = Payload.s.szzStrings;
146 rc = RTStrValidateEncodingEx(pszMsg, Payload.s.cchMsg + 1,
147 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
148 AssertRCReturn(rc, rc);
149
150 /* Title text: */
151 const char *pszTitle = &Payload.s.szzStrings[Payload.s.cchMsg + 1];
152 rc = RTStrValidateEncodingEx(pszMsg, Payload.s.cchTitle + 1,
153 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
154 AssertRCReturn(rc, rc);
155
156 /* Type/dwInfoFlags: */
157 AssertReturn( Payload.s.uType == NIIF_NONE
158 || Payload.s.uType == NIIF_INFO
159 || Payload.s.uType == NIIF_WARNING
160 || Payload.s.uType == NIIF_ERROR,
161 VERR_WRONG_TYPE);
162
163 /* Timeout: */
164 if (!Payload.s.cMsTimeout)
165 Payload.s.cMsTimeout = RT_MS_5SEC;
166 AssertStmt(Payload.s.cMsTimeout >= RT_MS_1SEC, Payload.s.cMsTimeout = RT_MS_1SEC);
167 AssertStmt(Payload.s.cMsTimeout <= RT_MS_1MIN, Payload.s.cMsTimeout = RT_MS_1MIN);
168
169 /*
170 * Showing the balloon tooltip is not critical.
171 */
172 int rc2 = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
173 pszMsg, pszTitle, Payload.s.cMsTimeout, Payload.s.uType);
174 LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n",
175 pszTitle, pszMsg, Payload.s.cMsTimeout, Payload.s.uType, rc2));
176 RT_NOREF_PV(rc2);
177
178 return VINF_SUCCESS;
179}
180
181/**
182 * Handles VBOXTRAYIPCMSGTYPE_USER_LAST_INPUT.
183 */
184static int vboxIPCHandleUserLastInput(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
185{
186 RT_NOREF(pHdr);
187
188 int rc = VINF_SUCCESS;
189 VBOXTRAYIPCREPLY_USER_LAST_INPUT_T Reply = { UINT32_MAX };
190 if (g_pfnGetLastInputInfo)
191 {
192 /* Note: This only works up to 49.7 days (= 2^32, 32-bit counter) since Windows was started. */
193 LASTINPUTINFO LastInput;
194 LastInput.cbSize = sizeof(LastInput);
195 if (g_pfnGetLastInputInfo(&LastInput))
196 Reply.cSecSinceLastInput = (GetTickCount() - LastInput.dwTime) / 1000;
197 else
198 rc = RTErrConvertFromWin32(GetLastError());
199 }
200
201 int rc2 = RTLocalIpcSessionWrite(pSession->hSession, &Reply, sizeof(Reply));
202 if (RT_SUCCESS(rc))
203 rc = rc2;
204
205 return rc;
206}
207
208/**
209 * Initializes the IPC communication.
210 *
211 * @return IPRT status code.
212 * @param pEnv The IPC service's environment.
213 * @param ppInstance The instance pointer which refers to this object.
214 */
215DECLCALLBACK(int) VBoxIPCInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
216{
217 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
218 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
219
220 LogFlowFuncEnter();
221
222 PVBOXIPCCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
223 AssertPtr(pCtx);
224
225 int rc = RTCritSectInit(&pCtx->CritSect);
226 if (RT_SUCCESS(rc))
227 {
228 char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
229 memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
230 rc = RTProcQueryUsername(NIL_RTPROCESS,
231 &szPipeName[sizeof(VBOXTRAY_IPC_PIPE_PREFIX) - 1],
232 sizeof(szPipeName) - sizeof(VBOXTRAY_IPC_PIPE_PREFIX) + 1,
233 NULL /*pcbUser*/);
234 AssertRC(rc);
235 if (RT_SUCCESS(rc))
236 {
237 rc = RTLocalIpcServerCreate(&pCtx->hServer, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
238 AssertRC(rc);
239 if (RT_SUCCESS(rc))
240 {
241 pCtx->pEnv = pEnv;
242 RTListInit(&pCtx->SessionList);
243
244 *ppInstance = pCtx;
245
246 /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
247 g_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
248
249 LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
250 return VINF_SUCCESS;
251 }
252
253 }
254
255 RTCritSectDelete(&pCtx->CritSect);
256 }
257
258 LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
259 return rc;
260}
261
262DECLCALLBACK(void) VBoxIPCStop(void *pInstance)
263{
264 /* Can be NULL if VBoxIPCInit failed. */
265 if (!pInstance)
266 return;
267 AssertPtrReturnVoid(pInstance);
268
269 LogFlowFunc(("Stopping pInstance=%p\n", pInstance));
270
271 /* Shut down local IPC server. */
272 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
273 AssertPtr(pCtx);
274
275 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
276 {
277 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
278 if (RT_FAILURE(rc2))
279 LogFlowFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
280 }
281
282 /* Stop all remaining session threads. */
283 int rc = RTCritSectEnter(&pCtx->CritSect);
284 if (RT_SUCCESS(rc))
285 {
286 PVBOXIPCSESSION pSession;
287 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
288 {
289 int rc2 = vboxIPCSessionStop(pSession);
290 if (RT_FAILURE(rc2))
291 {
292 LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
293 pSession, rc2));
294 /* Keep going. */
295 }
296 }
297 }
298}
299
300DECLCALLBACK(void) VBoxIPCDestroy(void *pInstance)
301{
302 AssertPtrReturnVoid(pInstance);
303
304 LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
305
306 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
307 AssertPtr(pCtx);
308
309 /* Shut down local IPC server. */
310 int rc = RTCritSectEnter(&pCtx->CritSect);
311 if (RT_SUCCESS(rc))
312 {
313 rc = RTLocalIpcServerDestroy(pCtx->hServer);
314 if (RT_FAILURE(rc))
315 LogFlowFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc));
316
317 int rc2 = RTCritSectLeave(&pCtx->CritSect);
318 if (RT_SUCCESS(rc))
319 rc = rc2;
320 }
321
322 LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
323
324 /* Wait for all IPC session threads to shut down. */
325 bool fListIsEmpty = true;
326 do
327 {
328 int rc2 = RTCritSectEnter(&pCtx->CritSect);
329 if (RT_SUCCESS(rc2))
330 {
331 fListIsEmpty = RTListIsEmpty(&pCtx->SessionList);
332 rc2 = RTCritSectLeave(&pCtx->CritSect);
333
334 if (!fListIsEmpty) /* Don't hog CPU while waiting. */
335 RTThreadSleep(100);
336 }
337
338 if (RT_FAILURE(rc2))
339 break;
340
341 } while (!fListIsEmpty);
342
343 AssertMsg(fListIsEmpty,
344 ("Session thread list is not empty when it should\n"));
345
346 LogFlowFunc(("All remaining IPC sessions shut down\n"));
347
348 int rc2 = RTCritSectDelete(&pCtx->CritSect);
349 if (RT_SUCCESS(rc))
350 rc = rc2;
351
352 LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
353 pInstance, rc));
354}
355
356/**
357 * Services a client session.
358 *
359 * @returns VINF_SUCCESS.
360 * @param hThreadSelf The thread handle.
361 * @param pvSession Pointer to the session instance data.
362 */
363static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThreadSelf, void *pvSession)
364{
365 RT_NOREF(hThreadSelf);
366 PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
367 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
368 RTLOCALIPCSESSION hSession = pThis->hSession;
369 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
370
371 LogFlowFunc(("pThis=%p\n", pThis));
372
373 int rc = VINF_SUCCESS;
374
375 /*
376 * Process client requests until it quits or we're cancelled on termination.
377 */
378 while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
379 && RT_SUCCESS(rc))
380 {
381 /* The next call will be cancelled via VBoxIPCStop if needed. */
382 rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
383 if (RT_SUCCESS(rc))
384 {
385 /*
386 * Read the message header.
387 */
388 VBOXTRAYIPCHEADER Hdr = {0};
389 rc = RTLocalIpcSessionRead(hSession, &Hdr, sizeof(Hdr), NULL /*pcbRead - exact, blocking*/);
390 if (RT_FAILURE(rc))
391 break;
392
393 /*
394 * Validate the message header.
395 *
396 * Disconnecting the client if invalid or something we don't grok.
397 * Currently all clients are one-shots, so there is no need to get
398 * in complicated recovery code if we don't understand one another.
399 */
400 if ( Hdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC
401 || Hdr.uVersion != VBOXTRAY_IPC_HDR_VERSION)
402 {
403 LogRelFunc(("Session %p: Invalid header magic/version: %#x, %#x, %#x, %#x\n",
404 pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload));
405 rc = VERR_INVALID_MAGIC;
406 break;
407 }
408 if (Hdr.cbPayload > VBOXTRAY_IPC_MAX_PAYLOAD)
409 {
410 LogRelFunc(("Session %p: Payload to big: %#x, %#x, %#x, %#x - max %#x\n",
411 pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload, VBOXTRAY_IPC_MAX_PAYLOAD));
412 rc = VERR_TOO_MUCH_DATA;
413 break;
414 }
415 if ( Hdr.enmMsgType <= VBOXTRAYIPCMSGTYPE_INVALID
416 || Hdr.enmMsgType >= VBOXTRAYIPCMSGTYPE_END)
417 {
418 LogRelFunc(("Session %p: Unknown message: %#x, %#x, %#x, %#x\n",
419 pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload));
420 rc = VERR_INVALID_FUNCTION;
421 break;
422 }
423
424 /*
425 * Handle the message.
426 */
427 switch (Hdr.enmMsgType)
428 {
429 case VBOXTRAYIPCMSGTYPE_RESTART:
430 rc = vboxIPCHandleVBoxTrayRestart(pThis, &Hdr);
431 break;
432
433 case VBOXTRAYIPCMSGTYPE_SHOW_BALLOON_MSG:
434 rc = vboxIPCHandleShowBalloonMsg(pThis, &Hdr);
435 break;
436
437 case VBOXTRAYIPCMSGTYPE_USER_LAST_INPUT:
438 rc = vboxIPCHandleUserLastInput(pThis, &Hdr);
439 break;
440
441 default:
442 AssertFailedBreakStmt(rc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
443 }
444 if (RT_FAILURE(rc))
445 LogFlowFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n", pThis, Hdr.enmMsgType, rc));
446 }
447 else if (rc == VERR_CANCELLED)
448 {
449 LogFlowFunc(("Session %p: Waiting for data cancelled\n", pThis));
450 rc = VINF_SUCCESS;
451 break;
452 }
453 else
454 LogFlowFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n", pThis, rc));
455 }
456
457 LogFlowFunc(("Session %p: Handler ended with rc=%Rrc\n", pThis, rc));
458
459 /*
460 * Close the session.
461 */
462 int rc2 = RTLocalIpcSessionClose(hSession);
463 if (RT_FAILURE(rc2))
464 LogFlowFunc(("Session %p: Failed closing session %p, rc=%Rrc\n", pThis, rc2));
465
466 /*
467 * Clean up the session.
468 */
469 PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
470 AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
471 rc2 = RTCritSectEnter(&pCtx->CritSect);
472 if (RT_SUCCESS(rc2))
473 {
474 /* Remove this session from the session list. */
475 RTListNodeRemove(&pThis->Node);
476
477 rc2 = RTCritSectLeave(&pCtx->CritSect);
478 if (RT_SUCCESS(rc))
479 rc = rc2;
480 }
481
482 LogFlowFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n", pThis, rc));
483
484 RTMemFree(pThis);
485 pThis = NULL;
486
487 return rc;
488}
489
490static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
491{
492 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
493 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
494
495 int rc = RTCritSectEnter(&pCtx->CritSect);
496 if (RT_SUCCESS(rc))
497 {
498 PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
499 if (pSession)
500 {
501 pSession->pCtx = pCtx;
502 pSession->hSession = hSession;
503 pSession->fTerminate = false;
504 pSession->hThread = NIL_RTTHREAD;
505
506 /* Start IPC session thread. */
507 LogFlowFunc(("Creating thread for session %p ...\n", pSession));
508 rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread,
509 pSession /* pvUser */, 0 /* Default stack size */,
510 RTTHREADTYPE_DEFAULT, 0 /* Flags */, "IPCSESSION");
511 if (RT_SUCCESS(rc))
512 {
513 /* Add session thread to session IPC list. */
514 RTListAppend(&pCtx->SessionList, &pSession->Node);
515 }
516 else
517 {
518 int rc2 = RTLocalIpcSessionClose(hSession);
519 if (RT_FAILURE(rc2))
520 LogFlowFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
521
522 LogFlowFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
523 RTMemFree(pSession);
524 }
525 }
526 else
527 rc = VERR_NO_MEMORY;
528
529 int rc2 = RTCritSectLeave(&pCtx->CritSect);
530 AssertRC(rc2);
531 }
532
533 return rc;
534}
535
536static int vboxIPCSessionStop(PVBOXIPCSESSION pSession)
537{
538 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
539
540 ASMAtomicWriteBool(&pSession->fTerminate, true);
541
542 RTLOCALIPCSESSION hSession;
543 ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
544 if (hSession)
545 return RTLocalIpcSessionClose(hSession);
546
547 return VINF_SUCCESS;
548}
549
550/**
551 * Thread function to wait for and process seamless mode change
552 * requests
553 */
554DECLCALLBACK(int) VBoxIPCWorker(void *pInstance, bool volatile *pfShutdown)
555{
556 AssertPtr(pInstance);
557 LogFlowFunc(("pInstance=%p\n", pInstance));
558
559 LogFlowFuncEnter();
560
561 /*
562 * Tell the control thread that it can continue
563 * spawning services.
564 */
565 RTThreadUserSignal(RTThreadSelf());
566
567 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
568 AssertPtr(pCtx);
569
570 int rc;
571
572 bool fShutdown = false;
573 for (;;)
574 {
575 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
576 rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
577 if (RT_FAILURE(rc))
578 {
579 if (rc == VERR_CANCELLED)
580 {
581 LogFlow(("Cancelled\n"));
582 fShutdown = true;
583 }
584 else
585 LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
586 }
587
588 if (fShutdown)
589 break;
590 rc = vboxIPCSessionCreate(pCtx, hClientSession);
591 if (RT_FAILURE(rc))
592 {
593 LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
594 /* Keep going. */
595 }
596
597 if (*pfShutdown)
598 break;
599 }
600
601 LogFlowFuncLeaveRC(rc);
602 return rc;
603}
604
605/**
606 * The service description.
607 */
608VBOXSERVICEDESC g_SvcDescIPC =
609{
610 /* pszName. */
611 "IPC",
612 /* pszDescription. */
613 "Inter-Process Communication",
614 /* methods */
615 VBoxIPCInit,
616 VBoxIPCWorker,
617 NULL /* pfnStop */,
618 VBoxIPCDestroy
619};
620
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