VirtualBox

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

Last change on this file since 93381 was 93380, checked in by vboxsync, 3 years ago

Additions: Linux: scm fixes, bugref:10185.

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