VirtualBox

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

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

RTLocalIpcServerCreate: Dropped the RTLOCALIPC_FLAGS_MULTI_SESSION as it's not easy to implement single session servers on windows and especially linux.

  • 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 58305 2015-10-18 23:41:37Z 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, 0 /*fFlags*/);
198 if (RT_SUCCESS(rc))
199 {
200 RTStrFree(pszUserName);
201
202 pCtx->pEnv = pEnv;
203 RTListInit(&pCtx->SessionList);
204
205 *ppInstance = pCtx;
206
207 /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
208 s_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)
209 RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
210
211 LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
212 return VINF_SUCCESS;
213 }
214
215 }
216 else
217 rc = VERR_NO_MEMORY;
218
219 RTStrFree(pszUserName);
220 }
221 }
222
223 RTCritSectDelete(&pCtx->CritSect);
224 }
225
226 LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
227 return rc;
228}
229
230DECLCALLBACK(void) VBoxIPCStop(void *pInstance)
231{
232 AssertPtrReturnVoid(pInstance);
233
234 LogFlowFunc(("Stopping pInstance=%p\n", pInstance));
235
236 /* Shut down local IPC server. */
237 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
238 AssertPtr(pCtx);
239
240 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
241 {
242 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
243 if (RT_FAILURE(rc2))
244 LogFlowFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
245 }
246
247 /* Stop all remaining session threads. */
248 int rc = RTCritSectEnter(&pCtx->CritSect);
249 if (RT_SUCCESS(rc))
250 {
251 PVBOXIPCSESSION pSession;
252 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
253 {
254 int rc2 = vboxIPCSessionStop(pSession);
255 if (RT_FAILURE(rc2))
256 {
257 LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
258 pSession, rc2));
259 /* Keep going. */
260 }
261 }
262 }
263}
264
265DECLCALLBACK(void) VBoxIPCDestroy(void *pInstance)
266{
267 AssertPtrReturnVoid(pInstance);
268
269 LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
270
271 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
272 AssertPtr(pCtx);
273
274 /* Shut down local IPC server. */
275 int rc = RTCritSectEnter(&pCtx->CritSect);
276 if (RT_SUCCESS(rc))
277 {
278 rc = RTLocalIpcServerDestroy(pCtx->hServer);
279 if (RT_FAILURE(rc))
280 LogFlowFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc));
281
282 int rc2 = RTCritSectLeave(&pCtx->CritSect);
283 if (RT_SUCCESS(rc))
284 rc = rc2;
285 }
286
287 LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
288
289 /* Wait for all IPC session threads to shut down. */
290 bool fListIsEmpty = true;
291 do
292 {
293 int rc2 = RTCritSectEnter(&pCtx->CritSect);
294 if (RT_SUCCESS(rc2))
295 {
296 fListIsEmpty = RTListIsEmpty(&pCtx->SessionList);
297 rc2 = RTCritSectLeave(&pCtx->CritSect);
298
299 if (!fListIsEmpty) /* Don't hog CPU while waiting. */
300 RTThreadSleep(100);
301 }
302
303 if (RT_FAILURE(rc2))
304 break;
305
306 } while (!fListIsEmpty);
307
308 AssertMsg(fListIsEmpty,
309 ("Session thread list is not empty when it should\n"));
310
311 LogFlowFunc(("All remaining IPC sessions shut down\n"));
312
313 int rc2 = RTCritSectDelete(&pCtx->CritSect);
314 if (RT_SUCCESS(rc))
315 rc = rc2;
316
317 LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
318 pInstance, rc));
319}
320
321/**
322 * Services a client session.
323 *
324 * @returns VINF_SUCCESS.
325 * @param hThread The thread handle.
326 * @param pvSession Pointer to the session instance data.
327 */
328static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThread, void *pvSession)
329{
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