VirtualBox

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

Last change on this file since 79506 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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