VirtualBox

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

Last change on this file since 50752 was 50040, checked in by vboxsync, 11 years ago

VBoxTray/IPC: Whops.

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