[34085] | 1 | /* $Id: VBoxIPC.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[47195] | 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.
|
---|
[34085] | 6 | */
|
---|
| 7 |
|
---|
| 8 | /*
|
---|
[98103] | 9 | * Copyright (C) 2010-2023 Oracle and/or its affiliates.
|
---|
[34085] | 10 | *
|
---|
[96407] | 11 | * This file is part of VirtualBox base platform packages, as
|
---|
| 12 | * available from https://www.virtualbox.org.
|
---|
| 13 | *
|
---|
| 14 | * This program is free software; you can redistribute it and/or
|
---|
| 15 | * modify it under the terms of the GNU General Public License
|
---|
| 16 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 17 | * License.
|
---|
| 18 | *
|
---|
| 19 | * This program is distributed in the hope that it will be useful, but
|
---|
| 20 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 22 | * General Public License for more details.
|
---|
| 23 | *
|
---|
| 24 | * You should have received a copy of the GNU General Public License
|
---|
| 25 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 26 | *
|
---|
| 27 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[34085] | 28 | */
|
---|
[63100] | 29 |
|
---|
[63549] | 30 |
|
---|
[63100] | 31 | /*********************************************************************************************************************************
|
---|
| 32 | * Header Files *
|
---|
| 33 | *********************************************************************************************************************************/
|
---|
[47195] | 34 | #include <iprt/asm.h>
|
---|
[34085] | 35 | #include <iprt/assert.h>
|
---|
[47195] | 36 | #include <iprt/critsect.h>
|
---|
[76474] | 37 | #include <iprt/errcore.h>
|
---|
[47960] | 38 | #include <iprt/ldr.h>
|
---|
[47195] | 39 | #include <iprt/list.h>
|
---|
| 40 | #include <iprt/localipc.h>
|
---|
[95961] | 41 | #include <iprt/log.h>
|
---|
[34097] | 42 | #include <iprt/mem.h>
|
---|
[58306] | 43 | #include <iprt/process.h>
|
---|
[95961] | 44 | #include <iprt/win/windows.h>
|
---|
[58306] | 45 |
|
---|
[95961] | 46 | #include "VBoxTray.h"
|
---|
| 47 | #include "VBoxTrayMsg.h"
|
---|
| 48 | #include "VBoxHelpers.h"
|
---|
| 49 | #include "VBoxIPC.h"
|
---|
[34357] | 50 |
|
---|
[95961] | 51 |
|
---|
[63100] | 52 | /*********************************************************************************************************************************
|
---|
| 53 | * Structures and Typedefs *
|
---|
| 54 | *********************************************************************************************************************************/
|
---|
[47195] | 55 | /**
|
---|
| 56 | * IPC context data.
|
---|
| 57 | */
|
---|
| 58 | typedef struct VBOXIPCCONTEXT
|
---|
[34085] | 59 | {
|
---|
[47195] | 60 | /** Pointer to the service environment. */
|
---|
| 61 | const VBOXSERVICEENV *pEnv;
|
---|
| 62 | /** Handle for the local IPC server. */
|
---|
| 63 | RTLOCALIPCSERVER hServer;
|
---|
| 64 | /** Critical section serializing access to the session list, the state,
|
---|
| 65 | * the response event, the session event, and the thread event. */
|
---|
| 66 | RTCRITSECT CritSect;
|
---|
| 67 | /** List of all active IPC sessions. */
|
---|
| 68 | RTLISTANCHOR SessionList;
|
---|
[34085] | 69 |
|
---|
| 70 | } VBOXIPCCONTEXT, *PVBOXIPCCONTEXT;
|
---|
| 71 |
|
---|
[47977] | 72 | /** Function pointer for GetLastInputInfo(). */
|
---|
| 73 | typedef BOOL (WINAPI *PFNGETLASTINPUTINFO)(PLASTINPUTINFO);
|
---|
| 74 |
|
---|
[47195] | 75 | /**
|
---|
| 76 | * IPC per-session thread data.
|
---|
| 77 | */
|
---|
| 78 | typedef struct VBOXIPCSESSION
|
---|
| 79 | {
|
---|
| 80 | /** The list node required to be part of the
|
---|
| 81 | * IPC session list. */
|
---|
| 82 | RTLISTNODE Node;
|
---|
| 83 | /** Pointer to the IPC context data. */
|
---|
| 84 | PVBOXIPCCONTEXT volatile pCtx;
|
---|
| 85 | /** The local ipc client handle. */
|
---|
| 86 | RTLOCALIPCSESSION volatile hSession;
|
---|
| 87 | /** Indicate that the thread should terminate ASAP. */
|
---|
| 88 | bool volatile fTerminate;
|
---|
| 89 | /** The thread handle. */
|
---|
| 90 | RTTHREAD hThread;
|
---|
[34357] | 91 |
|
---|
[47195] | 92 | } VBOXIPCSESSION, *PVBOXIPCSESSION;
|
---|
| 93 |
|
---|
[47977] | 94 |
|
---|
[63100] | 95 | /*********************************************************************************************************************************
|
---|
| 96 | * Global Variables *
|
---|
| 97 | *********************************************************************************************************************************/
|
---|
| 98 | static VBOXIPCCONTEXT g_Ctx = { NULL, NIL_RTLOCALIPCSERVER };
|
---|
| 99 | static PFNGETLASTINPUTINFO g_pfnGetLastInputInfo = NULL;
|
---|
[47195] | 100 |
|
---|
[63100] | 101 |
|
---|
| 102 | /*********************************************************************************************************************************
|
---|
| 103 | * Internal Functions *
|
---|
| 104 | *********************************************************************************************************************************/
|
---|
| 105 | static int vboxIPCSessionStop(PVBOXIPCSESSION pSession);
|
---|
| 106 |
|
---|
| 107 |
|
---|
| 108 |
|
---|
[96451] | 109 | /**
|
---|
| 110 | * Handles VBOXTRAYIPCMSGTYPE_RESTART.
|
---|
| 111 | */
|
---|
[47960] | 112 | static int vboxIPCHandleVBoxTrayRestart(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
|
---|
[47211] | 113 | {
|
---|
[96451] | 114 | RT_NOREF(pSession, pHdr);
|
---|
[47211] | 115 |
|
---|
| 116 | /** @todo Not implemented yet; don't return an error here. */
|
---|
| 117 | return VINF_SUCCESS;
|
---|
| 118 | }
|
---|
| 119 |
|
---|
[96451] | 120 | /**
|
---|
| 121 | * Handles VBOXTRAYIPCMSGTYPE_SHOW_BALLOON_MSG.
|
---|
| 122 | */
|
---|
[47960] | 123 | static int vboxIPCHandleShowBalloonMsg(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
|
---|
[47211] | 124 | {
|
---|
[96451] | 125 | /*
|
---|
| 126 | * Unmarshal and validate the data.
|
---|
| 127 | */
|
---|
| 128 | union
|
---|
[47211] | 129 | {
|
---|
[96451] | 130 | uint8_t abBuf[_4K];
|
---|
| 131 | VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T s;
|
---|
| 132 | } Payload;
|
---|
| 133 | AssertReturn(pHdr->cbPayload >= RT_UOFFSETOF_DYN(VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T, szzStrings[2]), VERR_INVALID_PARAMETER);
|
---|
| 134 | AssertReturn(pHdr->cbPayload < sizeof(Payload), VERR_BUFFER_OVERFLOW);
|
---|
[47211] | 135 |
|
---|
[96451] | 136 | int rc = RTLocalIpcSessionRead(pSession->hSession, &Payload, pHdr->cbPayload, NULL /*pcbRead - exact, blocking*/);
|
---|
| 137 | if (RT_FAILURE(rc))
|
---|
| 138 | return rc;
|
---|
| 139 |
|
---|
| 140 | /* String lengths: */
|
---|
| 141 | AssertReturn( Payload.s.cchMsg + 1 + Payload.s.cchTitle + 1 + RT_UOFFSETOF(VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T, szzStrings)
|
---|
| 142 | <= pHdr->cbPayload, VERR_INVALID_PARAMETER);
|
---|
| 143 |
|
---|
| 144 | /* Message text: */
|
---|
| 145 | const char *pszMsg = Payload.s.szzStrings;
|
---|
| 146 | rc = RTStrValidateEncodingEx(pszMsg, Payload.s.cchMsg + 1,
|
---|
| 147 | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
|
---|
| 148 | AssertRCReturn(rc, rc);
|
---|
| 149 |
|
---|
| 150 | /* Title text: */
|
---|
| 151 | const char *pszTitle = &Payload.s.szzStrings[Payload.s.cchMsg + 1];
|
---|
| 152 | rc = RTStrValidateEncodingEx(pszMsg, Payload.s.cchTitle + 1,
|
---|
| 153 | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
|
---|
| 154 | AssertRCReturn(rc, rc);
|
---|
| 155 |
|
---|
| 156 | /* Type/dwInfoFlags: */
|
---|
| 157 | AssertReturn( Payload.s.uType == NIIF_NONE
|
---|
| 158 | || Payload.s.uType == NIIF_INFO
|
---|
| 159 | || Payload.s.uType == NIIF_WARNING
|
---|
| 160 | || Payload.s.uType == NIIF_ERROR,
|
---|
| 161 | VERR_WRONG_TYPE);
|
---|
| 162 |
|
---|
| 163 | /* Timeout: */
|
---|
| 164 | if (!Payload.s.cMsTimeout)
|
---|
| 165 | Payload.s.cMsTimeout = RT_MS_5SEC;
|
---|
| 166 | AssertStmt(Payload.s.cMsTimeout >= RT_MS_1SEC, Payload.s.cMsTimeout = RT_MS_1SEC);
|
---|
| 167 | AssertStmt(Payload.s.cMsTimeout <= RT_MS_1MIN, Payload.s.cMsTimeout = RT_MS_1MIN);
|
---|
| 168 |
|
---|
| 169 | /*
|
---|
| 170 | * Showing the balloon tooltip is not critical.
|
---|
| 171 | */
|
---|
| 172 | int rc2 = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
|
---|
| 173 | pszMsg, pszTitle, Payload.s.cMsTimeout, Payload.s.uType);
|
---|
| 174 | LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n",
|
---|
| 175 | pszTitle, pszMsg, Payload.s.cMsTimeout, Payload.s.uType, rc2));
|
---|
| 176 | RT_NOREF_PV(rc2);
|
---|
| 177 |
|
---|
| 178 | return VINF_SUCCESS;
|
---|
[47211] | 179 | }
|
---|
| 180 |
|
---|
[96451] | 181 | /**
|
---|
| 182 | * Handles VBOXTRAYIPCMSGTYPE_USER_LAST_INPUT.
|
---|
| 183 | */
|
---|
[47960] | 184 | static int vboxIPCHandleUserLastInput(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr)
|
---|
[47211] | 185 | {
|
---|
[96451] | 186 | RT_NOREF(pHdr);
|
---|
[47211] | 187 |
|
---|
| 188 | int rc = VINF_SUCCESS;
|
---|
[96451] | 189 | VBOXTRAYIPCREPLY_USER_LAST_INPUT_T Reply = { UINT32_MAX };
|
---|
[63100] | 190 | if (g_pfnGetLastInputInfo)
|
---|
[47211] | 191 | {
|
---|
[96451] | 192 | /* Note: This only works up to 49.7 days (= 2^32, 32-bit counter) since Windows was started. */
|
---|
| 193 | LASTINPUTINFO LastInput;
|
---|
| 194 | LastInput.cbSize = sizeof(LastInput);
|
---|
| 195 | if (g_pfnGetLastInputInfo(&LastInput))
|
---|
| 196 | Reply.cSecSinceLastInput = (GetTickCount() - LastInput.dwTime) / 1000;
|
---|
[47960] | 197 | else
|
---|
| 198 | rc = RTErrConvertFromWin32(GetLastError());
|
---|
[47211] | 199 | }
|
---|
| 200 |
|
---|
[96451] | 201 | int rc2 = RTLocalIpcSessionWrite(pSession->hSession, &Reply, sizeof(Reply));
|
---|
[47960] | 202 | if (RT_SUCCESS(rc))
|
---|
| 203 | rc = rc2;
|
---|
| 204 |
|
---|
[47211] | 205 | return rc;
|
---|
| 206 | }
|
---|
| 207 |
|
---|
[34357] | 208 | /**
|
---|
[47195] | 209 | * Initializes the IPC communication.
|
---|
[34357] | 210 | *
|
---|
| 211 | * @return IPRT status code.
|
---|
[47195] | 212 | * @param pEnv The IPC service's environment.
|
---|
[57741] | 213 | * @param ppInstance The instance pointer which refers to this object.
|
---|
[34357] | 214 | */
|
---|
[57741] | 215 | DECLCALLBACK(int) VBoxIPCInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
|
---|
[34085] | 216 | {
|
---|
[47195] | 217 | AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
|
---|
[57741] | 218 | AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
|
---|
[47195] | 219 |
|
---|
| 220 | LogFlowFuncEnter();
|
---|
| 221 |
|
---|
[57741] | 222 | PVBOXIPCCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
|
---|
| 223 | AssertPtr(pCtx);
|
---|
[47195] | 224 |
|
---|
[57741] | 225 | int rc = RTCritSectInit(&pCtx->CritSect);
|
---|
[47195] | 226 | if (RT_SUCCESS(rc))
|
---|
[34085] | 227 | {
|
---|
[64291] | 228 | char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
|
---|
[64292] | 229 | memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
|
---|
[58306] | 230 | rc = RTProcQueryUsername(NIL_RTPROCESS,
|
---|
[64291] | 231 | &szPipeName[sizeof(VBOXTRAY_IPC_PIPE_PREFIX) - 1],
|
---|
| 232 | sizeof(szPipeName) - sizeof(VBOXTRAY_IPC_PIPE_PREFIX) + 1,
|
---|
[58306] | 233 | NULL /*pcbUser*/);
|
---|
[64326] | 234 | AssertRC(rc);
|
---|
[47232] | 235 | if (RT_SUCCESS(rc))
|
---|
[34097] | 236 | {
|
---|
[64292] | 237 | rc = RTLocalIpcServerCreate(&pCtx->hServer, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
|
---|
[64326] | 238 | AssertRC(rc);
|
---|
[47232] | 239 | if (RT_SUCCESS(rc))
|
---|
| 240 | {
|
---|
[64291] | 241 | pCtx->pEnv = pEnv;
|
---|
| 242 | RTListInit(&pCtx->SessionList);
|
---|
[47232] | 243 |
|
---|
[64291] | 244 | *ppInstance = pCtx;
|
---|
[47232] | 245 |
|
---|
[64291] | 246 | /* GetLastInputInfo only is available starting at Windows 2000 -- might fail. */
|
---|
[96600] | 247 | g_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO)RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo");
|
---|
[47232] | 248 |
|
---|
[64291] | 249 | LogRelFunc(("Local IPC server now running at \"%s\"\n", szPipeName));
|
---|
| 250 | return VINF_SUCCESS;
|
---|
| 251 | }
|
---|
[64285] | 252 |
|
---|
[34097] | 253 | }
|
---|
[47195] | 254 |
|
---|
[57741] | 255 | RTCritSectDelete(&pCtx->CritSect);
|
---|
[34085] | 256 | }
|
---|
[47195] | 257 |
|
---|
[47232] | 258 | LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc));
|
---|
[34085] | 259 | return rc;
|
---|
| 260 | }
|
---|
| 261 |
|
---|
[57741] | 262 | DECLCALLBACK(void) VBoxIPCStop(void *pInstance)
|
---|
[34357] | 263 | {
|
---|
[64326] | 264 | /* Can be NULL if VBoxIPCInit failed. */
|
---|
| 265 | if (!pInstance)
|
---|
| 266 | return;
|
---|
[47211] | 267 | AssertPtrReturnVoid(pInstance);
|
---|
[34357] | 268 |
|
---|
[51469] | 269 | LogFlowFunc(("Stopping pInstance=%p\n", pInstance));
|
---|
[47195] | 270 |
|
---|
[50039] | 271 | /* Shut down local IPC server. */
|
---|
[47195] | 272 | PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
|
---|
| 273 | AssertPtr(pCtx);
|
---|
| 274 |
|
---|
| 275 | if (pCtx->hServer != NIL_RTLOCALIPCSERVER)
|
---|
[34085] | 276 | {
|
---|
[47195] | 277 | int rc2 = RTLocalIpcServerCancel(pCtx->hServer);
|
---|
| 278 | if (RT_FAILURE(rc2))
|
---|
[51469] | 279 | LogFlowFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2));
|
---|
[34085] | 280 | }
|
---|
[50039] | 281 |
|
---|
| 282 | /* Stop all remaining session threads. */
|
---|
| 283 | int rc = RTCritSectEnter(&pCtx->CritSect);
|
---|
| 284 | if (RT_SUCCESS(rc))
|
---|
| 285 | {
|
---|
| 286 | PVBOXIPCSESSION pSession;
|
---|
| 287 | RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node)
|
---|
| 288 | {
|
---|
| 289 | int rc2 = vboxIPCSessionStop(pSession);
|
---|
| 290 | if (RT_FAILURE(rc2))
|
---|
| 291 | {
|
---|
[51469] | 292 | LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
|
---|
[50039] | 293 | pSession, rc2));
|
---|
| 294 | /* Keep going. */
|
---|
| 295 | }
|
---|
| 296 | }
|
---|
| 297 | }
|
---|
[34085] | 298 | }
|
---|
| 299 |
|
---|
[57741] | 300 | DECLCALLBACK(void) VBoxIPCDestroy(void *pInstance)
|
---|
[34085] | 301 | {
|
---|
[50039] | 302 | AssertPtrReturnVoid(pInstance);
|
---|
[34085] | 303 |
|
---|
[51469] | 304 | LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
|
---|
[47195] | 305 |
|
---|
| 306 | PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
|
---|
| 307 | AssertPtr(pCtx);
|
---|
| 308 |
|
---|
[50039] | 309 | /* Shut down local IPC server. */
|
---|
[47195] | 310 | int rc = RTCritSectEnter(&pCtx->CritSect);
|
---|
[34085] | 311 | if (RT_SUCCESS(rc))
|
---|
| 312 | {
|
---|
[50039] | 313 | rc = RTLocalIpcServerDestroy(pCtx->hServer);
|
---|
| 314 | if (RT_FAILURE(rc))
|
---|
[51469] | 315 | LogFlowFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc));
|
---|
[50039] | 316 |
|
---|
| 317 | int rc2 = RTCritSectLeave(&pCtx->CritSect);
|
---|
| 318 | if (RT_SUCCESS(rc))
|
---|
| 319 | rc = rc2;
|
---|
| 320 | }
|
---|
| 321 |
|
---|
[51469] | 322 | LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
|
---|
[50039] | 323 |
|
---|
| 324 | /* Wait for all IPC session threads to shut down. */
|
---|
| 325 | bool fListIsEmpty = true;
|
---|
| 326 | do
|
---|
| 327 | {
|
---|
| 328 | int rc2 = RTCritSectEnter(&pCtx->CritSect);
|
---|
| 329 | if (RT_SUCCESS(rc2))
|
---|
[47195] | 330 | {
|
---|
[50039] | 331 | fListIsEmpty = RTListIsEmpty(&pCtx->SessionList);
|
---|
| 332 | rc2 = RTCritSectLeave(&pCtx->CritSect);
|
---|
[50040] | 333 |
|
---|
| 334 | if (!fListIsEmpty) /* Don't hog CPU while waiting. */
|
---|
| 335 | RTThreadSleep(100);
|
---|
[47195] | 336 | }
|
---|
| 337 |
|
---|
[50039] | 338 | if (RT_FAILURE(rc2))
|
---|
| 339 | break;
|
---|
[47195] | 340 |
|
---|
[50039] | 341 | } while (!fListIsEmpty);
|
---|
[47195] | 342 |
|
---|
[50039] | 343 | AssertMsg(fListIsEmpty,
|
---|
| 344 | ("Session thread list is not empty when it should\n"));
|
---|
[34085] | 345 |
|
---|
[51469] | 346 | LogFlowFunc(("All remaining IPC sessions shut down\n"));
|
---|
[50039] | 347 |
|
---|
| 348 | int rc2 = RTCritSectDelete(&pCtx->CritSect);
|
---|
| 349 | if (RT_SUCCESS(rc))
|
---|
| 350 | rc = rc2;
|
---|
| 351 |
|
---|
[51469] | 352 | LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
|
---|
[47195] | 353 | pInstance, rc));
|
---|
[34357] | 354 | }
|
---|
| 355 |
|
---|
| 356 | /**
|
---|
[47195] | 357 | * Services a client session.
|
---|
[34357] | 358 | *
|
---|
[47195] | 359 | * @returns VINF_SUCCESS.
|
---|
[63100] | 360 | * @param hThreadSelf The thread handle.
|
---|
[47195] | 361 | * @param pvSession Pointer to the session instance data.
|
---|
[34357] | 362 | */
|
---|
[63100] | 363 | static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThreadSelf, void *pvSession)
|
---|
[34085] | 364 | {
|
---|
[63100] | 365 | RT_NOREF(hThreadSelf);
|
---|
[47195] | 366 | PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession;
|
---|
| 367 | AssertPtrReturn(pThis, VERR_INVALID_POINTER);
|
---|
| 368 | RTLOCALIPCSESSION hSession = pThis->hSession;
|
---|
| 369 | AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
|
---|
[34085] | 370 |
|
---|
[51469] | 371 | LogFlowFunc(("pThis=%p\n", pThis));
|
---|
[34085] | 372 |
|
---|
[47211] | 373 | int rc = VINF_SUCCESS;
|
---|
| 374 |
|
---|
[47195] | 375 | /*
|
---|
| 376 | * Process client requests until it quits or we're cancelled on termination.
|
---|
| 377 | */
|
---|
[47211] | 378 | while ( !ASMAtomicUoReadBool(&pThis->fTerminate)
|
---|
| 379 | && RT_SUCCESS(rc))
|
---|
[34085] | 380 | {
|
---|
[47211] | 381 | /* The next call will be cancelled via VBoxIPCStop if needed. */
|
---|
| 382 | rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT);
|
---|
[96451] | 383 | if (RT_SUCCESS(rc))
|
---|
[34097] | 384 | {
|
---|
[96451] | 385 | /*
|
---|
| 386 | * Read the message header.
|
---|
| 387 | */
|
---|
| 388 | VBOXTRAYIPCHEADER Hdr = {0};
|
---|
| 389 | rc = RTLocalIpcSessionRead(hSession, &Hdr, sizeof(Hdr), NULL /*pcbRead - exact, blocking*/);
|
---|
| 390 | if (RT_FAILURE(rc))
|
---|
| 391 | break;
|
---|
| 392 |
|
---|
| 393 | /*
|
---|
| 394 | * Validate the message header.
|
---|
| 395 | *
|
---|
| 396 | * Disconnecting the client if invalid or something we don't grok.
|
---|
| 397 | * Currently all clients are one-shots, so there is no need to get
|
---|
| 398 | * in complicated recovery code if we don't understand one another.
|
---|
| 399 | */
|
---|
| 400 | if ( Hdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC
|
---|
| 401 | || Hdr.uVersion != VBOXTRAY_IPC_HDR_VERSION)
|
---|
[34097] | 402 | {
|
---|
[96451] | 403 | LogRelFunc(("Session %p: Invalid header magic/version: %#x, %#x, %#x, %#x\n",
|
---|
[96663] | 404 | pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload));
|
---|
[96451] | 405 | rc = VERR_INVALID_MAGIC;
|
---|
[47195] | 406 | break;
|
---|
[34097] | 407 | }
|
---|
[96451] | 408 | if (Hdr.cbPayload > VBOXTRAY_IPC_MAX_PAYLOAD)
|
---|
| 409 | {
|
---|
| 410 | LogRelFunc(("Session %p: Payload to big: %#x, %#x, %#x, %#x - max %#x\n",
|
---|
[96663] | 411 | pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload, VBOXTRAY_IPC_MAX_PAYLOAD));
|
---|
[96451] | 412 | rc = VERR_TOO_MUCH_DATA;
|
---|
| 413 | break;
|
---|
| 414 | }
|
---|
[96663] | 415 | if ( Hdr.enmMsgType <= VBOXTRAYIPCMSGTYPE_INVALID
|
---|
| 416 | || Hdr.enmMsgType >= VBOXTRAYIPCMSGTYPE_END)
|
---|
[96451] | 417 | {
|
---|
| 418 | LogRelFunc(("Session %p: Unknown message: %#x, %#x, %#x, %#x\n",
|
---|
[96663] | 419 | pThis, Hdr.uMagic, Hdr.uVersion, Hdr.enmMsgType, Hdr.cbPayload));
|
---|
[96451] | 420 | rc = VERR_INVALID_FUNCTION;
|
---|
| 421 | break;
|
---|
| 422 | }
|
---|
[47234] | 423 |
|
---|
[96451] | 424 | /*
|
---|
| 425 | * Handle the message.
|
---|
| 426 | */
|
---|
| 427 | switch (Hdr.enmMsgType)
|
---|
[47195] | 428 | {
|
---|
[96451] | 429 | case VBOXTRAYIPCMSGTYPE_RESTART:
|
---|
| 430 | rc = vboxIPCHandleVBoxTrayRestart(pThis, &Hdr);
|
---|
| 431 | break;
|
---|
[47211] | 432 |
|
---|
[96451] | 433 | case VBOXTRAYIPCMSGTYPE_SHOW_BALLOON_MSG:
|
---|
| 434 | rc = vboxIPCHandleShowBalloonMsg(pThis, &Hdr);
|
---|
| 435 | break;
|
---|
[47211] | 436 |
|
---|
[96451] | 437 | case VBOXTRAYIPCMSGTYPE_USER_LAST_INPUT:
|
---|
| 438 | rc = vboxIPCHandleUserLastInput(pThis, &Hdr);
|
---|
| 439 | break;
|
---|
[47211] | 440 |
|
---|
[96451] | 441 | default:
|
---|
| 442 | AssertFailedBreakStmt(rc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
|
---|
[47234] | 443 | }
|
---|
[96451] | 444 | if (RT_FAILURE(rc))
|
---|
| 445 | LogFlowFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n", pThis, Hdr.enmMsgType, rc));
|
---|
[34097] | 446 | }
|
---|
[96451] | 447 | else if (rc == VERR_CANCELLED)
|
---|
| 448 | {
|
---|
| 449 | LogFlowFunc(("Session %p: Waiting for data cancelled\n", pThis));
|
---|
| 450 | rc = VINF_SUCCESS;
|
---|
| 451 | break;
|
---|
| 452 | }
|
---|
| 453 | else
|
---|
| 454 | LogFlowFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n", pThis, rc));
|
---|
[47195] | 455 | }
|
---|
| 456 |
|
---|
[96451] | 457 | LogFlowFunc(("Session %p: Handler ended with rc=%Rrc\n", pThis, rc));
|
---|
[47211] | 458 |
|
---|
[47195] | 459 | /*
|
---|
[50039] | 460 | * Close the session.
|
---|
| 461 | */
|
---|
| 462 | int rc2 = RTLocalIpcSessionClose(hSession);
|
---|
| 463 | if (RT_FAILURE(rc2))
|
---|
[51469] | 464 | LogFlowFunc(("Session %p: Failed closing session %p, rc=%Rrc\n", pThis, rc2));
|
---|
[50039] | 465 |
|
---|
| 466 | /*
|
---|
[47195] | 467 | * Clean up the session.
|
---|
| 468 | */
|
---|
| 469 | PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT);
|
---|
[47211] | 470 | AssertMsg(pCtx, ("Session %p: No context found\n", pThis));
|
---|
[50039] | 471 | rc2 = RTCritSectEnter(&pCtx->CritSect);
|
---|
| 472 | if (RT_SUCCESS(rc2))
|
---|
[47211] | 473 | {
|
---|
[50039] | 474 | /* Remove this session from the session list. */
|
---|
| 475 | RTListNodeRemove(&pThis->Node);
|
---|
[47195] | 476 |
|
---|
[50039] | 477 | rc2 = RTCritSectLeave(&pCtx->CritSect);
|
---|
[47211] | 478 | if (RT_SUCCESS(rc))
|
---|
| 479 | rc = rc2;
|
---|
[47195] | 480 | }
|
---|
| 481 |
|
---|
[96451] | 482 | LogFlowFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n", pThis, rc));
|
---|
[50039] | 483 |
|
---|
| 484 | RTMemFree(pThis);
|
---|
| 485 | pThis = NULL;
|
---|
| 486 |
|
---|
[47211] | 487 | return rc;
|
---|
[47195] | 488 | }
|
---|
| 489 |
|
---|
| 490 | static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession)
|
---|
| 491 | {
|
---|
| 492 | AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
|
---|
| 493 | AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER);
|
---|
| 494 |
|
---|
| 495 | int rc = RTCritSectEnter(&pCtx->CritSect);
|
---|
| 496 | if (RT_SUCCESS(rc))
|
---|
| 497 | {
|
---|
| 498 | PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION));
|
---|
| 499 | if (pSession)
|
---|
[34097] | 500 | {
|
---|
[57741] | 501 | pSession->pCtx = pCtx;
|
---|
| 502 | pSession->hSession = hSession;
|
---|
[47195] | 503 | pSession->fTerminate = false;
|
---|
[57741] | 504 | pSession->hThread = NIL_RTTHREAD;
|
---|
[47195] | 505 |
|
---|
| 506 | /* Start IPC session thread. */
|
---|
| 507 | LogFlowFunc(("Creating thread for session %p ...\n", pSession));
|
---|
[50039] | 508 | rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread,
|
---|
| 509 | pSession /* pvUser */, 0 /* Default stack size */,
|
---|
[57741] | 510 | RTTHREADTYPE_DEFAULT, 0 /* Flags */, "IPCSESSION");
|
---|
[47195] | 511 | if (RT_SUCCESS(rc))
|
---|
| 512 | {
|
---|
| 513 | /* Add session thread to session IPC list. */
|
---|
| 514 | RTListAppend(&pCtx->SessionList, &pSession->Node);
|
---|
| 515 | }
|
---|
[34097] | 516 | else
|
---|
| 517 | {
|
---|
[47195] | 518 | int rc2 = RTLocalIpcSessionClose(hSession);
|
---|
| 519 | if (RT_FAILURE(rc2))
|
---|
[51469] | 520 | LogFlowFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2));
|
---|
[47195] | 521 |
|
---|
[51469] | 522 | LogFlowFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc));
|
---|
[47195] | 523 | RTMemFree(pSession);
|
---|
[34097] | 524 | }
|
---|
| 525 | }
|
---|
[47195] | 526 | else
|
---|
| 527 | rc = VERR_NO_MEMORY;
|
---|
| 528 |
|
---|
| 529 | int rc2 = RTCritSectLeave(&pCtx->CritSect);
|
---|
| 530 | AssertRC(rc2);
|
---|
[34085] | 531 | }
|
---|
[47195] | 532 |
|
---|
[34085] | 533 | return rc;
|
---|
| 534 | }
|
---|
| 535 |
|
---|
[50039] | 536 | static int vboxIPCSessionStop(PVBOXIPCSESSION pSession)
|
---|
[34085] | 537 | {
|
---|
[47195] | 538 | AssertPtrReturn(pSession, VERR_INVALID_POINTER);
|
---|
[47960] | 539 |
|
---|
[50039] | 540 | ASMAtomicWriteBool(&pSession->fTerminate, true);
|
---|
[34085] | 541 |
|
---|
[47195] | 542 | RTLOCALIPCSESSION hSession;
|
---|
| 543 | ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession);
|
---|
[50039] | 544 | if (hSession)
|
---|
| 545 | return RTLocalIpcSessionClose(hSession);
|
---|
[47195] | 546 |
|
---|
[50039] | 547 | return VINF_SUCCESS;
|
---|
[34085] | 548 | }
|
---|
| 549 |
|
---|
| 550 | /**
|
---|
| 551 | * Thread function to wait for and process seamless mode change
|
---|
| 552 | * requests
|
---|
| 553 | */
|
---|
[57741] | 554 | DECLCALLBACK(int) VBoxIPCWorker(void *pInstance, bool volatile *pfShutdown)
|
---|
[34085] | 555 | {
|
---|
[57741] | 556 | AssertPtr(pInstance);
|
---|
| 557 | LogFlowFunc(("pInstance=%p\n", pInstance));
|
---|
| 558 |
|
---|
[47195] | 559 | LogFlowFuncEnter();
|
---|
[34085] | 560 |
|
---|
[57741] | 561 | /*
|
---|
| 562 | * Tell the control thread that it can continue
|
---|
| 563 | * spawning services.
|
---|
| 564 | */
|
---|
| 565 | RTThreadUserSignal(RTThreadSelf());
|
---|
| 566 |
|
---|
[34085] | 567 | PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance;
|
---|
| 568 | AssertPtr(pCtx);
|
---|
| 569 |
|
---|
[57741] | 570 | int rc;
|
---|
| 571 |
|
---|
[47195] | 572 | bool fShutdown = false;
|
---|
| 573 | for (;;)
|
---|
[34085] | 574 | {
|
---|
[47195] | 575 | RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
|
---|
[57741] | 576 | rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession);
|
---|
[47195] | 577 | if (RT_FAILURE(rc))
|
---|
[34085] | 578 | {
|
---|
[47195] | 579 | if (rc == VERR_CANCELLED)
|
---|
[34085] | 580 | {
|
---|
[47195] | 581 | LogFlow(("Cancelled\n"));
|
---|
| 582 | fShutdown = true;
|
---|
[34085] | 583 | }
|
---|
[47195] | 584 | else
|
---|
| 585 | LogRelFunc(("Listening failed with rc=%Rrc\n", rc));
|
---|
| 586 | }
|
---|
[34085] | 587 |
|
---|
[47195] | 588 | if (fShutdown)
|
---|
| 589 | break;
|
---|
| 590 | rc = vboxIPCSessionCreate(pCtx, hClientSession);
|
---|
| 591 | if (RT_FAILURE(rc))
|
---|
| 592 | {
|
---|
| 593 | LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc));
|
---|
| 594 | /* Keep going. */
|
---|
[34085] | 595 | }
|
---|
| 596 |
|
---|
[57741] | 597 | if (*pfShutdown)
|
---|
[47195] | 598 | break;
|
---|
| 599 | }
|
---|
[34085] | 600 |
|
---|
[57741] | 601 | LogFlowFuncLeaveRC(rc);
|
---|
| 602 | return rc;
|
---|
[34085] | 603 | }
|
---|
| 604 |
|
---|
[57741] | 605 | /**
|
---|
| 606 | * The service description.
|
---|
| 607 | */
|
---|
| 608 | VBOXSERVICEDESC g_SvcDescIPC =
|
---|
| 609 | {
|
---|
| 610 | /* pszName. */
|
---|
| 611 | "IPC",
|
---|
| 612 | /* pszDescription. */
|
---|
| 613 | "Inter-Process Communication",
|
---|
| 614 | /* methods */
|
---|
| 615 | VBoxIPCInit,
|
---|
| 616 | VBoxIPCWorker,
|
---|
| 617 | NULL /* pfnStop */,
|
---|
| 618 | VBoxIPCDestroy
|
---|
| 619 | };
|
---|
| 620 |
|
---|