VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp@ 93422

最後變更 在這個檔案從93422是 93422,由 vboxsync 提交於 3 年 前

doxygen: add initial documantation for VBoxDRMClient service, bugref:10134.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 41.7 KB
 
1/* $Id: display-drm.cpp 93422 2022-01-24 20:03:33Z vboxsync $ */
2/** @file
3 * A user space daemon which communicates with VirtualBox host interface
4 * and performs VMSVGA-specific guest screen resize and communicates with
5 * Desktop Environment helper daemon over IPC.
6 */
7
8/*
9 * Copyright (C) 2016-2022 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.alldomusa.eu.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
20/** @page pg_vboxdrmcliet VBoxDRMClient - The VMSVGA Guest Screen Resize Service
21 *
22 * The VMSVGA Guest Screen Resize Service is a service which communicates with a
23 * guest VMSVGA driver and triggers it to perform screen resize on a guest side.
24 *
25 * This service supposed to be started on early boot. On start it will try to find
26 * compatible VMSVGA graphics card and terminate immediately if not found.
27 * VMSVGA functionality implemented here is only supported starting from vmgfx
28 * driver version 2.10 which was introduced in Linux kernel 4.6. When compatible
29 * graphics card is found, service will start a worker loop in order to receive screen
30 * update data from host and apply it to local DRM stack.
31 *
32 * In addition, it will start a local IPC server in order to communicate with Desktop
33 * Environment specific service(s). Currently, it will propagate to IPC client information regarding to
34 * which display should be set as primary on Desktop Environment level. As well as
35 * receive screen layout change events obtained on Desktop Environment level and send it
36 * back to host, so host and guest will have the same screen layout representation.
37 *
38 * Logging is implemented in a way that errors are always printed out, VBClLogVerbose(1) and
39 * VBClLogVerbose(2) are used for debugging purposes. Verbosity level 1 is for messages related
40 * to service itself (excluding IPC), level 2 is for IPC communication debugging. In order to see
41 * logging on a host side it is enough to do:
42 *
43 * echo 1 > /sys/module/vboxguest/parameters/r3_log_to_host.
44 *
45 *
46 * Service is running the following threads:
47 *
48 * DrmResizeThread - this thread listens for display layout update events from host.
49 * Once event is received, it either injects new screen layout data into DRM stack,
50 * and/or asks IPC client(s) to set primary display. This thread is accessing IPC
51 * client connection list when it needs to sent new primary display data to all the
52 * connected clients.
53 *
54 * DrmIpcSRV - this thread is a main loop for IPC server. It accepts new connection(s),
55 * authenticates it and starts new client thread IpcCLT-XXX for processing client
56 * requests. This thread is accessing IPC client connection list by adding a new
57 * connection data into it.
58 *
59 * IpcCLT-%u - this thread processes all the client data. Suffix '-%u' in thread name is PID
60 * of a remote client process. Typical name for client thread would be IpcCLT-1234. This
61 * thread is accessing IPC client connection list when it removes connection data from it
62 * when actual IPC connection is closed. Due to IPRT thread name limitation, actual thread
63 * name will be cropped by 15 characters.
64 *
65 *
66 * The following loack are utilized:
67 *
68 * #g_ipcClientConnectionsListCritSect - protects access to list of IPC client connections.
69 * It is used by each thread - DrmResizeThread, DrmIpcSRV and IpcCLT-XXX.
70 *
71 * #g_monitorPositionsCritSect - serializes access to host interface when guest Desktop
72 * Environment reports display layout changes.
73 */
74
75#include "VBoxClient.h"
76#include "display-ipc.h"
77
78#include <VBox/VBoxGuestLib.h>
79
80#include <iprt/getopt.h>
81#include <iprt/assert.h>
82#include <iprt/file.h>
83#include <iprt/err.h>
84#include <iprt/string.h>
85#include <iprt/initterm.h>
86#include <iprt/message.h>
87#include <iprt/thread.h>
88#include <iprt/asm.h>
89#include <iprt/localipc.h>
90
91#include <unistd.h>
92#include <stdio.h>
93#include <limits.h>
94#include <signal.h>
95#include <grp.h>
96#include <errno.h>
97
98#ifdef RT_OS_LINUX
99# include <sys/ioctl.h>
100#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */
101# include <sys/ioccom.h>
102#endif
103
104/** Ioctl command to query vmwgfx version information. */
105#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION)
106/** Ioctl command to set new screen layout. */
107#define DRM_IOCTL_VMW_UPDATE_LAYOUT _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT)
108/** A driver name which identifies VMWare driver. */
109#define DRM_DRIVER_NAME "vmwgfx"
110/** VMWare driver compatible version number. On previous versions resizing does not seem work. */
111#define DRM_DRIVER_VERSION_MAJOR_MIN (2)
112#define DRM_DRIVER_VERSION_MINOR_MIN (10)
113
114/** VMWare char device driver minor numbers range. */
115#define VMW_CONTROL_DEVICE_MINOR_START (64)
116#define VMW_RENDER_DEVICE_MINOR_START (128)
117#define VMW_RENDER_DEVICE_MINOR_END (192)
118
119/** Name of DRM resize thread. */
120#define DRM_RESIZE_THREAD_NAME "DrmResizeThread"
121
122/** Name of DRM IPC server thread. */
123#define DRM_IPC_SERVER_THREAD_NAME "DrmIpcSRV"
124/** Maximum length of thread name. */
125#define DRM_IPC_THREAD_NAME_MAX (16)
126/** Name pattern of DRM IPC client thread. */
127#define DRM_IPC_CLIENT_THREAD_NAME_PTR "IpcCLT-%u"
128/** Maximum number of simultaneous IPC client connections. */
129#define DRM_IPC_SERVER_CONNECTIONS_MAX (16)
130
131/** IPC client connections counter. */
132static volatile uint32_t g_cDrmIpcConnections = 0;
133
134/** DRM version structure. */
135struct DRMVERSION
136{
137 int cMajor;
138 int cMinor;
139 int cPatchLevel;
140 size_t cbName;
141 char *pszName;
142 size_t cbDate;
143 char *pszDate;
144 size_t cbDescription;
145 char *pszDescription;
146};
147AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
148
149/** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl. The
150 * rects argument is a cast pointer to an array of drm_vmw_rect. */
151struct DRMVMWUPDATELAYOUT
152{
153 uint32_t cOutputs;
154 uint32_t u32Pad;
155 uint64_t ptrRects;
156};
157AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
158
159/** A node of IPC client connections list. */
160typedef struct VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE
161{
162 /** The list node. */
163 RTLISTNODE Node;
164 /** List node payload. */
165 PVBOX_DRMIPC_CLIENT pClient;
166} VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE;
167
168/* Pointer to VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE. */
169typedef VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE *PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE;
170
171/** IPC client connections list. */
172static VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE g_ipcClientConnectionsList;
173
174/** IPC client connections list critical section. */
175static RTCRITSECT g_ipcClientConnectionsListCritSect;
176
177/** Critical section used for reporting monitors position back to host. */
178static RTCRITSECT g_monitorPositionsCritSect;
179
180/** Counter of how often our daemon has been re-spawned. */
181unsigned g_cRespawn = 0;
182/** Logging verbosity level. */
183unsigned g_cVerbosity = 0;
184
185/** Path to the PID file. */
186static const char *g_pszPidFile = "/var/run/VBoxDRMClient";
187
188/** Global flag which is triggered when service requested to shutdown. */
189static bool volatile g_fShutdown;
190
191/**
192 * Go over all existing IPC client connection and put set-primary-screen request
193 * data into TX queue of each of them .
194 *
195 * @return IPRT status code.
196 * @param u32PrimaryDisplay Primary display ID.
197 */
198static int vbDrmIpcBroadcastPrimaryDisplay(uint32_t u32PrimaryDisplay);
199
200/**
201 * Attempts to open DRM device by given path and check if it is
202 * capable for screen resize.
203 *
204 * @return DRM device handle on success, NIL_RTFILE otherwise.
205 * @param szPathPattern Path name pattern to the DRM device.
206 * @param uInstance Driver / device instance.
207 */
208static RTFILE vbDrmTryDevice(const char *szPathPattern, uint8_t uInstance)
209{
210 int rc = VERR_NOT_FOUND;
211 char szPath[PATH_MAX];
212 struct DRMVERSION vmwgfxVersion;
213 RTFILE hDevice = NIL_RTFILE;
214
215 RT_ZERO(szPath);
216 RT_ZERO(vmwgfxVersion);
217
218 rc = RTStrPrintf(szPath, sizeof(szPath), szPathPattern, uInstance);
219 if (RT_SUCCESS(rc))
220 {
221 rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
222 if (RT_SUCCESS(rc))
223 {
224 char szVmwgfxDriverName[sizeof(DRM_DRIVER_NAME)];
225 RT_ZERO(szVmwgfxDriverName);
226
227 vmwgfxVersion.cbName = sizeof(szVmwgfxDriverName);
228 vmwgfxVersion.pszName = szVmwgfxDriverName;
229
230 /* Query driver version information and check if it can be used for screen resizing. */
231 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &vmwgfxVersion, sizeof(vmwgfxVersion), NULL);
232 if ( RT_SUCCESS(rc)
233 && strncmp(szVmwgfxDriverName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1) == 0
234 && ( vmwgfxVersion.cMajor >= DRM_DRIVER_VERSION_MAJOR_MIN
235 || ( vmwgfxVersion.cMajor == DRM_DRIVER_VERSION_MAJOR_MIN
236 && vmwgfxVersion.cMinor >= DRM_DRIVER_VERSION_MINOR_MIN)))
237 {
238 VBClLogInfo("found compatible device: %s\n", szPath);
239 }
240 else
241 {
242 RTFileClose(hDevice);
243 hDevice = NIL_RTFILE;
244 rc = VERR_NOT_FOUND;
245 }
246 }
247 }
248 else
249 {
250 VBClLogError("unable to construct path to DRM device: %Rrc\n", rc);
251 }
252
253 return RT_SUCCESS(rc) ? hDevice : NIL_RTFILE;
254}
255
256/**
257 * Attempts to find and open DRM device to be used for screen resize.
258 *
259 * @return DRM device handle on success, NIL_RTFILE otherwise.
260 */
261static RTFILE vbDrmOpenVmwgfx(void)
262{
263 /* Control devices for drm graphics driver control devices go from
264 * controlD64 to controlD127. Render node devices go from renderD128
265 * to renderD192. The driver takes resize hints via the control device
266 * on pre-4.10 (???) kernels and on the render device on newer ones.
267 * At first, try to find control device and render one if not found.
268 */
269 uint8_t i;
270 RTFILE hDevice = NIL_RTFILE;
271
272 /* Lookup control device. */
273 for (i = VMW_CONTROL_DEVICE_MINOR_START; i < VMW_RENDER_DEVICE_MINOR_START; i++)
274 {
275 hDevice = vbDrmTryDevice("/dev/dri/controlD%u", i);
276 if (hDevice != NIL_RTFILE)
277 return hDevice;
278 }
279
280 /* Lookup render device. */
281 for (i = VMW_RENDER_DEVICE_MINOR_START; i <= VMW_RENDER_DEVICE_MINOR_END; i++)
282 {
283 hDevice = vbDrmTryDevice("/dev/dri/renderD%u", i);
284 if (hDevice != NIL_RTFILE)
285 return hDevice;
286 }
287
288 VBClLogError("unable to find DRM device\n");
289
290 return hDevice;
291}
292
293/**
294 * This function converts input monitors layout array passed from DevVMM
295 * into monitors layout array to be passed to DRM stack.
296 *
297 * @return VINF_SUCCESS on success, VERR_DUPLICATE if monitors layout was not changed, IPRT error code otherwise.
298 * @param aDisplaysIn Input displays array.
299 * @param cDisplaysIn Number of elements in input displays array.
300 * @param aDisplaysOut Output displays array.
301 * @param cDisplaysOutMax Number of elements in output displays array.
302 * @param pu32PrimaryDisplay ID of a display which marked as primary.
303 * @param pcActualDisplays Number of displays to report to DRM stack (number of enabled displays).
304 */
305static int vbDrmValidateLayout(VMMDevDisplayDef *aDisplaysIn, uint32_t cDisplaysIn,
306 struct VBOX_DRMIPC_VMWRECT *aDisplaysOut, uint32_t *pu32PrimaryDisplay,
307 uint32_t cDisplaysOutMax, uint32_t *pcActualDisplays)
308{
309 /* This array is a cache of what was received from DevVMM so far.
310 * DevVMM may send to us partial information bout scree layout. This
311 * cache remembers entire picture. */
312 static struct VMMDevDisplayDef aVmMonitorsCache[VBOX_DRMIPC_MONITORS_MAX];
313 /* Number of valid (enabled) displays in output array. */
314 uint32_t cDisplaysOut = 0;
315 /* Flag indicates that current layout cache is consistent and can be passed to DRM stack. */
316 bool fValid = true;
317
318 /* Make sure input array fits cache size. */
319 if (cDisplaysIn > VBOX_DRMIPC_MONITORS_MAX)
320 {
321 VBClLogError("unable to validate screen layout: input (%u) array does not fit to cache size (%u)\n",
322 cDisplaysIn, VBOX_DRMIPC_MONITORS_MAX);
323 return VERR_INVALID_PARAMETER;
324 }
325
326 /* Make sure there is enough space in output array. */
327 if (cDisplaysIn > cDisplaysOutMax)
328 {
329 VBClLogError("unable to validate screen layout: input array (%u) is bigger than output one (%u)\n",
330 cDisplaysIn, cDisplaysOut);
331 return VERR_INVALID_PARAMETER;
332 }
333
334 /* Make sure input and output arrays are of non-zero size. */
335 if (!(cDisplaysIn > 0 && cDisplaysOutMax > 0))
336 {
337 VBClLogError("unable to validate screen layout: invalid size of either input (%u) or output display array\n",
338 cDisplaysIn, cDisplaysOutMax);
339 return VERR_INVALID_PARAMETER;
340 }
341
342 /* Update cache. */
343 for (uint32_t i = 0; i < cDisplaysIn; i++)
344 {
345 uint32_t idDisplay = aDisplaysIn[i].idDisplay;
346 if (idDisplay < VBOX_DRMIPC_MONITORS_MAX)
347 {
348 aVmMonitorsCache[idDisplay].idDisplay = idDisplay;
349 aVmMonitorsCache[idDisplay].fDisplayFlags = aDisplaysIn[i].fDisplayFlags;
350 aVmMonitorsCache[idDisplay].cBitsPerPixel = aDisplaysIn[i].cBitsPerPixel;
351 aVmMonitorsCache[idDisplay].cx = aDisplaysIn[i].cx;
352 aVmMonitorsCache[idDisplay].cy = aDisplaysIn[i].cy;
353 aVmMonitorsCache[idDisplay].xOrigin = aDisplaysIn[i].xOrigin;
354 aVmMonitorsCache[idDisplay].yOrigin = aDisplaysIn[i].yOrigin;
355 }
356 else
357 {
358 VBClLogError("received display ID (0x%x, position %u) is invalid\n", idDisplay, i);
359 /* If monitor configuration cannot be placed into cache, consider entire cache is invalid. */
360 fValid = false;
361 }
362 }
363
364 /* Now, go though complete cache and check if it is valid. */
365 for (uint32_t i = 0; i < VBOX_DRMIPC_MONITORS_MAX; i++)
366 {
367 if (i == 0)
368 {
369 if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
370 {
371 VBClLogError("unable to validate screen layout: first monitor is not allowed to be disabled\n");
372 fValid = false;
373 }
374 else
375 cDisplaysOut++;
376 }
377 else
378 {
379 /* Check if there is no hole in between monitors (i.e., if current monitor is enabled, but previous one does not). */
380 if ( !(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
381 && aVmMonitorsCache[i - 1].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
382 {
383 VBClLogError("unable to validate screen layout: there is a hole in displays layout config, "
384 "monitor (%u) is ENABLED while (%u) does not\n", i, i - 1);
385 fValid = false;
386 }
387 else
388 {
389 /* Always align screens since unaligned layout will result in disaster. */
390 aVmMonitorsCache[i].xOrigin = aVmMonitorsCache[i - 1].xOrigin + aVmMonitorsCache[i - 1].cx;
391 aVmMonitorsCache[i].yOrigin = aVmMonitorsCache[i - 1].yOrigin;
392
393 /* Only count enabled monitors. */
394 if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
395 cDisplaysOut++;
396 }
397 }
398 }
399
400 /* Copy out layout data. */
401 if (fValid)
402 {
403 /* Start with invalid display ID. */
404 uint32_t u32PrimaryDisplay = VBOX_DRMIPC_MONITORS_MAX;
405
406 for (uint32_t i = 0; i < cDisplaysOut; i++)
407 {
408 aDisplaysOut[i].x = aVmMonitorsCache[i].xOrigin;
409 aDisplaysOut[i].y = aVmMonitorsCache[i].yOrigin;
410 aDisplaysOut[i].w = aVmMonitorsCache[i].cx;
411 aDisplaysOut[i].h = aVmMonitorsCache[i].cy;
412
413 if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_PRIMARY)
414 {
415 /* Make sure display layout has only one primary display
416 * set (for display 0, host side sets primary flag, so exclude it). */
417 Assert(u32PrimaryDisplay == 0 || u32PrimaryDisplay == VBOX_DRMIPC_MONITORS_MAX);
418 u32PrimaryDisplay = i;
419 }
420
421 VBClLogVerbose(1, "update monitor %u parameters: %dx%d, (%d, %d)\n",
422 i, aDisplaysOut[i].w, aDisplaysOut[i].h, aDisplaysOut[i].x, aDisplaysOut[i].y);
423 }
424
425 *pu32PrimaryDisplay = u32PrimaryDisplay;
426 *pcActualDisplays = cDisplaysOut;
427 }
428
429 return (fValid && cDisplaysOut > 0) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
430}
431
432/**
433 * This function sends screen layout data to DRM stack.
434 *
435 * @return VINF_SUCCESS on success, IPRT error code otherwise.
436 * @param hDevice Handle to opened DRM device.
437 * @param paRects Array of screen configuration data.
438 * @param cRects Number of elements in screen configuration array.
439 */
440static int vbDrmSendHints(RTFILE hDevice, struct VBOX_DRMIPC_VMWRECT *paRects, uint32_t cRects)
441{
442 int rc = 0;
443 uid_t curuid;
444
445 /* Store real user id. */
446 curuid = getuid();
447
448 /* Change effective user id. */
449 if (setreuid(0, 0) == 0)
450 {
451 struct DRMVMWUPDATELAYOUT ioctlLayout;
452
453 RT_ZERO(ioctlLayout);
454 ioctlLayout.cOutputs = cRects;
455 ioctlLayout.ptrRects = (uint64_t)paRects;
456
457 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
458 &ioctlLayout, sizeof(ioctlLayout), NULL);
459
460 if (setreuid(curuid, 0) != 0)
461 {
462 VBClLogError("reset of setreuid failed after drm ioctl");
463 rc = VERR_ACCESS_DENIED;
464 }
465 }
466 else
467 {
468 VBClLogError("setreuid failed during drm ioctl\n");
469 rc = VERR_ACCESS_DENIED;
470 }
471
472 return rc;
473}
474
475/**
476 * Send monitor positions to host (thread safe).
477 *
478 * This function is accessed from DRMResize thread and from IPC Client thread.
479 *
480 * @return IPRT status code.
481 * @param cDisplays Number of displays (elements in pDisplays).
482 * @param pDisplays Displays parameters as it was sent to vmwgfx driver.
483 */
484static int vbDrmSendMonitorPositionsSync(uint32_t cDisplays, struct RTPOINT *pDisplays)
485{
486 int rc;
487
488 rc = RTCritSectEnter(&g_monitorPositionsCritSect);
489 if (RT_SUCCESS(rc))
490 {
491 rc = VbglR3SeamlessSendMonitorPositions(cDisplays, pDisplays);
492 int rc2 = RTCritSectLeave(&g_monitorPositionsCritSect);
493 if (RT_FAILURE(rc2))
494 VBClLogError("vbDrmSendMonitorPositionsSync: unable to leave critical section, rc=%Rrc\n", rc);
495 }
496 else
497 VBClLogError("vbDrmSendMonitorPositionsSync: unable to enter critical section, rc=%Rrc\n", rc);
498
499 return rc;
500}
501
502/**
503 * This function converts vmwgfx monitors layout data into an array of monitor offsets
504 * and sends it back to the host in order to ensure that host and guest have the same
505 * monitors layout representation.
506 *
507 * @return IPRT status code.
508 * @param cDisplays Number of displays (elements in pDisplays).
509 * @param pDisplays Displays parameters as it was sent to vmwgfx driver.
510 */
511static int drmSendMonitorPositions(uint32_t cDisplays, struct VBOX_DRMIPC_VMWRECT *pDisplays)
512{
513 static RTPOINT aPositions[VBOX_DRMIPC_MONITORS_MAX];
514
515 if (!pDisplays || !cDisplays || cDisplays > VBOX_DRMIPC_MONITORS_MAX)
516 {
517 return VERR_INVALID_PARAMETER;
518 }
519
520 /* Prepare monitor offsets list to be sent to the host. */
521 for (uint32_t i = 0; i < cDisplays; i++)
522 {
523 aPositions[i].x = pDisplays[i].x;
524 aPositions[i].y = pDisplays[i].y;
525 }
526
527 return vbDrmSendMonitorPositionsSync(cDisplays, aPositions);
528}
529
530/** Worker thread for resize task. */
531static DECLCALLBACK(int) vbDrmResizeWorker(RTTHREAD ThreadSelf, void *pvUser)
532{
533 int rc = VERR_GENERAL_FAILURE;
534 RTFILE hDevice = (RTFILE)pvUser;
535
536 RT_NOREF1(ThreadSelf);
537
538 AssertReturn(hDevice, VERR_INVALID_PARAMETER);
539
540 for (;;)
541 {
542 /* Do not acknowledge the first event we query for to pick up old events,
543 * e.g. from before a guest reboot. */
544 bool fAck = false;
545
546 uint32_t events;
547
548 VMMDevDisplayDef aDisplaysIn[VBOX_DRMIPC_MONITORS_MAX];
549 uint32_t cDisplaysIn = 0;
550
551 struct VBOX_DRMIPC_VMWRECT aDisplaysOut[VBOX_DRMIPC_MONITORS_MAX];
552 uint32_t cDisplaysOut = 0;
553
554 RT_ZERO(aDisplaysIn);
555 RT_ZERO(aDisplaysOut);
556
557 /* Query the first size without waiting. This lets us e.g. pick up
558 * the last event before a guest reboot when we start again after. */
559 rc = VbglR3GetDisplayChangeRequestMulti(VBOX_DRMIPC_MONITORS_MAX, &cDisplaysIn, aDisplaysIn, fAck);
560 fAck = true;
561 if (RT_SUCCESS(rc))
562 {
563 uint32_t u32PrimaryDisplay = VBOX_DRMIPC_MONITORS_MAX;
564 static uint32_t u32PrimaryDisplayLast = VBOX_DRMIPC_MONITORS_MAX;
565
566 /* Validate displays layout and push it to DRM stack if valid. */
567 rc = vbDrmValidateLayout(aDisplaysIn, cDisplaysIn, aDisplaysOut, &u32PrimaryDisplay, sizeof(aDisplaysOut), &cDisplaysOut);
568 if (RT_SUCCESS(rc))
569 {
570 rc = vbDrmSendHints(hDevice, aDisplaysOut, cDisplaysOut);
571 VBClLogInfo("push screen layout data of %u display(s) to DRM stack has %s (%Rrc)\n",
572 cDisplaysOut, RT_SUCCESS(rc) ? "succeeded" : "failed", rc);
573 /* In addition, notify host that configuration was successfully applied to the guest vmwgfx driver. */
574 if (RT_SUCCESS(rc))
575 {
576 rc = drmSendMonitorPositions(cDisplaysOut, aDisplaysOut);
577 if (RT_FAILURE(rc))
578 VBClLogError("cannot send host notification: %Rrc\n", rc);
579
580 /* If information about primary display is present in display layout, send it to DE over IPC. */
581 if (u32PrimaryDisplay != VBOX_DRMIPC_MONITORS_MAX
582 && u32PrimaryDisplayLast != u32PrimaryDisplay)
583 {
584 rc = vbDrmIpcBroadcastPrimaryDisplay(u32PrimaryDisplay);
585
586 /* Cache last value in order to avoid sending duplicate data over IPC. */
587 u32PrimaryDisplayLast = u32PrimaryDisplay;
588
589 VBClLogVerbose(2, "DE was notified that display %u is now primary, rc=%Rrc\n", u32PrimaryDisplay, rc);
590 }
591 else
592 VBClLogVerbose(2, "do not notify DE that display %u is now primary, rc=%Rrc\n", u32PrimaryDisplay, rc);
593 }
594 }
595 else if (rc == VERR_DUPLICATE)
596 VBClLogVerbose(2, "do not notify DRM stack about monitors layout change, rc=%Rrc\n", rc);
597 else
598 VBClLogError("displays layout is invalid, will not notify guest driver, rc=%Rrc\n", rc);
599 }
600 else
601 VBClLogError("Failed to get display change request, rc=%Rrc\n", rc);
602
603 do
604 {
605 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, VBOX_DRMIPC_RX_TIMEOUT_MS, &events);
606 } while (rc == VERR_TIMEOUT && !ASMAtomicReadBool(&g_fShutdown));
607
608 if (ASMAtomicReadBool(&g_fShutdown))
609 {
610 VBClLogInfo("exiting resize thread: shutdown requested\n");
611 /* This is a case when we should return positive status. */
612 rc = (rc == VERR_TIMEOUT) ? VINF_SUCCESS : rc;
613 break;
614 }
615 else if (RT_FAILURE(rc))
616 VBClLogFatalError("VBoxDRMClient: resize thread: failure waiting for event, rc=%Rrc\n", rc);
617 }
618
619 return rc;
620}
621
622/**
623 * Go over all existing IPC client connection and put set-primary-screen request
624 * data into TX queue of each of them .
625 *
626 * @return IPRT status code.
627 * @param u32PrimaryDisplay Primary display ID.
628 */
629static int vbDrmIpcBroadcastPrimaryDisplay(uint32_t u32PrimaryDisplay)
630{
631 int rc;
632
633 rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
634 if (RT_SUCCESS(rc))
635 {
636 if (!RTListIsEmpty(&g_ipcClientConnectionsList.Node))
637 {
638 PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pEntry;
639 RTListForEach(&g_ipcClientConnectionsList.Node, pEntry, VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE, Node)
640 {
641 AssertReturn(pEntry, VERR_INVALID_PARAMETER);
642 AssertReturn(pEntry->pClient, VERR_INVALID_PARAMETER);
643 AssertReturn(pEntry->pClient->hThread, VERR_INVALID_PARAMETER);
644
645 rc = vbDrmIpcSetPrimaryDisplay(pEntry->pClient, u32PrimaryDisplay);
646
647 VBClLogInfo("thread %s notified IPC Client that display %u is now primary, rc=%Rrc\n",
648 RTThreadGetName(pEntry->pClient->hThread), u32PrimaryDisplay, rc);
649 }
650 }
651
652 int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
653 if (RT_FAILURE(rc2))
654 VBClLogError("notify DE: unable to leave critical section, rc=%Rrc\n", rc2);
655 }
656 else
657 VBClLogError("notify DE: unable to enter critical section, rc=%Rrc\n", rc);
658
659 return rc;
660}
661
662/**
663 * Main loop for IPC client connection handling.
664 *
665 * @return IPRT status code.
666 * @param pClient Pointer to IPC client data.
667 */
668static int vbDrmIpcConnectionProc(PVBOX_DRMIPC_CLIENT pClient)
669{
670 int rc = VERR_GENERAL_FAILURE;
671
672 AssertReturn(pClient, VERR_INVALID_PARAMETER);
673
674 /* This loop handles incoming messages. */
675 for (;;)
676 {
677 rc = vbDrmIpcConnectionHandler(pClient);
678
679 /* Try to detect if we should shutdown as early as we can. */
680 if (ASMAtomicReadBool(&g_fShutdown))
681 break;
682
683 /* Normal case. No data received within short interval. */
684 if (rc == VERR_TIMEOUT)
685 {
686 continue;
687 }
688 else if (RT_FAILURE(rc))
689 {
690 /* Terminate connection handling in case of error. */
691 VBClLogError("unable to handle IPC session, rc=%Rrc\n", rc);
692 break;
693 }
694 }
695
696 return rc;
697}
698
699/**
700 * Add IPC client connection data into list of connections.
701 *
702 * List size is limited indirectly by DRM_IPC_SERVER_CONNECTIONS_MAX value.
703 * This function should only be invoked from client thread context
704 * (from vbDrmIpcClientWorker() in particular).
705 *
706 * @return IPRT status code.
707 * @param pClientNode Client connection information to add to the list.
708 */
709static int vbDrmIpcClientsListAdd(PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pClientNode)
710{
711 int rc;
712
713 AssertReturn(pClientNode, VERR_INVALID_PARAMETER);
714
715 rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
716 if (RT_SUCCESS(rc))
717 {
718 RTListAppend(&g_ipcClientConnectionsList.Node, &pClientNode->Node);
719
720 int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
721 if (RT_FAILURE(rc2))
722 VBClLogError("add client connection: unable to leave critical section, rc=%Rrc\n", rc2);
723 }
724 else
725 VBClLogError("add client connection: unable to enter critical section, rc=%Rrc\n", rc);
726
727 return rc;
728}
729
730/**
731 * Remove IPC client connection data from list of connections.
732 *
733 * This function should only be invoked from client thread context
734 * (from vbDrmIpcClientWorker() in particular).
735 *
736 * @return IPRT status code.
737 * @param pClientNode Client connection information to remove from the list.
738 */
739static int vbDrmIpcClientsListRemove(PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pClientNode)
740{
741 int rc;
742 PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pEntry, pNextEntry, pFound = NULL;
743
744 AssertReturn(pClientNode, VERR_INVALID_PARAMETER);
745
746 rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
747 if (RT_SUCCESS(rc))
748 {
749
750 if (!RTListIsEmpty(&g_ipcClientConnectionsList.Node))
751 {
752 RTListForEachSafe(&g_ipcClientConnectionsList.Node, pEntry, pNextEntry, VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE, Node)
753 {
754 if (pEntry == pClientNode)
755 pFound = (PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE)RTListNodeRemoveRet(&pEntry->Node);
756 }
757 }
758 else
759 VBClLogError("remove client connection: connections list empty, node %p not there\n", pClientNode);
760
761 int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
762 if (RT_FAILURE(rc2))
763 VBClLogError("remove client connection: unable to leave critical section, rc=%Rrc\n", rc2);
764 }
765 else
766 VBClLogError("remove client connection: unable to enter critical section, rc=%Rrc\n", rc);
767
768 if (!pFound)
769 VBClLogError("remove client connection: node not found\n");
770
771 return !rc && pFound ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
772}
773
774/**
775 * @interface_method_impl{VBOX_DRMIPC_CLIENT,pfnRxCb}
776 */
777static DECLCALLBACK(int) vbDrmIpcClientRxCallBack(uint8_t idCmd, void *pvData, uint32_t cbData)
778{
779 int rc = VERR_INVALID_PARAMETER;
780
781 AssertReturn(pvData, VERR_INVALID_PARAMETER);
782 AssertReturn(cbData, VERR_INVALID_PARAMETER);
783
784 switch (idCmd)
785 {
786 case VBOXDRMIPCSRVCMD_REPORT_DISPLAY_OFFSETS:
787 {
788 PVBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS pCmd = (PVBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS)pvData;
789 AssertReturn(cbData == sizeof(VBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS), VERR_INVALID_PARAMETER);
790 rc = vbDrmSendMonitorPositionsSync(pCmd->cOffsets, pCmd->paOffsets);
791 break;
792 }
793
794 default:
795 {
796 VBClLogError("received unknown IPC command 0x%x\n", idCmd);
797 break;
798 }
799 }
800
801 return rc;
802}
803
804/** Worker thread for IPC client task. */
805static DECLCALLBACK(int) vbDrmIpcClientWorker(RTTHREAD ThreadSelf, void *pvUser)
806{
807 VBOX_DRMIPC_CLIENT hClient = VBOX_DRMIPC_CLIENT_INITIALIZER;
808 RTLOCALIPCSESSION hSession = (RTLOCALIPCSESSION)pvUser;
809 int rc;
810
811 AssertReturn(RT_VALID_PTR(hSession), VERR_INVALID_PARAMETER);
812
813 /* Initialize client session resources. */
814 rc = vbDrmIpcClientInit(&hClient, ThreadSelf, hSession, VBOX_DRMIPC_TX_QUEUE_SIZE, vbDrmIpcClientRxCallBack);
815 if (RT_SUCCESS(rc))
816 {
817 /* Add IPC client connection data into clients list. */
818 VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE hClientNode = { { 0, 0 } , &hClient };
819
820 rc = vbDrmIpcClientsListAdd(&hClientNode);
821 if (RT_SUCCESS(rc))
822 {
823 rc = RTThreadUserSignal(ThreadSelf);
824 if (RT_SUCCESS(rc))
825 {
826 /* Start spinning the connection. */
827 VBClLogInfo("IPC client connection started\n", rc);
828 rc = vbDrmIpcConnectionProc(&hClient);
829 VBClLogInfo("IPC client connection ended, rc=%Rrc\n", rc);
830 }
831 else
832 VBClLogError("unable to report IPC client connection handler start, rc=%Rrc\n", rc);
833
834 /* Remove IPC client connection data from clients list. */
835 rc = vbDrmIpcClientsListRemove(&hClientNode);
836 if (RT_FAILURE(rc))
837 VBClLogError("unable to remove IPC client session from list of connections, rc=%Rrc\n", rc);
838 }
839 else
840 VBClLogError("unable to add IPC client connection to the list, rc=%Rrc\n");
841
842 /* Disconnect remote peer if still connected. */
843 if (RT_VALID_PTR(hSession))
844 {
845 rc = RTLocalIpcSessionClose(hSession);
846 VBClLogInfo("IPC session closed, rc=%Rrc\n", rc);
847 }
848
849 /* Connection handler loop has ended, release session resources. */
850 rc = vbDrmIpcClientReleaseResources(&hClient);
851 if (RT_FAILURE(rc))
852 VBClLogError("unable to release IPC client session, rc=%Rrc\n", rc);
853
854 ASMAtomicDecU32(&g_cDrmIpcConnections);
855 }
856 else
857 VBClLogError("unable to initialize IPC client session, rc=%Rrc\n", rc);
858
859 VBClLogInfo("closing IPC client session, rc=%Rrc\n", rc);
860
861 return rc;
862}
863
864/**
865 * Start processing thread for IPC client requests handling.
866 *
867 * @returns IPRT status code.
868 * @param hSession IPC client connection handle.
869 */
870static int vbDrmIpcClientStart(RTLOCALIPCSESSION hSession)
871{
872 int rc;
873 /** DRM client thread (IpcCLT-%u). */
874 RTTHREAD hThread = 0;
875 RTPROCESS hProcess = 0;
876
877 rc = RTLocalIpcSessionQueryProcess(hSession, &hProcess);
878 if (RT_SUCCESS(rc))
879 {
880 char pszThreadName[DRM_IPC_THREAD_NAME_MAX];
881 RT_ZERO(pszThreadName);
882
883 RTStrPrintf2(pszThreadName, DRM_IPC_THREAD_NAME_MAX, DRM_IPC_CLIENT_THREAD_NAME_PTR, hProcess);
884
885 /* Attempt to start IPC client connection handler task. */
886 rc = RTThreadCreate(&hThread, vbDrmIpcClientWorker, (void *)hSession, 0,
887 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pszThreadName);
888 if (RT_SUCCESS(rc))
889 {
890 rc = RTThreadUserWait(hThread, RT_MS_5SEC);
891 }
892 }
893
894 return rc;
895}
896
897/** Worker thread for IPC server task. */
898static DECLCALLBACK(int) vbDrmIpcServerWorker(RTTHREAD ThreadSelf, void *pvUser)
899{
900 int rc = VERR_GENERAL_FAILURE;
901 RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
902
903 RT_NOREF1(ThreadSelf);
904
905 AssertReturn(hIpcServer, VERR_INVALID_PARAMETER);
906
907 /* This loop accepts incoming connections. */
908 for (;;)
909 {
910 RTLOCALIPCSESSION hClientSession;
911
912 /* Wait for incoming connection. */
913 rc = RTLocalIpcServerListen(hIpcServer, &hClientSession);
914 if (RT_SUCCESS(rc))
915 {
916 VBClLogVerbose(2, "new IPC session\n");
917
918 if (ASMAtomicIncU32(&g_cDrmIpcConnections) <= DRM_IPC_SERVER_CONNECTIONS_MAX)
919 {
920 /* Authenticate remote peer. */
921 rc = vbDrmIpcAuth(hClientSession);
922 if (RT_SUCCESS(rc))
923 {
924 /* Start incoming connection handler thread. */
925 rc = vbDrmIpcClientStart(hClientSession);
926 VBClLogVerbose(2, "connection processing ended, rc=%Rrc\n", rc);
927 }
928 else
929 VBClLogError("IPC authentication failed, rc=%Rrc\n", rc);
930 }
931 else
932 rc = VERR_RESOURCE_BUSY;
933
934 /* Release resources in case of error. */
935 if (RT_FAILURE(rc))
936 {
937 VBClLogError("maximum amount of IPC client connections reached, dropping connection\n");
938
939 int rc2 = RTLocalIpcSessionClose(hClientSession);
940 if (RT_FAILURE(rc2))
941 VBClLogError("unable to close IPC session, rc=%Rrc\n", rc2);
942
943 ASMAtomicDecU32(&g_cDrmIpcConnections);
944 }
945 }
946 else
947 VBClLogError("IPC authentication failed, rc=%Rrc\n", rc);
948
949 /* Check shutdown was requested. */
950 if (ASMAtomicReadBool(&g_fShutdown))
951 {
952 VBClLogInfo("exiting IPC thread: shutdown requested\n");
953 break;
954 }
955
956 /* Wait a bit before spinning a loop if something went wrong. */
957 if (RT_FAILURE(rc))
958 RTThreadSleep(VBOX_DRMIPC_RX_RELAX_MS);
959 }
960
961 return rc;
962}
963
964/** A signal handler. */
965static void vbDrmRequestShutdown(int sig)
966{
967 RT_NOREF(sig);
968 ASMAtomicWriteBool(&g_fShutdown, true);
969}
970
971int main(int argc, char *argv[])
972{
973 /** Custom log prefix to be used for logger instance of this process. */
974 static const char *pszLogPrefix = "VBoxDRMClient:";
975
976 static const RTGETOPTDEF s_aOptions[] = { { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, };
977 RTGETOPTUNION ValueUnion;
978 RTGETOPTSTATE GetState;
979 int ch;
980
981 RTFILE hDevice = NIL_RTFILE;
982 RTFILE hPidFile;
983
984 RTLOCALIPCSERVER hIpcServer;
985 /** DRM IPC server thread (DrmIpcSRV). */
986 RTTHREAD vbDrmIpcThread;
987 int rcDrmIpcThread = 0;
988
989 /** DRM resize thread: receive resize data from host and apply it to local DRM stack (DrmResizeThread). */
990 RTTHREAD drmResizeThread;
991 int rcDrmResizeThread = 0;
992 int rc, rc2 = 0;
993
994 rc = RTR3InitExe(argc, &argv, 0);
995 if (RT_FAILURE(rc))
996 return RTMsgInitFailure(rc);
997
998 rc = VbglR3InitUser();
999 if (RT_FAILURE(rc))
1000 VBClLogFatalError("VBoxDRMClient: VbglR3InitUser failed: %Rrc", rc);
1001
1002 /* Process command line options. */
1003 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1004 if (RT_FAILURE(rc))
1005 VBClLogFatalError("VBoxDRMClient: unable to process command line options, rc=%Rrc\n", rc);
1006 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1007 {
1008 switch (ch)
1009 {
1010 case 'v':
1011 {
1012 g_cVerbosity++;
1013 break;
1014 }
1015
1016 case VERR_GETOPT_UNKNOWN_OPTION:
1017 {
1018 VBClLogFatalError("unknown command line option '%s'\n", ValueUnion.psz);
1019 return RTEXITCODE_SYNTAX;
1020
1021 }
1022
1023 default:
1024 break;
1025 }
1026 }
1027
1028 rc = VBClLogCreate("");
1029 if (RT_FAILURE(rc))
1030 VBClLogFatalError("VBoxDRMClient: failed to setup logging, rc=%Rrc\n", rc);
1031 VBClLogSetLogPrefix(pszLogPrefix);
1032
1033 /* Check PID file before attempting to initialize anything. */
1034 rc = VbglR3PidFile(g_pszPidFile, &hPidFile);
1035 if (rc == VERR_FILE_LOCK_VIOLATION)
1036 {
1037 VBClLogInfo("already running, exiting\n");
1038 return RTEXITCODE_SUCCESS;
1039 }
1040 if (RT_FAILURE(rc))
1041 {
1042 VBClLogError("unable to lock PID file (%Rrc), exiting\n", rc);
1043 return RTEXITCODE_FAILURE;
1044 }
1045
1046 hDevice = vbDrmOpenVmwgfx();
1047 if (hDevice == NIL_RTFILE)
1048 return RTEXITCODE_FAILURE;
1049
1050 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
1051 if (RT_FAILURE(rc))
1052 {
1053 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
1054 return RTEXITCODE_FAILURE;
1055 }
1056 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
1057 if (RT_FAILURE(rc))
1058 {
1059 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
1060 return RTEXITCODE_FAILURE;
1061 }
1062
1063 /* Setup signals: gracefully terminate on SIGINT, SIGTERM. */
1064 if ( signal(SIGINT, vbDrmRequestShutdown) == SIG_ERR
1065 || signal(SIGTERM, vbDrmRequestShutdown) == SIG_ERR)
1066 {
1067 VBClLogError("unable to setup signals\n");
1068 return RTEXITCODE_FAILURE;
1069 }
1070
1071 /* Init IPC client connection list. */
1072 RTListInit(&g_ipcClientConnectionsList.Node);
1073 rc = RTCritSectInit(&g_ipcClientConnectionsListCritSect);
1074 if (RT_FAILURE(rc))
1075 {
1076 VBClLogError("unable to initialize IPC client connection list critical section\n");
1077 return RTEXITCODE_FAILURE;
1078 }
1079
1080 /* Init critical section which is used for reporting monitors offset back to host. */
1081 rc = RTCritSectInit(&g_monitorPositionsCritSect);
1082 if (RT_FAILURE(rc))
1083 {
1084 VBClLogError("unable to initialize monitors position critical section\n");
1085 return RTEXITCODE_FAILURE;
1086 }
1087
1088 /* Instantiate IPC server for VBoxClient service communication. */
1089 rc = RTLocalIpcServerCreate(&hIpcServer, VBOX_DRMIPC_SERVER_NAME, 0);
1090 if (RT_FAILURE(rc))
1091 {
1092 VBClLogError("unable to setup IPC server, rc=%Rrc\n", rc);
1093 return RTEXITCODE_FAILURE;
1094 }
1095
1096 struct group *pGrp;
1097 pGrp = getgrnam(VBOX_DRMIPC_USER_GROUP);
1098 if (pGrp)
1099 {
1100 rc = RTLocalIpcServerGrantGroupAccess(hIpcServer, pGrp->gr_gid);
1101 if (RT_FAILURE(rc))
1102 VBClLogError("unable to grant IPC server socket access to '" VBOX_DRMIPC_USER_GROUP "', rc=%Rrc\n", rc);
1103 }
1104 else
1105 VBClLogError("unable to grant IPC server socket access to '" VBOX_DRMIPC_USER_GROUP "', group does not exist\n");
1106
1107 /* Attempt to start DRM resize task. */
1108 rc = RTThreadCreate(&drmResizeThread, vbDrmResizeWorker, (void *)hDevice, 0,
1109 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, DRM_RESIZE_THREAD_NAME);
1110 if (RT_SUCCESS(rc))
1111 {
1112 /* Attempt to start IPC task. */
1113 rc = RTThreadCreate(&vbDrmIpcThread, vbDrmIpcServerWorker, (void *)hIpcServer, 0,
1114 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, DRM_IPC_SERVER_THREAD_NAME);
1115 if (RT_SUCCESS(rc))
1116 {
1117 /* HACK ALERT!
1118 * The sequence of RTThreadWait(drmResizeThread) -> RTLocalIpcServerDestroy() -> RTThreadWait(vbDrmIpcThread)
1119 * is intentional! Once process received a signal, it will pull g_fShutdown flag, which in turn will cause
1120 * drmResizeThread to quit. The vbDrmIpcThread might hang on accept() call, so we terminate IPC server to
1121 * release it and then wait for its termination. */
1122
1123 rc = RTThreadWait(drmResizeThread, RT_INDEFINITE_WAIT, &rcDrmResizeThread);
1124 VBClLogInfo("%s thread exited with status, rc=%Rrc\n", DRM_RESIZE_THREAD_NAME, rcDrmResizeThread);
1125
1126 rc = RTLocalIpcServerCancel(hIpcServer);
1127 if (RT_FAILURE(rc))
1128 VBClLogError("unable to notify IPC server about shutdown, rc=%Rrc\n", rc);
1129
1130 /* Wait for threads to terminate gracefully. */
1131 rc = RTThreadWait(vbDrmIpcThread, RT_INDEFINITE_WAIT, &rcDrmIpcThread);
1132 VBClLogInfo("%s thread exited with status, rc=%Rrc\n", DRM_IPC_SERVER_THREAD_NAME, rcDrmResizeThread);
1133
1134 }
1135 else
1136 VBClLogError("unable to start IPC thread, rc=%Rrc\n", rc);
1137 }
1138 else
1139 VBClLogError("unable to start resize thread, rc=%Rrc\n", rc);
1140
1141 rc = RTLocalIpcServerDestroy(hIpcServer);
1142 if (RT_FAILURE(rc))
1143 VBClLogError("unable to stop IPC server, rc=%Rrc\n", rc);
1144
1145 rc2 = RTCritSectDelete(&g_monitorPositionsCritSect);
1146 if (RT_FAILURE(rc2))
1147 VBClLogError("unable to destroy g_monitorPositionsCritSect critsect, rc=%Rrc\n", rc2);
1148
1149 rc2 = RTCritSectDelete(&g_ipcClientConnectionsListCritSect);
1150 if (RT_FAILURE(rc2))
1151 VBClLogError("unable to destroy g_ipcClientConnectionsListCritSect critsect, rc=%Rrc\n", rc2);
1152
1153 RTFileClose(hDevice);
1154
1155 VBClLogInfo("releasing PID file lock\n");
1156 VbglR3ClosePidFile(g_pszPidFile, hPidFile);
1157
1158 VBClLogDestroy();
1159
1160 return rc == 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1161}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette