VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/display.cpp@ 98474

Last change on this file since 98474 was 98474, checked in by vboxsync, 16 months ago

Additions: X11: Add possibility restart VBoxClient processes during Guest Additions update, bugref:10359.

This commit makes VBoxClient processes to temporary release reference to vboxguest kernel module and then
restart itself. So module can be reloaded and newly started VBoxClient instance will utilize updated module.

When VBoxClient starts in demonized mode, it forks a child process which represents actual service. Parent
process continues to run and its main function is to restart child when it crashes or terminates with exit
status != 0. This commit makes child process to catch SIGUSR1 and terminate with exit
status VBGLR3EXITCODERELOAD (currently equal to 2). Parent process will detect this and in turn will release
its reference to vboxguest kernel module, allowing to reload it. The parent process will then wait for SIGUSR1
itself. Once received, it will restart itself (loading new, updated VBoxClient process image). This is a part
of the procedure to install and reload/restart Guest Additions kernel modules and user services without
requiring guest reboot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 KB
Line 
1/* $Id: display.cpp 98474 2023-02-03 19:20:53Z vboxsync $ */
2/** @file
3 * X11 guest client - display management.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include "VBoxClient.h"
29
30#include <iprt/errcore.h>
31#include <iprt/file.h>
32#include <iprt/mem.h>
33#include <iprt/string.h>
34
35#include <X11/Xlib.h>
36#include <X11/Xatom.h>
37#include <X11/extensions/Xrandr.h>
38
39/** @todo this should probably be replaced by something IPRT */
40/* For system() and WEXITSTATUS() */
41#include <stdlib.h>
42#include <sys/types.h>
43#include <sys/wait.h>
44#include <errno.h>
45#include <limits.h>
46#include <poll.h>
47#include <time.h>
48#include <dlfcn.h>
49
50/* TESTING: Dynamic resizing and mouse integration toggling should work
51 * correctly with a range of X servers (pre-1.3, 1.3 and later under Linux, 1.3
52 * and later under Solaris) with Guest Additions installed. Switching to a
53 * virtual terminal while a user session is in place should disable dynamic
54 * resizing and cursor integration, switching back should re-enable them. */
55
56/** State information needed for the service. The main VBoxClient code provides
57 * the daemon logic needed by all services. */
58struct DISPLAYSTATE
59{
60 /** Are we initialised yet? */
61 bool mfInit;
62 /** The connection to the server. */
63 Display *pDisplay;
64 /** The RandR extension base event number. */
65 int cRREventBase;
66 /** Can we use version 1.2 or later of the RandR protocol here? */
67 bool fHaveRandR12;
68 /** The command argument to use for the xrandr binary. Currently only
69 * used to support the non-standard location on some Solaris systems -
70 * would it make sense to use absolute paths on all systems? */
71 const char *pcszXrandr;
72 /** Was there a recent mode hint with no following root window resize, and
73 * if so, have we waited for a reasonable time? */
74 time_t timeLastModeHint;
75 /** Handle to libXrandr. */
76 void *pRandLibraryHandle;
77 /** Handle to pXRRSelectInput. */
78 void (*pXRRSelectInput) (Display *, Window, int);
79 /** Handle to pXRRQueryExtension. */
80 Bool (*pXRRQueryExtension) (Display *, int *, int *);
81};
82
83static struct DISPLAYSTATE g_DisplayState;
84
85static unsigned char *getRootProperty(struct DISPLAYSTATE *pState, const char *pszName,
86 long cItems, Atom type)
87{
88 Atom actualType = None;
89 int iFormat = 0;
90 unsigned long cReturned = 0;
91 unsigned long cAfter = 0;
92 unsigned char *pData = 0;
93
94 if (XGetWindowProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
95 XInternAtom(pState->pDisplay, pszName, 0), 0, cItems,
96 False /* delete */, type, &actualType, &iFormat,
97 &cReturned, &cAfter, &pData))
98 return NULL;
99 return pData;
100}
101
102static void doResize(struct DISPLAYSTATE *pState)
103{
104 /** @note The xrandr command can fail if something else accesses RandR at
105 * the same time. We just ignore failure for now as we do not know what
106 * someone else is doing. */
107 if (!pState->fHaveRandR12)
108 {
109 char szCommand[256];
110 unsigned char *pData;
111
112 pData = getRootProperty(pState, "VBOXVIDEO_PREFERRED_MODE", 1, XA_INTEGER);
113 if (pData != NULL)
114 {
115 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
116 pState->pcszXrandr, ((unsigned long *)pData)[0] >> 16, ((unsigned long *)pData)[0] & 0xFFFF);
117 int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc);
118 XFree(pData);
119 }
120 }
121 else
122 {
123 const char szCommandBase[] =
124 "%s --output VGA-0 --auto --output VGA-1 --auto --right-of VGA-0 "
125 "--output VGA-2 --auto --right-of VGA-1 --output VGA-3 --auto --right-of VGA-2 "
126 "--output VGA-4 --auto --right-of VGA-3 --output VGA-5 --auto --right-of VGA-4 "
127 "--output VGA-6 --auto --right-of VGA-5 --output VGA-7 --auto --right-of VGA-6 "
128 "--output VGA-8 --auto --right-of VGA-7 --output VGA-9 --auto --right-of VGA-8 "
129 "--output VGA-10 --auto --right-of VGA-9 --output VGA-11 --auto --right-of VGA-10 "
130 "--output VGA-12 --auto --right-of VGA-11 --output VGA-13 --auto --right-of VGA-12 "
131 "--output VGA-14 --auto --right-of VGA-13 --output VGA-15 --auto --right-of VGA-14 "
132 "--output VGA-16 --auto --right-of VGA-15 --output VGA-17 --auto --right-of VGA-16 "
133 "--output VGA-18 --auto --right-of VGA-17 --output VGA-19 --auto --right-of VGA-18 "
134 "--output VGA-20 --auto --right-of VGA-19 --output VGA-21 --auto --right-of VGA-20 "
135 "--output VGA-22 --auto --right-of VGA-21 --output VGA-23 --auto --right-of VGA-22 "
136 "--output VGA-24 --auto --right-of VGA-23 --output VGA-25 --auto --right-of VGA-24 "
137 "--output VGA-26 --auto --right-of VGA-25 --output VGA-27 --auto --right-of VGA-26 "
138 "--output VGA-28 --auto --right-of VGA-27 --output VGA-29 --auto --right-of VGA-28 "
139 "--output VGA-30 --auto --right-of VGA-29 --output VGA-31 --auto --right-of VGA-30";
140 char szCommand[sizeof(szCommandBase) + 256];
141 RTStrPrintf(szCommand, sizeof(szCommand), szCommandBase, pState->pcszXrandr);
142 int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc);
143 }
144}
145
146/** Main loop: handle display hot-plug events, property updates (which can
147 * signal VT switches hot-plug in old X servers). */
148static void runDisplay(struct DISPLAYSTATE *pState)
149{
150 Display *pDisplay = pState->pDisplay;
151 long cValue = 1;
152
153 /* One way or another we want the preferred mode at server start-up. */
154 doResize(pState);
155 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask | StructureNotifyMask);
156 if (pState->fHaveRandR12)
157 pState->pXRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask);
158 /* Semantics: when VBOXCLIENT_STARTED is set, pre-1.3 X.Org Server driver
159 * assumes that a client capable of handling mode hints will be present for the
160 * rest of the X session. If we crash things will not work as they should.
161 * I thought that preferable to implementing complex crash-handling logic.
162 */
163 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOXCLIENT_STARTED", 0),
164 XA_INTEGER, 32, PropModeReplace, (unsigned char *)&cValue, 1);
165 /* Interrupting this cleanly will be more work than making it robust
166 * against spontaneous termination, especially as it will never get
167 * properly tested, so I will go for the second. */
168 while (true)
169 {
170 XEvent event;
171 struct pollfd PollFd;
172 int pollTimeOut = -1;
173 int cFds;
174
175 /* Do not handle overflow. */
176 if (pState->timeLastModeHint > 0 && pState->timeLastModeHint < INT_MAX - 2)
177 pollTimeOut = 2 - (time(0) - pState->timeLastModeHint);
178 PollFd.fd = ConnectionNumber(pDisplay);
179 PollFd.events = POLLIN; /* Hang-up is always reported. */
180 XFlush(pDisplay);
181 cFds = poll(&PollFd, 1, pollTimeOut >= 0 ? pollTimeOut * 1000 : -1);
182 while (XPending(pDisplay))
183 {
184 XNextEvent(pDisplay, &event);
185 /* This property is deleted when the server regains the virtual
186 * terminal. Force the main thread to call xrandr again, as old X
187 * servers could not handle it while switched out. */
188 if ( !pState->fHaveRandR12
189 && event.type == PropertyNotify
190 && event.xproperty.state == PropertyDelete
191 && event.xproperty.window == DefaultRootWindow(pDisplay)
192 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False))
193 doResize(pState);
194 if ( !pState->fHaveRandR12
195 && event.type == PropertyNotify
196 && event.xproperty.state == PropertyNewValue
197 && event.xproperty.window == DefaultRootWindow(pDisplay)
198 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_PREFERRED_MODE", False))
199 doResize(pState);
200 if ( pState->fHaveRandR12
201 && event.type == pState->cRREventBase + RRScreenChangeNotify)
202 pState->timeLastModeHint = time(0);
203 if ( event.type == ConfigureNotify
204 && event.xproperty.window == DefaultRootWindow(pDisplay))
205 pState->timeLastModeHint = 0;
206 }
207 if (cFds == 0 && pState->timeLastModeHint > 0)
208 doResize(pState);
209 }
210}
211
212static int initDisplay(struct DISPLAYSTATE *pState)
213{
214 char szCommand[256];
215 int status;
216
217 pState->pRandLibraryHandle = dlopen("libXrandr.so", RTLD_LAZY /*| RTLD_LOCAL */);
218 if (!pState->pRandLibraryHandle)
219 pState->pRandLibraryHandle = dlopen("libXrandr.so.2", RTLD_LAZY /*| RTLD_LOCAL */);
220 if (!pState->pRandLibraryHandle)
221 pState->pRandLibraryHandle = dlopen("libXrandr.so.2.2.0", RTLD_LAZY /*| RTLD_LOCAL */);
222
223 if (!RT_VALID_PTR(pState->pRandLibraryHandle))
224 {
225 VBClLogFatalError("Could not locate libXrandr for dlopen\n");
226 return VERR_NOT_FOUND;
227 }
228
229 *(void **)(&pState->pXRRSelectInput) = dlsym(pState->pRandLibraryHandle, "XRRSelectInput");
230 *(void **)(&pState->pXRRQueryExtension) = dlsym(pState->pRandLibraryHandle, "XRRQueryExtension");
231
232 if ( !RT_VALID_PTR(pState->pXRRSelectInput)
233 || !RT_VALID_PTR(pState->pXRRQueryExtension))
234 {
235 VBClLogFatalError("Could not load required libXrandr symbols\n");
236 dlclose(pState->pRandLibraryHandle);
237 pState->pRandLibraryHandle = NULL;
238 return VERR_NOT_FOUND;
239 }
240
241 pState->pDisplay = XOpenDisplay(NULL);
242 if (!pState->pDisplay)
243 return VERR_NOT_FOUND;
244 if (!pState->pXRRQueryExtension(pState->pDisplay, &pState->cRREventBase, &status))
245 return VERR_NOT_FOUND;
246 pState->fHaveRandR12 = false;
247 pState->pcszXrandr = "xrandr";
248 if (RTFileExists("/usr/X11/bin/xrandr"))
249 pState->pcszXrandr = "/usr/X11/bin/xrandr";
250 status = system(pState->pcszXrandr);
251 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
252 VBClLogFatalError("Failed to execute the xrandr utility\n");
253 RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
254 status = system(szCommand);
255 if (WEXITSTATUS(status) == 0)
256 pState->fHaveRandR12 = true;
257 return VINF_SUCCESS;
258}
259
260/**
261 * @interface_method_impl{VBCLSERVICE,pfnInit}
262 */
263static DECLCALLBACK(int) init(void)
264{
265 struct DISPLAYSTATE *pSelf = &g_DisplayState;
266 int rc;
267
268 if (pSelf->mfInit)
269 return VERR_WRONG_ORDER;
270 rc = initDisplay(pSelf);
271 if (RT_FAILURE(rc))
272 return rc;
273 if (RT_SUCCESS(rc))
274 pSelf->mfInit = true;
275 return rc;
276}
277
278/**
279 * @interface_method_impl{VBCLSERVICE,pfnWorker}
280 */
281static DECLCALLBACK(int) run(bool volatile *pfShutdown)
282{
283 RT_NOREF(pfShutdown); /** @todo Probably very wrong not to check pfShutdown... Especially given no pfnStop implementation. */
284 struct DISPLAYSTATE *pSelf = &g_DisplayState;
285
286 if (!pSelf->mfInit)
287 return VERR_WRONG_ORDER;
288 runDisplay(pSelf);
289 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
290}
291
292VBCLSERVICE g_SvcDisplayLegacy =
293{
294 "dp-legacy-x11", /* szName */
295 "Legacy display assistant", /* pszDescription */
296 ".vboxclient-display", /* pszPidFilePathTemplate */
297 NULL, /* pszUsage */
298 NULL, /* pszOptions */
299 NULL, /* pfnOption */
300 init, /* pfnInit */
301 run, /* pfnWorker */
302 NULL, /* pfnStop */
303 NULL, /* pfnTerm */
304};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use