VirtualBox

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

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

GA/NT/VBoxTray: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 KB
Line 
1/* $Id: VBoxIPC.cpp 63100 2016-08-06 15:44:22Z 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* Header Files *
22*********************************************************************************************************************************/
23#include <iprt/win/windows.h>
24#include "VBoxTray.h"
25#include "VBoxTrayMsg.h"
26#include "VBoxHelpers.h"
27#include "VBoxIPC.h"
28
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/critsect.h>
32#include <iprt/err.h>
33#include <iprt/ldr.h>
34#include <iprt/list.h>
35#include <iprt/localipc.h>
36#include <iprt/mem.h>
37#include <iprt/process.h>
38
39#include <VBox/VMMDev.h>
40#ifdef DEBUG
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 strcpy(szPipeName, 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 if (RT_SUCCESS(rc))
203 {
204 rc = RTLocalIpcServerCreate(&pCtx->hServer, szPipeName, 0 /*fFlags*/);
205 if (RT_SUCCESS(rc))
206 {
207 pCtx->pEnv = pEnv;
208 RTListInit(&pCtx->SessionList);
209
210 *ppInstance = pCtx;
211
212 /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
213 g_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)
214 RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
215
216 LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
217 return VINF_SUCCESS;
218 }
219
220 }
221
222 RTCritSectDelete(&pCtx->CritSect);
223 }
224
225 LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
226 return rc;
227}
228
229DECLCALLBACK(void) VBoxIPCStop(void *pInstance)
230{
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
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