VirtualBox

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

Last change on this file since 50039 was 50039, checked in by vboxsync, 11 years ago

VBoxTray/IPC: Fixed handle leaks.

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

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