VirtualBox

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

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

VBoxService/VBoxTray: User idle detection: Lower last input time granularity from milliseconds to seconds in order to also support periods > 49 days in a row.

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