VirtualBox

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

Last change on this file since 53517 was 51469, checked in by vboxsync, 11 years ago

VBoxTray: Logging; ripped out all custom logging.

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