VirtualBox

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

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

Additions/VBoxTray:

  • Refactored internal services to use the RTThread API.
  • First take on cleaning up VBoxTray, separating the services more and more. See @todos.
  • Updated some code areas where deprecated APIs were used.
  • A lot of log formatting fixes and renaming.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: VBoxIPC.cpp 57741 2015-09-14 15:24:42Z 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 <VBoxGuestInternal.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 RTUTF16 wszUserName[255];
182 DWORD cchUserName = sizeof(wszUserName) / sizeof(RTUTF16);
183 BOOL fRc = GetUserNameW(wszUserName, &cchUserName);
184 if (!fRc)
185 rc = RTErrConvertFromWin32(GetLastError());
186
187 if (RT_SUCCESS(rc))
188 {
189 char *pszUserName;
190 rc = RTUtf16ToUtf8(wszUserName, &pszUserName);
191 if (RT_SUCCESS(rc))
192 {
193 char szPipeName[255];
194 if (RTStrPrintf(szPipeName, sizeof(szPipeName), "%s%s",
195 VBOXTRAY_IPC_PIPE_PREFIX, pszUserName))
196 {
197 rc = RTLocalIpcServerCreate(&pCtx->hServer, szPipeName,
198 RTLOCALIPC_FLAGS_MULTI_SESSION);
199 if (RT_SUCCESS(rc))
200 {
201 RTStrFree(pszUserName);
202
203 pCtx->pEnv = pEnv;
204 RTListInit(&pCtx->SessionList);
205
206 *ppInstance = pCtx;
207
208 /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
209 s_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)
210 RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
211
212 LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
213 return VINF_SUCCESS;
214 }
215
216 }
217 else
218 rc = VERR_NO_MEMORY;
219
220 RTStrFree(pszUserName);
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 AssertPtrReturnVoid(pInstance);
234
235 LogFlowFunc(("Stopping pInstance=%p\n", pInstance));
236
237 /* Shut down local IPC server. */
238 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
239 AssertPtr(pCtx);
240
241 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
242 {
243 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
244 if (RT_FAILURE(rc2))
245 LogFlowFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
246 }
247
248 /* Stop all remaining session threads. */
249 int rc = RTCritSectEnter(&pCtx->CritSect);
250 if (RT_SUCCESS(rc))
251 {
252 PVBOXIPCSESSION pSession;
253 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
254 {
255 int rc2 = vboxIPCSessionStop(pSession);
256 if (RT_FAILURE(rc2))
257 {
258 LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
259 pSession, rc2));
260 /* Keep going. */
261 }
262 }
263 }
264}
265
266DECLCALLBACK(void) VBoxIPCDestroy(void *pInstance)
267{
268 AssertPtrReturnVoid(pInstance);
269
270 LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
271
272 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
273 AssertPtr(pCtx);
274
275 /* Shut down local IPC server. */
276 int rc = RTCritSectEnter(&pCtx->CritSect);
277 if (RT_SUCCESS(rc))
278 {
279 rc = RTLocalIpcServerDestroy(pCtx->hServer);
280 if (RT_FAILURE(rc))
281 LogFlowFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc));
282
283 int rc2 = RTCritSectLeave(&pCtx->CritSect);
284 if (RT_SUCCESS(rc))
285 rc = rc2;
286 }
287
288 LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
289
290 /* Wait for all IPC session threads to shut down. */
291 bool fListIsEmpty = true;
292 do
293 {
294 int rc2 = RTCritSectEnter(&pCtx->CritSect);
295 if (RT_SUCCESS(rc2))
296 {
297 fListIsEmpty = RTListIsEmpty(&pCtx->SessionList);
298 rc2 = RTCritSectLeave(&pCtx->CritSect);
299
300 if (!fListIsEmpty) /* Don't hog CPU while waiting. */
301 RTThreadSleep(100);
302 }
303
304 if (RT_FAILURE(rc2))
305 break;
306
307 } while (!fListIsEmpty);
308
309 AssertMsg(fListIsEmpty,
310 ("Session thread list is not empty when it should\n"));
311
312 LogFlowFunc(("All remaining IPC sessions shut down\n"));
313
314 int rc2 = RTCritSectDelete(&pCtx->CritSect);
315 if (RT_SUCCESS(rc))
316 rc = rc2;
317
318 LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
319 pInstance, rc));
320}
321
322/**
323 * Services a client session.
324 *
325 * @returns VINF_SUCCESS.
326 * @param hThread The thread handle.
327 * @param pvSession Pointer to the session instance data.
328 */
329static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThread, void *pvSession)
330{
331 PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
332 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
333 RTLOCALIPCSESSION hSession = pThis->hSession;
334 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
335
336 LogFlowFunc(("pThis=%p\n", pThis));
337
338 int rc = VINF_SUCCESS;
339
340 /*
341 * Process client requests until it quits or we're cancelled on termination.
342 */
343 while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
344 && RT_SUCCESS(rc))
345 {
346 /* The next call will be cancelled via VBoxIPCStop if needed. */
347 rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
348 if (RT_FAILURE(rc))
349 {
350 if (rc == VERR_CANCELLED)
351 {
352 LogFlowFunc(("Session %p: Waiting for data cancelled\n", pThis));
353 rc = VINF_SUCCESS;
354 break;
355 }
356 else
357 LogFlowFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n",
358 pThis, rc));
359 }
360 else
361 {
362 VBOXTRAYIPCHEADER ipcHdr;
363 rc = RTLocalIpcSessionRead(hSession, &ipcHdr, sizeof(ipcHdr),
364 NULL /* Exact read, blocking */);
365 bool fRejected = false; /* Reject current command? */
366 if (RT_SUCCESS(rc))
367 fRejected = ipcHdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC
368 || ipcHdr.uHdrVersion != 0; /* We only know version 0 commands for now. */
369
370 if ( !fRejected
371 && RT_SUCCESS(rc))
372 {
373 switch (ipcHdr.uMsgType)
374 {
375 case VBOXTRAYIPCMSGTYPE_RESTART:
376 rc = vboxIPCHandleVBoxTrayRestart(pThis, &ipcHdr);
377 break;
378
379 case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG:
380 rc = vboxIPCHandleShowBalloonMsg(pThis, &ipcHdr);
381 break;
382
383 case VBOXTRAYIPCMSGTYPE_USERLASTINPUT:
384 rc = vboxIPCHandleUserLastInput(pThis, &ipcHdr);
385 break;
386
387 default:
388 {
389 /* Unknown command, reject. */
390 fRejected = true;
391 break;
392 }
393 }
394
395 if (RT_FAILURE(rc))
396 LogFlowFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n",
397 pThis, ipcHdr.uMsgType, rc));
398 }
399
400 if (fRejected)
401 {
402 static int s_cRejectedCmds = 0;
403 if (++s_cRejectedCmds <= 3)
404 {
405 LogRelFunc(("Session %p: Received invalid/unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n",
406 pThis, ipcHdr.uMsgType, ipcHdr.uMsgLen, s_cRejectedCmds + 1));
407 if (ipcHdr.uMsgLen)
408 {
409 /* Get and discard payload data. */
410 size_t cbRead;
411 uint8_t devNull[_1K];
412 while (ipcHdr.uMsgLen)
413 {
414 rc = RTLocalIpcSessionRead(hSession, &devNull, sizeof(devNull), &cbRead);
415 if (RT_FAILURE(rc))
416 break;
417 AssertRelease(cbRead <= ipcHdr.uMsgLen);
418 ipcHdr.uMsgLen -= (uint32_t)cbRead;
419 }
420 }
421 }
422 else
423 rc = VERR_INVALID_PARAMETER; /* Enough fun, bail out. */
424 }
425 }
426 }
427
428 LogFlowFunc(("Session %p: Handler ended with rc=%Rrc\n",
429 pThis, rc));
430
431 /*
432 * Close the session.
433 */
434 int rc2 = RTLocalIpcSessionClose(hSession);
435 if (RT_FAILURE(rc2))
436 LogFlowFunc(("Session %p: Failed closing session %p, rc=%Rrc\n", pThis, rc2));
437
438 /*
439 * Clean up the session.
440 */
441 PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
442 AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
443 rc2 = RTCritSectEnter(&pCtx->CritSect);
444 if (RT_SUCCESS(rc2))
445 {
446 /* Remove this session from the session list. */
447 RTListNodeRemove(&pThis->Node);
448
449 rc2 = RTCritSectLeave(&pCtx->CritSect);
450 if (RT_SUCCESS(rc))
451 rc = rc2;
452 }
453
454 LogFlowFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n",
455 pThis, rc));
456
457 RTMemFree(pThis);
458 pThis = NULL;
459
460 return rc;
461}
462
463static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
464{
465 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
466 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
467
468 int rc = RTCritSectEnter(&pCtx->CritSect);
469 if (RT_SUCCESS(rc))
470 {
471 PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
472 if (pSession)
473 {
474 pSession->pCtx = pCtx;
475 pSession->hSession = hSession;
476 pSession->fTerminate = false;
477 pSession->hThread = NIL_RTTHREAD;
478
479 /* Start IPC session thread. */
480 LogFlowFunc(("Creating thread for session %p ...\n", pSession));
481 rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread,
482 pSession /* pvUser */, 0 /* Default stack size */,
483 RTTHREADTYPE_DEFAULT, 0 /* Flags */, "IPCSESSION");
484 if (RT_SUCCESS(rc))
485 {
486 /* Add session thread to session IPC list. */
487 RTListAppend(&pCtx->SessionList, &pSession->Node);
488 }
489 else
490 {
491 int rc2 = RTLocalIpcSessionClose(hSession);
492 if (RT_FAILURE(rc2))
493 LogFlowFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
494
495 LogFlowFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
496 RTMemFree(pSession);
497 }
498 }
499 else
500 rc = VERR_NO_MEMORY;
501
502 int rc2 = RTCritSectLeave(&pCtx->CritSect);
503 AssertRC(rc2);
504 }
505
506 return rc;
507}
508
509static int vboxIPCSessionStop(PVBOXIPCSESSION pSession)
510{
511 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
512
513 ASMAtomicWriteBool(&pSession->fTerminate, true);
514
515 RTLOCALIPCSESSION hSession;
516 ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
517 if (hSession)
518 return RTLocalIpcSessionClose(hSession);
519
520 return VINF_SUCCESS;
521}
522
523/**
524 * Thread function to wait for and process seamless mode change
525 * requests
526 */
527DECLCALLBACK(int) VBoxIPCWorker(void *pInstance, bool volatile *pfShutdown)
528{
529 AssertPtr(pInstance);
530 LogFlowFunc(("pInstance=%p\n", pInstance));
531
532 LogFlowFuncEnter();
533
534 /*
535 * Tell the control thread that it can continue
536 * spawning services.
537 */
538 RTThreadUserSignal(RTThreadSelf());
539
540 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
541 AssertPtr(pCtx);
542
543 int rc;
544
545 bool fShutdown = false;
546 for (;;)
547 {
548 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
549 rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
550 if (RT_FAILURE(rc))
551 {
552 if (rc == VERR_CANCELLED)
553 {
554 LogFlow(("Cancelled\n"));
555 fShutdown = true;
556 }
557 else
558 LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
559 }
560
561 if (fShutdown)
562 break;
563 rc = vboxIPCSessionCreate(pCtx, hClientSession);
564 if (RT_FAILURE(rc))
565 {
566 LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
567 /* Keep going. */
568 }
569
570 if (*pfShutdown)
571 break;
572 }
573
574 LogFlowFuncLeaveRC(rc);
575 return rc;
576}
577
578/**
579 * The service description.
580 */
581VBOXSERVICEDESC g_SvcDescIPC =
582{
583 /* pszName. */
584 "IPC",
585 /* pszDescription. */
586 "Inter-Process Communication",
587 /* methods */
588 VBoxIPCInit,
589 VBoxIPCWorker,
590 NULL /* pfnStop */,
591 VBoxIPCDestroy
592};
593
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