VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/main.cpp@ 67954

Last change on this file since 67954 was 67755, checked in by vboxsync, 7 years ago

bugref:8533: Additions/x11: fully support VMSVGA
Revert r116031: Allow VBoxClient services to daemonise without providing a pidfile; and r116032: Use AcquireGuestCaps in the vmsvga VBoxClient service and do not create a pidfile.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.4 KB
Line 
1/* $Id: main.cpp 67755 2017-07-03 13:33:55Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox Guest Service:
5 * Linux guest.
6 */
7
8/*
9 * Copyright (C) 2006-2016 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#include <sys/types.h>
21#include <sys/wait.h>
22#include <stdlib.h> /* For exit */
23#include <stdio.h>
24#include <string.h>
25#include <unistd.h>
26#include <errno.h>
27#include <poll.h>
28#include <signal.h>
29
30#include <X11/Xlib.h>
31#include <X11/Xatom.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/critsect.h>
35#include <iprt/env.h>
36#include <iprt/file.h>
37#include <iprt/initterm.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/param.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/types.h>
44#include <VBox/VBoxGuestLib.h>
45#include <VBox/log.h>
46
47#include "VBoxClient.h"
48
49/*static int (*gpfnOldIOErrorHandler)(Display *) = NULL; - unused */
50
51/** Object representing the service we are running. This has to be global
52 * so that the cleanup routine can access it. */
53struct VBCLSERVICE **g_pService;
54/** The name of our pidfile. It is global for the benefit of the cleanup
55 * routine. */
56static char g_szPidFile[RTPATH_MAX] = "";
57/** The file handle of our pidfile. It is global for the benefit of the
58 * cleanup routine. */
59static RTFILE g_hPidFile;
60/** Global critical section held during the clean-up routine (to prevent it
61 * being called on multiple threads at once) or things which may not happen
62 * during clean-up (e.g. pausing and resuming the service).
63 */
64RTCRITSECT g_critSect;
65/** Counter of how often our deamon has been respawned. */
66unsigned cRespawn = 0;
67
68/** Exit with a fatal error. */
69void vbclFatalError(char *pszMessage)
70{
71 char *pszCommand;
72 int status;
73 if (pszMessage && cRespawn == 0)
74 {
75 pszCommand = RTStrAPrintf2("notify-send \"VBoxClient: %s\"", pszMessage);
76 if (pszCommand)
77 {
78 status = system(pszCommand);
79 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
80 {
81 pszCommand = RTStrAPrintf2("xmessage -buttons OK:0 -center \"VBoxClient: %s\"",
82 pszMessage);
83 if (pszCommand)
84 {
85 status = system(pszCommand);
86 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
87 {
88 RTPrintf("VBoxClient: %s", pszMessage);
89 }
90 }
91 }
92 }
93 }
94 _exit(1);
95}
96
97/** Clean up if we get a signal or something. This is extern so that we
98 * can call it from other compilation units. */
99void VBClCleanUp()
100{
101 /* We never release this, as we end up with a call to exit(3) which is not
102 * async-safe. Unless we fix this application properly, we should be sure
103 * never to exit from anywhere except from this method. */
104 int rc = RTCritSectEnter(&g_critSect);
105 if (RT_FAILURE(rc))
106 VBClFatalError(("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc));
107 if (g_pService)
108 (*g_pService)->cleanup(g_pService);
109 if (g_szPidFile[0] && g_hPidFile)
110 VbglR3ClosePidFile(g_szPidFile, g_hPidFile);
111 exit(0);
112}
113
114/**
115 * A standard signal handler which cleans up and exits.
116 */
117static void vboxClientSignalHandler(int cSignal)
118{
119 LogRel(("VBoxClient: terminated with signal %d\n", cSignal));
120 /** Disable seamless mode */
121 RTPrintf(("VBoxClient: terminating...\n"));
122 VBClCleanUp();
123}
124
125/**
126 * Xlib error handler for certain errors that we can't avoid.
127 */
128static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
129{
130 char errorText[1024];
131
132 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
133 LogRelFlow(("VBoxClient: an X Window protocol error occurred: %s (error code %d). Request code: %d, minor code: %d, serial number: %d\n", errorText, pError->error_code, pError->request_code, pError->minor_code, pError->serial));
134 return 0;
135}
136
137/**
138 * Xlib error handler for fatal errors. This often means that the programme is still running
139 * when X exits.
140 */
141static int vboxClientXLibIOErrorHandler(Display *pDisplay)
142{
143 RT_NOREF1(pDisplay);
144 LogRel(("VBoxClient: a fatal guest X Window error occurred. This may just mean that the Window system was shut down while the client was still running.\n"));
145 VBClCleanUp();
146 return 0; /* We should never reach this. */
147}
148
149/**
150 * Reset all standard termination signals to call our signal handler, which
151 * cleans up and exits.
152 */
153static void vboxClientSetSignalHandlers(void)
154{
155 struct sigaction sigAction;
156
157 LogRelFlowFunc(("\n"));
158 sigAction.sa_handler = vboxClientSignalHandler;
159 sigemptyset(&sigAction.sa_mask);
160 sigAction.sa_flags = 0;
161 sigaction(SIGHUP, &sigAction, NULL);
162 sigaction(SIGINT, &sigAction, NULL);
163 sigaction(SIGQUIT, &sigAction, NULL);
164 sigaction(SIGPIPE, &sigAction, NULL);
165 sigaction(SIGALRM, &sigAction, NULL);
166 sigaction(SIGTERM, &sigAction, NULL);
167 sigaction(SIGUSR1, &sigAction, NULL);
168 sigaction(SIGUSR2, &sigAction, NULL);
169 LogRelFlowFunc(("returning\n"));
170}
171
172/**
173 * Print out a usage message and exit with success.
174 */
175void vboxClientUsage(const char *pcszFileName)
176{
177 RTPrintf("Usage: %s --clipboard|"
178#ifdef VBOX_WITH_DRAG_AND_DROP
179 "--draganddrop|"
180#endif
181 "--display|"
182# ifdef VBOX_WITH_GUEST_PROPS
183 "--checkhostversion|"
184#endif
185 "--seamless|check3d|"
186 "--vmsvga [-d|--nodaemon]\n", pcszFileName);
187 RTPrintf("Starts the VirtualBox DRM/X Window System guest services.\n\n");
188 RTPrintf("Options:\n");
189 RTPrintf(" --clipboard starts the shared clipboard service\n");
190#ifdef VBOX_WITH_DRAG_AND_DROP
191 RTPrintf(" --draganddrop starts the drag and drop service\n");
192#endif
193 RTPrintf(" --display starts the display management service\n");
194#ifdef VBOX_WITH_GUEST_PROPS
195 RTPrintf(" --checkhostversion starts the host version notifier service\n");
196#endif
197 RTPrintf(" --check3d tests whether 3D pass-through is enabled\n");
198 RTPrintf(" --seamless starts the seamless windows service\n");
199 RTPrintf(" --vmsvga starts VMSVGA dynamic resizing for DRM or for X11\n");
200 RTPrintf(" -d, --nodaemon continues running as a system service\n");
201 RTPrintf(" -h, --help shows this help text\n");
202 RTPrintf(" -V, --version shows version information\n");
203 RTPrintf("\n");
204 exit(0);
205}
206
207/**
208 * The main loop for the VBoxClient daemon.
209 * @todo Clean up for readability.
210 */
211int main(int argc, char *argv[])
212{
213 bool fDaemonise = true, fRespawn = true;
214 int rc;
215 const char *pcszFileName;
216
217 /* Initialise our runtime before all else. */
218 rc = RTR3InitExe(argc, &argv, 0);
219 if (RT_FAILURE(rc))
220 return RTMsgInitFailure(rc);
221 /* This should never be called twice in one process - in fact one Display
222 * object should probably never be used from multiple threads anyway. */
223 if (!XInitThreads())
224 VBClFatalError(("Failed to initialize X11 threads\n"));
225 /* Get our file name for error output. */
226 pcszFileName = RTPathFilename(argv[0]);
227 if (!pcszFileName)
228 pcszFileName = "VBoxClient";
229
230 /* Parse our option(s) */
231 /** @todo Use RTGetOpt() if the arguments become more complex. */
232 for (int i = 1; i < argc; ++i)
233 {
234 rc = VERR_INVALID_PARAMETER;
235 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--nodaemon"))
236 {
237 /* If the user is running in "no daemon" mode anyway, send critical
238 * logging to stdout as well. */
239 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
240 if (pReleaseLog)
241 rc = RTLogDestinations(pReleaseLog, "stdout");
242 if (pReleaseLog && RT_FAILURE(rc))
243 RTPrintf("%s: failed to redivert error output, rc=%Rrc\n",
244 pcszFileName, rc);
245 fDaemonise = false;
246 }
247 else if (!strcmp(argv[i], "--no-respawn"))
248 {
249 fRespawn = false;
250 }
251 else if (!strcmp(argv[i], "--clipboard"))
252 {
253 if (g_pService)
254 break;
255 g_pService = VBClGetClipboardService();
256 }
257 else if (!strcmp(argv[i], "--display"))
258 {
259 if (g_pService)
260 break;
261 g_pService = VBClGetDisplayService();
262 }
263 else if (!strcmp(argv[i], "--seamless"))
264 {
265 if (g_pService)
266 break;
267 g_pService = VBClGetSeamlessService();
268 }
269 else if (!strcmp(argv[i], "--checkhostversion"))
270 {
271 if (g_pService)
272 break;
273 g_pService = VBClGetHostVersionService();
274 }
275#ifdef VBOX_WITH_DRAG_AND_DROP
276 else if (!strcmp(argv[i], "--draganddrop"))
277 {
278 if (g_pService)
279 break;
280 g_pService = VBClGetDragAndDropService();
281 }
282#endif /* VBOX_WITH_DRAG_AND_DROP */
283 else if (!strcmp(argv[i], "--check3d"))
284 {
285 if (g_pService)
286 break;
287 g_pService = VBClCheck3DService();
288 }
289 else if (!strcmp(argv[i], "--vmsvga"))
290 {
291 if (g_pService)
292 break;
293 g_pService = VBClDisplaySVGAService();
294 }
295 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
296 {
297 vboxClientUsage(pcszFileName);
298 return RTEXITCODE_SUCCESS;
299 }
300 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
301 {
302 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
303 return RTEXITCODE_SUCCESS;
304 }
305 else
306 {
307 RTPrintf("%s: unrecognized option `%s'\n", pcszFileName, argv[i]);
308 RTPrintf("Try `%s --help' for more information\n", pcszFileName);
309 return RTEXITCODE_SYNTAX;
310 }
311 rc = VINF_SUCCESS;
312 }
313 if (RT_FAILURE(rc) || !g_pService)
314 {
315 vboxClientUsage(pcszFileName);
316 return RTEXITCODE_SYNTAX;
317 }
318
319 rc = RTCritSectInit(&g_critSect);
320 if (RT_FAILURE(rc))
321 VBClFatalError(("Initialising critical section failed: %Rrc\n", rc));
322 if ((*g_pService)->getPidFilePath)
323 {
324 rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
325 if (RT_FAILURE(rc))
326 VBClFatalError(("Getting home directory for PID file failed: %Rrc\n", rc));
327 rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile),
328 (*g_pService)->getPidFilePath());
329 if (RT_FAILURE(rc))
330 VBClFatalError(("Creating PID file path failed: %Rrc\n", rc));
331 if (fDaemonise)
332 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &cRespawn);
333 if (RT_FAILURE(rc))
334 VBClFatalError(("Daemonizing failed: %Rrc\n", rc));
335 if (g_szPidFile[0])
336 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
337 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
338 return RTEXITCODE_SUCCESS;
339 if (RT_FAILURE(rc))
340 VBClFatalError(("Creating PID file failed: %Rrc\n", rc));
341 }
342 /* Set signal handlers to clean up on exit. */
343 vboxClientSetSignalHandlers();
344#ifndef VBOXCLIENT_WITHOUT_X11
345 /* Set an X11 error handler, so that we don't die when we get unavoidable
346 * errors. */
347 XSetErrorHandler(vboxClientXLibErrorHandler);
348 /* Set an X11 I/O error handler, so that we can shutdown properly on
349 * fatal errors. */
350 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
351#endif
352 rc = (*g_pService)->init(g_pService);
353 if (RT_SUCCESS(rc))
354 {
355 rc = (*g_pService)->run(g_pService, fDaemonise);
356 if (RT_FAILURE(rc))
357 LogRel2(("Running service failed: %Rrc\n", rc));
358 }
359 else
360 {
361 /** @todo r=andy Should we return an appropriate exit code if the service failed to init?
362 * Must be tested carefully with our init scripts first. */
363 LogRel2(("Initializing service failed: %Rrc\n", rc));
364 }
365 VBClCleanUp();
366 return RTEXITCODE_SUCCESS;
367}
368
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