VirtualBox

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

最後變更 在這個檔案從95967是 95961,由 vboxsync 提交於 3 年 前

Additions/VBoxTray: Got rid of VBoxDisplay.h (renamed to VBoxTrayInternal.h, more stuff added later), as I also tripped over this several times in the past already, log include fixes.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 19.2 KB
 
1/* $Id: VBoxIPC.cpp 95961 2022-08-01 14:08:20Z 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-2022 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.alldomusa.eu.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#include <iprt/asm.h>
25#include <iprt/assert.h>
26#include <iprt/critsect.h>
27#include <iprt/errcore.h>
28#include <iprt/ldr.h>
29#include <iprt/list.h>
30#include <iprt/localipc.h>
31#include <iprt/log.h>
32#include <iprt/mem.h>
33#include <iprt/process.h>
34#include <iprt/win/windows.h>
35
36#include "VBoxTray.h"
37#include "VBoxTrayMsg.h"
38#include "VBoxHelpers.h"
39#include "VBoxIPC.h"
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/**
46 * IPC context data.
47 */
48typedef struct VBOXIPCCONTEXT
49{
50 /** Pointer to the service environment. */
51 const VBOXSERVICEENV *pEnv;
52 /** Handle for the local IPC server. */
53 RTLOCALIPCSERVER hServer;
54 /** Critical section serializing access to the session list, the state,
55 * the response event, the session event, and the thread event. */
56 RTCRITSECT CritSect;
57 /** List of all active IPC sessions. */
58 RTLISTANCHOR SessionList;
59
60} VBOXIPCCONTEXT, *PVBOXIPCCONTEXT;
61
62/** Function pointer for GetLastInputInfo(). */
63typedef BOOL (WINAPI *PFNGETLASTINPUTINFO)(PLASTINPUTINFO);
64
65/**
66 * IPC per-session thread data.
67 */
68typedef struct VBOXIPCSESSION
69{
70 /** The list node required to be part of the
71 * IPC session list. */
72 RTLISTNODE Node;
73 /** Pointer to the IPC context data. */
74 PVBOXIPCCONTEXT volatile pCtx;
75 /** The local ipc client handle. */
76 RTLOCALIPCSESSION volatile hSession;
77 /** Indicate that the thread should terminate ASAP. */
78 bool volatile fTerminate;
79 /** The thread handle. */
80 RTTHREAD hThread;
81
82} VBOXIPCSESSION, *PVBOXIPCSESSION;
83
84
85/*********************************************************************************************************************************
86* Global Variables *
87*********************************************************************************************************************************/
88static VBOXIPCCONTEXT g_Ctx = { NULL, NIL_RTLOCALIPCSERVER };
89static PFNGETLASTINPUTINFO g_pfnGetLastInputInfo = NULL;
90
91
92/*********************************************************************************************************************************
93* Internal Functions *
94*********************************************************************************************************************************/
95static int vboxIPCSessionStop(PVBOXIPCSESSION pSession);
96
97
98
99static int vboxIPCHandleVBoxTrayRestart(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
100{
101 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
102 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
103
104 /** @todo Not implemented yet; don't return an error here. */
105 return VINF_SUCCESS;
106}
107
108static int vboxIPCHandleShowBalloonMsg(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
109{
110 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
111 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
112 AssertReturn(pHdr->uMsgLen > 0, VERR_INVALID_PARAMETER);
113
114 VBOXTRAYIPCMSG_SHOWBALLOONMSG ipcMsg;
115 int rc = RTLocalIpcSessionRead(pSession->hSession, &ipcMsg, pHdr->uMsgLen,
116 NULL /* Exact read, blocking */);
117 if (RT_SUCCESS(rc))
118 {
119 /* Showing the balloon tooltip is not critical. */
120 int rc2 = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
121 ipcMsg.szMsgContent, ipcMsg.szMsgTitle,
122 ipcMsg.uShowMS, ipcMsg.uType);
123 LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n",
124 ipcMsg.szMsgTitle, ipcMsg.szMsgContent,
125 ipcMsg.uType, ipcMsg.uShowMS, rc2));
126 NOREF(rc2);
127 }
128
129 return rc;
130}
131
132static int vboxIPCHandleUserLastInput(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
133{
134 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
135 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
136 /* No actual message from client. */
137
138 int rc = VINF_SUCCESS;
139
140 bool fLastInputAvailable = false;
141 VBOXTRAYIPCRES_USERLASTINPUT ipcRes;
142 if (g_pfnGetLastInputInfo)
143 {
144 /* Note: This only works up to 49.7 days (= 2^32, 32-bit counter)
145 since Windows was started. */
146 LASTINPUTINFO lastInput;
147 lastInput.cbSize = sizeof(LASTINPUTINFO);
148 BOOL fRc = g_pfnGetLastInputInfo(&lastInput);
149 if (fRc)
150 {
151 ipcRes.uLastInput = (GetTickCount() - lastInput.dwTime) / 1000;
152 fLastInputAvailable = true;
153 }
154 else
155 rc = RTErrConvertFromWin32(GetLastError());
156 }
157
158 if (!fLastInputAvailable)
159 {
160 /* No last input available. */
161 ipcRes.uLastInput = UINT32_MAX;
162 }
163
164 int rc2 = RTLocalIpcSessionWrite(pSession->hSession, &ipcRes, sizeof(ipcRes));
165 if (RT_SUCCESS(rc))
166 rc = rc2;
167
168 return rc;
169}
170
171/**
172 * Initializes the IPC communication.
173 *
174 * @return IPRT status code.
175 * @param pEnv The IPC service's environment.
176 * @param ppInstance The instance pointer which refers to this object.
177 */
178DECLCALLBACK(int) VBoxIPCInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
179{
180 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
181 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
182
183 LogFlowFuncEnter();
184
185 PVBOXIPCCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
186 AssertPtr(pCtx);
187
188 int rc = RTCritSectInit(&pCtx->CritSect);
189 if (RT_SUCCESS(rc))
190 {
191 char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
192 memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
193 rc = RTProcQueryUsername(NIL_RTPROCESS,
194 &szPipeName[sizeof(VBOXTRAY_IPC_PIPE_PREFIX) - 1],
195 sizeof(szPipeName) - sizeof(VBOXTRAY_IPC_PIPE_PREFIX) + 1,
196 NULL /*pcbUser*/);
197 AssertRC(rc);
198 if (RT_SUCCESS(rc))
199 {
200 rc = RTLocalIpcServerCreate(&pCtx->hServer, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
201 AssertRC(rc);
202 if (RT_SUCCESS(rc))
203 {
204 pCtx->pEnv = pEnv;
205 RTListInit(&pCtx->SessionList);
206
207 *ppInstance = pCtx;
208
209 /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
210 g_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)
211 RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
212
213 LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
214 return VINF_SUCCESS;
215 }
216
217 }
218
219 RTCritSectDelete(&pCtx->CritSect);
220 }
221
222 LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
223 return rc;
224}
225
226DECLCALLBACK(void) VBoxIPCStop(void *pInstance)
227{
228 /* Can be NULL if VBoxIPCInit failed. */
229 if (!pInstance)
230 return;
231 AssertPtrReturnVoid(pInstance);
232
233 LogFlowFunc(("Stopping pInstance=%p\n", pInstance));
234
235 /* Shut down local IPC server. */
236 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
237 AssertPtr(pCtx);
238
239 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
240 {
241 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
242 if (RT_FAILURE(rc2))
243 LogFlowFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
244 }
245
246 /* Stop all remaining session threads. */
247 int rc = RTCritSectEnter(&pCtx->CritSect);
248 if (RT_SUCCESS(rc))
249 {
250 PVBOXIPCSESSION pSession;
251 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
252 {
253 int rc2 = vboxIPCSessionStop(pSession);
254 if (RT_FAILURE(rc2))
255 {
256 LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
257 pSession, rc2));
258 /* Keep going. */
259 }
260 }
261 }
262}
263
264DECLCALLBACK(void) VBoxIPCDestroy(void *pInstance)
265{
266 AssertPtrReturnVoid(pInstance);
267
268 LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
269
270 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
271 AssertPtr(pCtx);
272
273 /* Shut down local IPC server. */
274 int rc = RTCritSectEnter(&pCtx->CritSect);
275 if (RT_SUCCESS(rc))
276 {
277 rc = RTLocalIpcServerDestroy(pCtx->hServer);
278 if (RT_FAILURE(rc))
279 LogFlowFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc));
280
281 int rc2 = RTCritSectLeave(&pCtx->CritSect);
282 if (RT_SUCCESS(rc))
283 rc = rc2;
284 }
285
286 LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
287
288 /* Wait for all IPC session threads to shut down. */
289 bool fListIsEmpty = true;
290 do
291 {
292 int rc2 = RTCritSectEnter(&pCtx->CritSect);
293 if (RT_SUCCESS(rc2))
294 {
295 fListIsEmpty = RTListIsEmpty(&pCtx->SessionList);
296 rc2 = RTCritSectLeave(&pCtx->CritSect);
297
298 if (!fListIsEmpty) /* Don't hog CPU while waiting. */
299 RTThreadSleep(100);
300 }
301
302 if (RT_FAILURE(rc2))
303 break;
304
305 } while (!fListIsEmpty);
306
307 AssertMsg(fListIsEmpty,
308 ("Session thread list is not empty when it should\n"));
309
310 LogFlowFunc(("All remaining IPC sessions shut down\n"));
311
312 int rc2 = RTCritSectDelete(&pCtx->CritSect);
313 if (RT_SUCCESS(rc))
314 rc = rc2;
315
316 LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
317 pInstance, rc));
318}
319
320/**
321 * Services a client session.
322 *
323 * @returns VINF_SUCCESS.
324 * @param hThreadSelf The thread handle.
325 * @param pvSession Pointer to the session instance data.
326 */
327static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThreadSelf, void *pvSession)
328{
329 RT_NOREF(hThreadSelf);
330 PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
331 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
332 RTLOCALIPCSESSION hSession = pThis->hSession;
333 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
334
335 LogFlowFunc(("pThis=%p\n", pThis));
336
337 int rc = VINF_SUCCESS;
338
339 /*
340 * Process client requests until it quits or we're cancelled on termination.
341 */
342 while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
343 && RT_SUCCESS(rc))
344 {
345 /* The next call will be cancelled via VBoxIPCStop if needed. */
346 rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
347 if (RT_FAILURE(rc))
348 {
349 if (rc == VERR_CANCELLED)
350 {
351 LogFlowFunc(("Session %p: Waiting for data cancelled\n", pThis));
352 rc = VINF_SUCCESS;
353 break;
354 }
355 else
356 LogFlowFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n",
357 pThis, rc));
358 }
359 else
360 {
361 VBOXTRAYIPCHEADER ipcHdr;
362 rc = RTLocalIpcSessionRead(hSession, &ipcHdr, sizeof(ipcHdr),
363 NULL /* Exact read, blocking */);
364 bool fRejected = false; /* Reject current command? */
365 if (RT_SUCCESS(rc))
366 fRejected = ipcHdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC
367 || ipcHdr.uHdrVersion != 0; /* We only know version 0 commands for now. */
368
369 if ( !fRejected
370 && RT_SUCCESS(rc))
371 {
372 switch (ipcHdr.uMsgType)
373 {
374 case VBOXTRAYIPCMSGTYPE_RESTART:
375 rc = vboxIPCHandleVBoxTrayRestart(pThis, &ipcHdr);
376 break;
377
378 case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG:
379 rc = vboxIPCHandleShowBalloonMsg(pThis, &ipcHdr);
380 break;
381
382 case VBOXTRAYIPCMSGTYPE_USERLASTINPUT:
383 rc = vboxIPCHandleUserLastInput(pThis, &ipcHdr);
384 break;
385
386 default:
387 {
388 /* Unknown command, reject. */
389 fRejected = true;
390 break;
391 }
392 }
393
394 if (RT_FAILURE(rc))
395 LogFlowFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n",
396 pThis, ipcHdr.uMsgType, rc));
397 }
398
399 if (fRejected)
400 {
401 static int s_cRejectedCmds = 0;
402 if (++s_cRejectedCmds <= 3)
403 {
404 LogRelFunc(("Session %p: Received invalid/unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n",
405 pThis, ipcHdr.uMsgType, ipcHdr.uMsgLen, s_cRejectedCmds + 1));
406 if (ipcHdr.uMsgLen)
407 {
408 /* Get and discard payload data. */
409 size_t cbRead;
410 uint8_t devNull[_1K];
411 while (ipcHdr.uMsgLen)
412 {
413 rc = RTLocalIpcSessionRead(hSession, &devNull, sizeof(devNull), &cbRead);
414 if (RT_FAILURE(rc))
415 break;
416 AssertRelease(cbRead <= ipcHdr.uMsgLen);
417 ipcHdr.uMsgLen -= (uint32_t)cbRead;
418 }
419 }
420 }
421 else
422 rc = VERR_INVALID_PARAMETER; /* Enough fun, bail out. */
423 }
424 }
425 }
426
427 LogFlowFunc(("Session %p: Handler ended with rc=%Rrc\n",
428 pThis, rc));
429
430 /*
431 * Close the session.
432 */
433 int rc2 = RTLocalIpcSessionClose(hSession);
434 if (RT_FAILURE(rc2))
435 LogFlowFunc(("Session %p: Failed closing session %p, rc=%Rrc\n", pThis, rc2));
436
437 /*
438 * Clean up the session.
439 */
440 PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
441 AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
442 rc2 = RTCritSectEnter(&pCtx->CritSect);
443 if (RT_SUCCESS(rc2))
444 {
445 /* Remove this session from the session list. */
446 RTListNodeRemove(&pThis->Node);
447
448 rc2 = RTCritSectLeave(&pCtx->CritSect);
449 if (RT_SUCCESS(rc))
450 rc = rc2;
451 }
452
453 LogFlowFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n",
454 pThis, rc));
455
456 RTMemFree(pThis);
457 pThis = NULL;
458
459 return rc;
460}
461
462static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
463{
464 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
465 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
466
467 int rc = RTCritSectEnter(&pCtx->CritSect);
468 if (RT_SUCCESS(rc))
469 {
470 PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
471 if (pSession)
472 {
473 pSession->pCtx = pCtx;
474 pSession->hSession = hSession;
475 pSession->fTerminate = false;
476 pSession->hThread = NIL_RTTHREAD;
477
478 /* Start IPC session thread. */
479 LogFlowFunc(("Creating thread for session %p ...\n", pSession));
480 rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread,
481 pSession /* pvUser */, 0 /* Default stack size */,
482 RTTHREADTYPE_DEFAULT, 0 /* Flags */, "IPCSESSION");
483 if (RT_SUCCESS(rc))
484 {
485 /* Add session thread to session IPC list. */
486 RTListAppend(&pCtx->SessionList, &pSession->Node);
487 }
488 else
489 {
490 int rc2 = RTLocalIpcSessionClose(hSession);
491 if (RT_FAILURE(rc2))
492 LogFlowFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
493
494 LogFlowFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
495 RTMemFree(pSession);
496 }
497 }
498 else
499 rc = VERR_NO_MEMORY;
500
501 int rc2 = RTCritSectLeave(&pCtx->CritSect);
502 AssertRC(rc2);
503 }
504
505 return rc;
506}
507
508static int vboxIPCSessionStop(PVBOXIPCSESSION pSession)
509{
510 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
511
512 ASMAtomicWriteBool(&pSession->fTerminate, true);
513
514 RTLOCALIPCSESSION hSession;
515 ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
516 if (hSession)
517 return RTLocalIpcSessionClose(hSession);
518
519 return VINF_SUCCESS;
520}
521
522/**
523 * Thread function to wait for and process seamless mode change
524 * requests
525 */
526DECLCALLBACK(int) VBoxIPCWorker(void *pInstance, bool volatile *pfShutdown)
527{
528 AssertPtr(pInstance);
529 LogFlowFunc(("pInstance=%p\n", pInstance));
530
531 LogFlowFuncEnter();
532
533 /*
534 * Tell the control thread that it can continue
535 * spawning services.
536 */
537 RTThreadUserSignal(RTThreadSelf());
538
539 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
540 AssertPtr(pCtx);
541
542 int rc;
543
544 bool fShutdown = false;
545 for (;;)
546 {
547 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
548 rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
549 if (RT_FAILURE(rc))
550 {
551 if (rc == VERR_CANCELLED)
552 {
553 LogFlow(("Cancelled\n"));
554 fShutdown = true;
555 }
556 else
557 LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
558 }
559
560 if (fShutdown)
561 break;
562 rc = vboxIPCSessionCreate(pCtx, hClientSession);
563 if (RT_FAILURE(rc))
564 {
565 LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
566 /* Keep going. */
567 }
568
569 if (*pfShutdown)
570 break;
571 }
572
573 LogFlowFuncLeaveRC(rc);
574 return rc;
575}
576
577/**
578 * The service description.
579 */
580VBOXSERVICEDESC g_SvcDescIPC =
581{
582 /* pszName. */
583 "IPC",
584 /* pszDescription. */
585 "Inter-Process Communication",
586 /* methods */
587 VBoxIPCInit,
588 VBoxIPCWorker,
589 NULL /* pfnStop */,
590 VBoxIPCDestroy
591};
592
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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