VirtualBox

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

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

Additions: Linux: update description for guest screen resizing code, bugref:10134.

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

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