VirtualBox

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

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

VBoxService/VBoxTray: User idle detection: Reset usage status to idle when VBoxTray exits, cleanup.

  • 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 47977 2013-08-21 16:37:27Z 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/** 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 vboxIPCSessionDestroyLocked(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 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
234 AssertPtr(pCtx);
235
236 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
237 {
238 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
239 if (RT_FAILURE(rc2))
240 LogFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
241 }
242}
243
244void VBoxIPCDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
245{
246 AssertPtr(pEnv);
247 AssertPtr(pInstance);
248
249 LogFunc(("Destroying pInstance=%p\n", pInstance));
250
251 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
252 AssertPtr(pCtx);
253
254 int rc = RTCritSectEnter(&pCtx->CritSect);
255 if (RT_SUCCESS(rc))
256 {
257 PVBOXIPCSESSION pSession;
258 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
259 {
260 int rc2 = vboxIPCSessionDestroyLocked(pSession);
261 if (RT_FAILURE(rc2))
262 {
263 LogFunc(("Destroying IPC session %p failed with rc=%Rrc\n",
264 pSession, rc2));
265 /* Keep going. */
266 }
267 }
268
269 RTLocalIpcServerDestroy(pCtx->hServer);
270
271 int rc2 = RTCritSectLeave(&pCtx->CritSect);
272 AssertRC(rc2);
273
274 rc2 = RTCritSectDelete(&pCtx->CritSect);
275 AssertRC(rc2);
276 }
277
278 LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
279 pInstance, rc));
280}
281
282/**
283 * Services a client session.
284 *
285 * @returns VINF_SUCCESS.
286 * @param hThread The thread handle.
287 * @param pvSession Pointer to the session instance data.
288 */
289static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThread, void *pvSession)
290{
291 PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
292 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
293 RTLOCALIPCSESSION hSession = pThis->hSession;
294 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
295
296 LogFunc(("pThis=%p\n", pThis));
297
298 int rc = VINF_SUCCESS;
299
300 /*
301 * Process client requests until it quits or we're cancelled on termination.
302 */
303 while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
304 && RT_SUCCESS(rc))
305 {
306 /* The next call will be cancelled via VBoxIPCStop if needed. */
307 rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
308 if (RT_FAILURE(rc))
309 {
310 if (rc == VERR_CANCELLED)
311 {
312 LogFunc(("Session %p: Waiting for data cancelled\n", pThis));
313 rc = VINF_SUCCESS;
314 break;
315 }
316 else
317 LogRelFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n",
318 pThis, rc));
319 }
320 else
321 {
322 VBOXTRAYIPCHEADER ipcHdr;
323 rc = RTLocalIpcSessionRead(hSession, &ipcHdr, sizeof(ipcHdr),
324 NULL /* Exact read, blocking */);
325 bool fRejected = false; /* Reject current command? */
326 if (RT_SUCCESS(rc))
327 fRejected = ipcHdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC
328 || ipcHdr.uHdrVersion != 0; /* We only know version 0 commands for now. */
329
330 if ( !fRejected
331 && RT_SUCCESS(rc))
332 {
333 switch (ipcHdr.uMsgType)
334 {
335 case VBOXTRAYIPCMSGTYPE_RESTART:
336 rc = vboxIPCHandleVBoxTrayRestart(pThis, &ipcHdr);
337 break;
338
339 case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG:
340 rc = vboxIPCHandleShowBalloonMsg(pThis, &ipcHdr);
341 break;
342
343 case VBOXTRAYIPCMSGTYPE_USERLASTINPUT:
344 rc = vboxIPCHandleUserLastInput(pThis, &ipcHdr);
345 break;
346
347 default:
348 {
349 /* Unknown command, reject. */
350 fRejected = true;
351 break;
352 }
353 }
354
355 if (RT_FAILURE(rc))
356 LogFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n",
357 pThis, ipcHdr.uMsgType, rc));
358 }
359
360 if (fRejected)
361 {
362 static int s_cRejectedCmds = 0;
363 if (++s_cRejectedCmds <= 3)
364 {
365 LogRelFunc(("Session %p: Received invalid/unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n",
366 pThis, ipcHdr.uMsgType, ipcHdr.uMsgLen, s_cRejectedCmds + 1));
367 if (ipcHdr.uMsgLen)
368 {
369 /* Get and discard payload data. */
370 size_t cbRead;
371 uint8_t devNull[_1K];
372 while (ipcHdr.uMsgLen)
373 {
374 rc = RTLocalIpcSessionRead(hSession, &devNull, sizeof(devNull), &cbRead);
375 if (RT_FAILURE(rc))
376 break;
377 AssertRelease(cbRead <= ipcHdr.uMsgLen);
378 ipcHdr.uMsgLen -= (uint32_t)cbRead;
379 }
380 }
381 }
382 else
383 rc = VERR_INVALID_PARAMETER; /* Enough fun, bail out. */
384 }
385 }
386 }
387
388 LogRelFunc(("Session %p: Handler ended with rc=%Rrc\n", rc));
389
390 /*
391 * Clean up the session.
392 */
393 PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
394 AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
395 rc = RTCritSectEnter(&pCtx->CritSect);
396 if (RT_SUCCESS(rc))
397 {
398 rc = vboxIPCSessionDestroyLocked(pThis);
399
400 int rc2 = RTCritSectLeave(&pCtx->CritSect);
401 if (RT_SUCCESS(rc))
402 rc = rc2;
403 }
404
405 LogFunc(("Session %p: Terminated\n", pThis));
406 return rc;
407}
408
409static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
410{
411 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
412 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
413
414 int rc = RTCritSectEnter(&pCtx->CritSect);
415 if (RT_SUCCESS(rc))
416 {
417 PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
418 if (pSession)
419 {
420 pSession->pCtx = pCtx;
421 pSession->hSession = hSession;
422 pSession->fTerminate = false;
423 pSession->hThread = NIL_RTTHREAD;
424
425 /* Start IPC session thread. */
426 LogFlowFunc(("Creating thread for session %p ...\n", pSession));
427 rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread, pSession, 0,
428 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBXTRYIPCSESS");
429 if (RT_SUCCESS(rc))
430 {
431 /* Add session thread to session IPC list. */
432 RTListAppend(&pCtx->SessionList, &pSession->Node);
433 }
434 else
435 {
436 int rc2 = RTLocalIpcSessionClose(hSession);
437 if (RT_FAILURE(rc2))
438 LogFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
439
440 LogFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
441 RTMemFree(pSession);
442 }
443 }
444 else
445 rc = VERR_NO_MEMORY;
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
460 RTLOCALIPCSESSION hSession;
461 ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
462 int rc = RTLocalIpcSessionClose(hSession);
463
464 RTListNodeRemove(&pSession->Node);
465
466 RTMemFree(pSession);
467 pSession = NULL;
468
469 return rc;
470}
471
472/**
473 * Thread function to wait for and process seamless mode change
474 * requests
475 */
476unsigned __stdcall VBoxIPCThread(void *pInstance)
477{
478 LogFlowFuncEnter();
479
480 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
481 AssertPtr(pCtx);
482
483 bool fShutdown = false;
484 for (;;)
485 {
486 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
487 int rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
488 if (RT_FAILURE(rc))
489 {
490 if (rc == VERR_CANCELLED)
491 {
492 LogFlow(("Cancelled\n"));
493 fShutdown = true;
494 }
495 else
496 LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
497 }
498
499 if (fShutdown)
500 break;
501 rc = vboxIPCSessionCreate(pCtx, hClientSession);
502 if (RT_FAILURE(rc))
503 {
504 LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
505 /* Keep going. */
506 }
507
508 AssertPtr(pCtx->pEnv);
509 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0 /* No waiting */) == WAIT_OBJECT_0)
510 break;
511 }
512
513 LogFlowFuncLeave();
514 return 0;
515}
516
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