VirtualBox

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

Last change on this file since 64285 was 64285, checked in by vboxsync, 8 years ago

Additions: bugref:8622: Use crc of user name in name of ipc pipe

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