VirtualBox

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

Last change on this file since 59010 was 58307, checked in by vboxsync, 9 years ago

Additions/WINNT/VBoxGuestInternal.h: Removed empty misnamed file. VBoxGuestInternal.h is the internal header for Additions/common/VBoxGuest, not for the WINNT additions. Very confusing and pointless.

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