VirtualBox

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

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

VBoxTray/IPC: Update, now supports retrieving last user input from current session (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: VBoxIPC.cpp 47211 2013-07-17 12:33:23Z 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/list.h>
30#include <iprt/localipc.h>
31#include <iprt/mem.h>
32#include <VBoxGuestInternal.h>
33
34
35
36/**
37 * IPC context data.
38 */
39typedef struct VBOXIPCCONTEXT
40{
41 /** Pointer to the service environment. */
42 const VBOXSERVICEENV *pEnv;
43 /** Handle for the local IPC server. */
44 RTLOCALIPCSERVER hServer;
45 /** Critical section serializing access to the session list, the state,
46 * the response event, the session event, and the thread event. */
47 RTCRITSECT CritSect;
48 /** List of all active IPC sessions. */
49 RTLISTANCHOR SessionList;
50
51} VBOXIPCCONTEXT, *PVBOXIPCCONTEXT;
52static VBOXIPCCONTEXT gCtx = {0};
53
54/**
55 * IPC per-session thread data.
56 */
57typedef struct VBOXIPCSESSION
58{
59 /** The list node required to be part of the
60 * IPC session list. */
61 RTLISTNODE Node;
62 /** Pointer to the IPC context data. */
63 PVBOXIPCCONTEXT volatile pCtx;
64 /** The local ipc client handle. */
65 RTLOCALIPCSESSION volatile hSession;
66 /** Indicate that the thread should terminate ASAP. */
67 bool volatile fTerminate;
68 /** The thread handle. */
69 RTTHREAD hThread;
70
71} VBOXIPCSESSION, *PVBOXIPCSESSION;
72
73int vboxIPCSessionDestroyLocked(PVBOXIPCSESSION pSession);
74
75static int vboxIPCHandleVBoxTrayRestart(RTLOCALIPCSESSION hSession, PVBOXTRAYIPCHEADER pHdr)
76{
77 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
78
79 /** @todo Not implemented yet; don't return an error here. */
80 return VINF_SUCCESS;
81}
82
83static int vboxIPCHandleShowBalloonMsg(RTLOCALIPCSESSION hSession, PVBOXTRAYIPCHEADER pHdr)
84{
85 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
86 AssertReturn(pHdr->uMsgLen > 0, VERR_INVALID_PARAMETER);
87
88 VBOXTRAYIPCMSG_SHOWBALLOONMSG ipcMsg;
89 int rc = RTLocalIpcSessionRead(hSession, &ipcMsg, pHdr->uMsgLen,
90 NULL /* Exact read, blocking */);
91 if (RT_SUCCESS(rc))
92 {
93 /* Showing the balloon tooltip is not critical. */
94 int rc2 = hlpShowBalloonTip(ghInstance, ghwndToolWindow, ID_TRAYICON,
95 ipcMsg.szMsgContent, ipcMsg.szMsgTitle,
96 ipcMsg.uShowMS, ipcMsg.uType);
97 LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n",
98 ipcMsg.szMsgTitle, ipcMsg.szMsgContent,
99 ipcMsg.uType, ipcMsg.uShowMS, rc2));
100 }
101
102 return rc;
103}
104
105static int vboxIPCHandleUserLastInput(RTLOCALIPCSESSION hSession, PVBOXTRAYIPCHEADER pHdr)
106{
107 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
108 /* No actual message from client. */
109
110 int rc = VINF_SUCCESS;
111
112 LASTINPUTINFO lastInput;
113 lastInput.cbSize = sizeof(LASTINPUTINFO);
114 BOOL fRc = GetLastInputInfo(&lastInput);
115 if (fRc)
116 {
117 VBOXTRAYIPCRES_USERLASTINPUT ipcRes;
118 ipcRes.uTickCount = lastInput.dwTime; /** @sa GetTickCount(). */
119 rc = RTLocalIpcSessionWrite(hSession, &ipcRes, sizeof(ipcRes));
120 }
121 else
122 rc = RTErrConvertFromWin32(GetLastError());
123
124 return rc;
125}
126
127/**
128 * Initializes the IPC communication.
129 *
130 * @return IPRT status code.
131 * @param pEnv The IPC service's environment.
132 * @param ppInstance The instance pointer which refer to this object.
133 * @param pfStartThread Pointer to flag whether the IPC service can be started or not.
134 */
135int VBoxIPCInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
136{
137 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
138 /** ppInstance not used here. */
139 AssertPtrReturn(pfStartThread, VERR_INVALID_POINTER);
140
141 LogFlowFuncEnter();
142
143 *pfStartThread = false;
144
145 gCtx.pEnv = pEnv;
146 gCtx.hServer = NIL_RTLOCALIPCSERVER;
147
148 int rc = RTCritSectInit(&gCtx.CritSect);
149 if (RT_SUCCESS(rc))
150 {
151 rc = RTLocalIpcServerCreate(&gCtx.hServer, VBOXTRAY_IPC_PIPENAME,
152 RTLOCALIPC_FLAGS_MULTI_SESSION);
153 if (RT_FAILURE(rc))
154 {
155 LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
156 return rc;
157 }
158
159 RTListInit(&gCtx.SessionList);
160
161 *pfStartThread = true;
162 }
163
164 return rc;
165}
166
167void VBoxIPCStop(const VBOXSERVICEENV *pEnv, void *pInstance)
168{
169 AssertPtrReturnVoid(pEnv);
170 AssertPtrReturnVoid(pInstance);
171
172 LogFunc(("Stopping pInstance=%p\n", pInstance));
173
174 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
175 AssertPtr(pCtx);
176
177 if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
178 {
179 int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
180 if (RT_FAILURE(rc2))
181 LogFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
182 }
183}
184
185void VBoxIPCDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
186{
187 AssertPtr(pEnv);
188 AssertPtr(pInstance);
189
190 LogFunc(("Destroying pInstance=%p\n", pInstance));
191
192 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
193 AssertPtr(pCtx);
194
195 int rc = RTCritSectEnter(&pCtx->CritSect);
196 if (RT_SUCCESS(rc))
197 {
198 PVBOXIPCSESSION pSession;
199 RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
200 {
201 int rc2 = vboxIPCSessionDestroyLocked(pSession);
202 if (RT_FAILURE(rc2))
203 {
204 LogFunc(("Destroying IPC session %p failed with rc=%Rrc\n",
205 pSession, rc2));
206 /* Keep going. */
207 }
208 }
209
210 RTLocalIpcServerDestroy(pCtx->hServer);
211
212 int rc2 = RTCritSectLeave(&pCtx->CritSect);
213 AssertRC(rc2);
214
215 rc2 = RTCritSectDelete(&pCtx->CritSect);
216 AssertRC(rc2);
217 }
218
219 LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
220 pInstance, rc));
221}
222
223/**
224 * Services a client session.
225 *
226 * @returns VINF_SUCCESS.
227 * @param hThread The thread handle.
228 * @param pvSession Pointer to the session instance data.
229 */
230static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThread, void *pvSession)
231{
232 PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
233 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
234 RTLOCALIPCSESSION hSession = pThis->hSession;
235 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
236
237 LogFunc(("pThis=%p\n", pThis));
238
239 int rc = VINF_SUCCESS;
240
241 /*
242 * Process client requests until it quits or we're cancelled on termination.
243 */
244 while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
245 && RT_SUCCESS(rc))
246 {
247 /* The next call will be cancelled via VBoxIPCStop if needed. */
248 rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
249 if (RT_FAILURE(rc))
250 {
251 if (rc == VERR_CANCELLED)
252 {
253 LogFunc(("Session %p: Waiting for data cancelled\n", pThis));
254 rc = VINF_SUCCESS;
255 break;
256 }
257 else
258 LogRelFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n",
259 pThis, rc));
260 }
261 else
262 {
263 VBOXTRAYIPCHEADER ipcHdr;
264 rc = RTLocalIpcSessionRead(hSession, &ipcHdr, sizeof(ipcHdr),
265 NULL /* Exact read, blocking */);
266 if (RT_SUCCESS(rc))
267 {
268 switch (ipcHdr.uMsgType)
269 {
270 case VBOXTRAYIPCMSGTYPE_RESTART:
271 rc = vboxIPCHandleVBoxTrayRestart(hSession, &ipcHdr);
272 break;
273
274 case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG:
275 rc = vboxIPCHandleShowBalloonMsg(hSession, &ipcHdr);
276 break;
277
278 case VBOXTRAYIPCMSGTYPE_USERLASTINPUT:
279 rc = vboxIPCHandleUserLastInput(hSession, &ipcHdr);
280 break;
281
282 default:
283 {
284 static int s_cRejectedCmds = 0;
285 if (++s_cRejectedCmds <= 3)
286 {
287 LogRelFunc(("Session %p: Received unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n",
288 pThis, ipcHdr.uMsgType, ipcHdr.uMsgLen, s_cRejectedCmds + 1));
289 if (ipcHdr.uMsgLen)
290 {
291 /* Get and discard payload data. */
292 size_t cbRead;
293 uint8_t devNull[_1K];
294 while (ipcHdr.uMsgLen)
295 {
296 rc = RTLocalIpcSessionRead(hSession, &devNull, sizeof(devNull), &cbRead);
297 if (RT_FAILURE(rc))
298 break;
299 AssertRelease(cbRead <= ipcHdr.uMsgLen);
300 ipcHdr.uMsgLen -= (uint32_t)cbRead;
301 }
302 }
303 }
304 else
305 rc = VERR_INVALID_PARAMETER; /* Enough fun, bail out. */
306 break;
307 }
308
309 if (RT_FAILURE(rc))
310 LogFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n",
311 pThis, ipcHdr.uMsgType, rc));
312 }
313 }
314 }
315 }
316
317 LogRelFunc(("Session %p: Handler ended with rc=%Rrc\n", rc));
318
319 /*
320 * Clean up the session.
321 */
322 PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
323 AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
324 rc = RTCritSectEnter(&pCtx->CritSect);
325 if (RT_SUCCESS(rc))
326 {
327 rc = vboxIPCSessionDestroyLocked(pThis);
328
329 int rc2 = RTCritSectLeave(&pCtx->CritSect);
330 if (RT_SUCCESS(rc))
331 rc = rc2;
332 }
333
334 LogFunc(("Session %p: Terminated\n", pThis));
335 return rc;
336}
337
338static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
339{
340 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
341 AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
342
343 int rc = RTCritSectEnter(&pCtx->CritSect);
344 if (RT_SUCCESS(rc))
345 {
346 PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
347 if (pSession)
348 {
349 pSession->pCtx = pCtx;
350 pSession->hSession = hSession;
351 pSession->fTerminate = false;
352 pSession->hThread = NIL_RTTHREAD;
353
354 /* Start IPC session thread. */
355 LogFlowFunc(("Creating thread for session %p ...\n", pSession));
356 rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread, pSession, 0,
357 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBXTRYIPCSESS");
358 if (RT_SUCCESS(rc))
359 {
360 /* Add session thread to session IPC list. */
361 RTListAppend(&pCtx->SessionList, &pSession->Node);
362 }
363 else
364 {
365 int rc2 = RTLocalIpcSessionClose(hSession);
366 if (RT_FAILURE(rc2))
367 LogFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
368
369 LogFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
370 RTMemFree(pSession);
371 }
372 }
373 else
374 rc = VERR_NO_MEMORY;
375
376 int rc2 = RTCritSectLeave(&pCtx->CritSect);
377 AssertRC(rc2);
378 }
379
380 return rc;
381}
382
383static int vboxIPCSessionDestroyLocked(PVBOXIPCSESSION pSession)
384{
385 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
386 pSession->hThread = NIL_RTTHREAD;
387
388 RTLOCALIPCSESSION hSession;
389 ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
390 int rc = RTLocalIpcSessionClose(hSession);
391
392 RTListNodeRemove(&pSession->Node);
393
394 RTMemFree(pSession);
395 pSession = NULL;
396
397 return rc;
398}
399
400/**
401 * Thread function to wait for and process seamless mode change
402 * requests
403 */
404unsigned __stdcall VBoxIPCThread(void *pInstance)
405{
406 LogFlowFuncEnter();
407
408 PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
409 AssertPtr(pCtx);
410
411 bool fShutdown = false;
412 for (;;)
413 {
414 RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
415 int rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
416 if (RT_FAILURE(rc))
417 {
418 if (rc == VERR_CANCELLED)
419 {
420 LogFlow(("Cancelled\n"));
421 fShutdown = true;
422 }
423 else
424 LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
425 }
426
427 if (fShutdown)
428 break;
429 rc = vboxIPCSessionCreate(pCtx, hClientSession);
430 if (RT_FAILURE(rc))
431 {
432 LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
433 /* Keep going. */
434 }
435
436 AssertPtr(pCtx->pEnv);
437 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0 /* No waiting */) == WAIT_OBJECT_0)
438 break;
439 }
440
441 LogFlowFuncLeave();
442 return 0;
443}
444
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