VirtualBox

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

Last change on this file since 86117 was 84538, checked in by vboxsync, 5 years ago

bugref:9637

  • Renaming vmsvga-x11 service to vmsvga service
  • Replacing display-svga.cpp with display-wayland.cpp
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.2 KB
Line 
1/* $Id: main.cpp 84538 2020-05-26 13:25:05Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions - X11 Client.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <sys/wait.h>
23#include <stdlib.h> /* For exit */
24#include <signal.h>
25#include <X11/Xlib.h>
26#include "product-generated.h"
27#include <iprt/buildconfig.h>
28#include <iprt/critsect.h>
29#include <iprt/getopt.h>
30#include <iprt/initterm.h>
31#include <iprt/message.h>
32#include <iprt/path.h>
33#include <iprt/stream.h>
34#include <VBox/VBoxGuestLib.h>
35#include <VBox/err.h>
36#include "VBoxClient.h"
37
38
39/*********************************************************************************************************************************
40* Defines *
41*********************************************************************************************************************************/
42#define VBOXCLIENT_OPT_NORESPAWN 950
43
44#define VBOXCLIENT_OPT_SERVICES 980
45#define VBOXCLIENT_OPT_CHECKHOSTVERSION VBOXCLIENT_OPT_SERVICES
46#define VBOXCLIENT_OPT_CLIPBOARD VBOXCLIENT_OPT_SERVICES + 1
47#define VBOXCLIENT_OPT_DRAGANDDROP VBOXCLIENT_OPT_SERVICES + 2
48#define VBOXCLIENT_OPT_SEAMLESS VBOXCLIENT_OPT_SERVICES + 3
49#define VBOXCLIENT_OPT_VMSVGA VBOXCLIENT_OPT_SERVICES + 4
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55/*static int (*gpfnOldIOErrorHandler)(Display *) = NULL; - unused */
56
57/** Object representing the service we are running. This has to be global
58 * so that the cleanup routine can access it. */
59struct VBCLSERVICE **g_pService;
60/** The name of our pidfile. It is global for the benefit of the cleanup
61 * routine. */
62static char g_szPidFile[RTPATH_MAX] = "";
63/** The file handle of our pidfile. It is global for the benefit of the
64 * cleanup routine. */
65static RTFILE g_hPidFile;
66/** Global critical section held during the clean-up routine (to prevent it
67 * being called on multiple threads at once) or things which may not happen
68 * during clean-up (e.g. pausing and resuming the service).
69 */
70static RTCRITSECT g_critSect;
71/** Counter of how often our daemon has been respawned. */
72unsigned g_cRespawn = 0;
73/** Logging verbosity level. */
74unsigned g_cVerbosity = 0;
75static char g_szLogFile[RTPATH_MAX + 128] = "";
76
77
78/**
79 * Clean up if we get a signal or something.
80 *
81 * This is extern so that we can call it from other compilation units.
82 */
83void VBClCleanUp(bool fExit /*=true*/)
84{
85 /* We never release this, as we end up with a call to exit(3) which is not
86 * async-safe. Unless we fix this application properly, we should be sure
87 * never to exit from anywhere except from this method. */
88 int rc = RTCritSectEnter(&g_critSect);
89 if (RT_FAILURE(rc))
90 VBClLogFatalError("Failure while acquiring the global critical section, rc=%Rrc\n", rc);
91 if (g_pService)
92 (*g_pService)->cleanup(g_pService);
93 if (g_szPidFile[0] && g_hPidFile)
94 VbglR3ClosePidFile(g_szPidFile, g_hPidFile);
95
96 VBClLogDestroy();
97
98 if (fExit)
99 exit(RTEXITCODE_SUCCESS);
100}
101
102/**
103 * A standard signal handler which cleans up and exits.
104 */
105static void vboxClientSignalHandler(int cSignal)
106{
107 VBClLogInfo("Terminated with signal %d\n", cSignal);
108 /** Disable seamless mode */
109 VBClLogInfo("Terminating ...\n");
110 VBClCleanUp();
111}
112
113/**
114 * Xlib error handler for certain errors that we can't avoid.
115 */
116static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
117{
118 char errorText[1024];
119
120 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
121 VBClLogError("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);
122 return 0;
123}
124
125/**
126 * Xlib error handler for fatal errors. This often means that the programme is still running
127 * when X exits.
128 */
129static int vboxClientXLibIOErrorHandler(Display *pDisplay)
130{
131 RT_NOREF1(pDisplay);
132 VBClLogError("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");
133 VBClCleanUp();
134 return 0; /* We should never reach this. */
135}
136
137/**
138 * Reset all standard termination signals to call our signal handler, which
139 * cleans up and exits.
140 */
141static void vboxClientSetSignalHandlers(void)
142{
143 struct sigaction sigAction;
144
145 LogRelFlowFuncEnter();
146 sigAction.sa_handler = vboxClientSignalHandler;
147 sigemptyset(&sigAction.sa_mask);
148 sigAction.sa_flags = 0;
149 sigaction(SIGHUP, &sigAction, NULL);
150 sigaction(SIGINT, &sigAction, NULL);
151 sigaction(SIGQUIT, &sigAction, NULL);
152 sigaction(SIGPIPE, &sigAction, NULL);
153 sigaction(SIGALRM, &sigAction, NULL);
154 sigaction(SIGTERM, &sigAction, NULL);
155 sigaction(SIGUSR1, &sigAction, NULL);
156 sigaction(SIGUSR2, &sigAction, NULL);
157 LogRelFlowFuncLeave();
158}
159
160/**
161 * Print out a usage message and exit with success.
162 */
163static void vboxClientUsage(const char *pcszFileName)
164{
165 RTPrintf("Usage: %s "
166#ifdef VBOX_WITH_SHARED_CLIPBOARD
167 "--clipboard|"
168#endif
169#ifdef VBOX_WITH_DRAG_AND_DROP
170 "--draganddrop|"
171#endif
172# ifdef VBOX_WITH_GUEST_PROPS
173 "--checkhostversion|"
174#endif
175 "--seamless|"
176 "--vmsvga"
177 "[-d|--nodaemon]\n", pcszFileName);
178 RTPrintf("Starts the VirtualBox DRM/X Window System guest services.\n\n");
179 RTPrintf("Options:\n");
180#ifdef VBOX_WITH_SHARED_CLIPBOARD
181 RTPrintf(" --clipboard starts the shared clipboard service\n");
182#endif
183#ifdef VBOX_WITH_DRAG_AND_DROP
184 RTPrintf(" --draganddrop starts the drag and drop service\n");
185#endif
186#ifdef VBOX_WITH_GUEST_PROPS
187 RTPrintf(" --checkhostversion starts the host version notifier service\n");
188#endif
189 RTPrintf(" --seamless starts the seamless windows service\n");
190 RTPrintf(" --vmsvga starts VMSVGA dynamic resizing for x11/Wayland guests\n");
191 RTPrintf(" -f, --foreground run in the foreground (no daemonizing)\n");
192 RTPrintf(" -d, --nodaemon continues running as a system service\n");
193 RTPrintf(" -h, --help shows this help text\n");
194 RTPrintf(" -v, --verbose increases logging verbosity level\n");
195 RTPrintf(" -V, --version shows version information\n");
196 RTPrintf("\n");
197}
198
199/**
200 * Complains about seeing more than one service specification.
201 *
202 * @returns RTEXITCODE_SYNTAX.
203 */
204static int vbclSyntaxOnlyOneService(void)
205{
206 RTMsgError("More than one service specified! Only one, please.");
207 return RTEXITCODE_SYNTAX;
208}
209
210/**
211 * The main loop for the VBoxClient daemon.
212 * @todo Clean up for readability.
213 */
214int main(int argc, char *argv[])
215{
216 /* Initialise our runtime before all else. */
217 int rc = RTR3InitExe(argc, &argv, 0);
218 if (RT_FAILURE(rc))
219 return RTMsgInitFailure(rc);
220
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 VBClLogFatalError("Failed to initialize X11 threads\n");
225
226 /* Get our file name for usage info and hints. */
227 const char *pcszFileName = RTPathFilename(argv[0]);
228 if (!pcszFileName)
229 pcszFileName = "VBoxClient";
230
231 /* Parse our option(s). */
232 static const RTGETOPTDEF s_aOptions[] =
233 {
234 { "--nodaemon", 'd', RTGETOPT_REQ_NOTHING },
235 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
236 { "--help", 'h', RTGETOPT_REQ_NOTHING },
237 { "--logfile", 'l', RTGETOPT_REQ_STRING },
238 { "--no-respawn", VBOXCLIENT_OPT_NORESPAWN, RTGETOPT_REQ_NOTHING },
239 { "--version", 'V', RTGETOPT_REQ_NOTHING },
240 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
241
242 /* Services */
243 { "--checkhostversion", VBOXCLIENT_OPT_CHECKHOSTVERSION, RTGETOPT_REQ_NOTHING },
244#ifdef VBOX_WITH_SHARED_CLIPBOARD
245 { "--clipboard", VBOXCLIENT_OPT_CLIPBOARD, RTGETOPT_REQ_NOTHING },
246#endif
247#ifdef VBOX_WITH_DRAG_AND_DROP
248 { "--draganddrop", VBOXCLIENT_OPT_DRAGANDDROP, RTGETOPT_REQ_NOTHING },
249#endif
250 { "--seamless", VBOXCLIENT_OPT_SEAMLESS, RTGETOPT_REQ_NOTHING },
251 { "--vmsvga", VBOXCLIENT_OPT_VMSVGA, RTGETOPT_REQ_NOTHING },
252 };
253
254 int ch;
255 RTGETOPTUNION ValueUnion;
256 RTGETOPTSTATE GetState;
257 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
258 AssertRC(rc);
259
260 bool fDaemonise = true;
261 bool fRespawn = true;
262
263 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
264 {
265 /* For options that require an argument, ValueUnion has received the value. */
266 switch (ch)
267 {
268 case 'd':
269 {
270 fDaemonise = false;
271 break;
272 }
273
274 case 'h':
275 {
276 vboxClientUsage(pcszFileName);
277 return RTEXITCODE_SUCCESS;
278 }
279
280 case 'f':
281 {
282 fDaemonise = false;
283 fRespawn = false;
284 break;
285 }
286
287 case 'l':
288 {
289 rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
290 if (RT_FAILURE(rc))
291 VBClLogFatalError("Unable to create log file path, rc=%Rrc\n", rc);
292 break;
293 }
294
295 case 'n':
296 {
297 fRespawn = false;
298 break;
299 }
300
301 case 'v':
302 {
303 g_cVerbosity++;
304 break;
305 }
306
307 case 'V':
308 {
309 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
310 return RTEXITCODE_SUCCESS;
311 }
312
313 /* Services */
314
315 case VBOXCLIENT_OPT_CHECKHOSTVERSION:
316 {
317 if (g_pService)
318 return vbclSyntaxOnlyOneService();
319 g_pService = VBClGetHostVersionService();
320 break;
321 }
322
323#ifdef VBOX_WITH_SHARED_CLIPBOARD
324 case VBOXCLIENT_OPT_CLIPBOARD:
325 {
326 if (g_pService)
327 return vbclSyntaxOnlyOneService();
328 g_pService = VBClGetClipboardService();
329 break;
330 }
331#endif
332#ifdef VBOX_WITH_DRAG_AND_DROP
333 case VBOXCLIENT_OPT_DRAGANDDROP:
334 {
335 if (g_pService)
336 return vbclSyntaxOnlyOneService();
337 g_pService = VBClGetDragAndDropService();
338 break;
339 }
340#endif
341 case VBOXCLIENT_OPT_SEAMLESS:
342 {
343 if (g_pService)
344 return vbclSyntaxOnlyOneService();
345 g_pService = VBClGetSeamlessService();
346 break;
347 }
348
349 case VBOXCLIENT_OPT_VMSVGA:
350 {
351 if (g_pService)
352 return vbclSyntaxOnlyOneService();
353 g_pService = VBClDisplaySVGAX11Service();
354 break;
355 }
356
357 case VERR_GETOPT_UNKNOWN_OPTION:
358 {
359 RTMsgError("unrecognized option '%s'", ValueUnion.psz);
360 RTMsgInfo("Try '%s --help' for more information", pcszFileName);
361 return RTEXITCODE_SYNTAX;
362 }
363
364 case VINF_GETOPT_NOT_OPTION:
365 default:
366 break;
367
368 } /* switch */
369 } /* while RTGetOpt */
370
371 if (!g_pService)
372 {
373 RTMsgError("No service specified. Quitting because nothing to do!");
374 return RTEXITCODE_SYNTAX;
375 }
376
377 /* Initialize VbglR3 before we do anything else with the logger. */
378 rc = VbglR3InitUser();
379 if (RT_FAILURE(rc))
380 VBClLogFatalError("VbglR3InitUser failed: %Rrc", rc);
381
382 rc = VBClLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
383 if (RT_FAILURE(rc))
384 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log '%s', rc=%Rrc\n",
385 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
386
387 LogRel(("Service: %s\n", (*g_pService)->getName()));
388
389 if (!fDaemonise)
390 {
391 /* If the user is running in "no daemon" mode, send critical logging to stdout as well. */
392 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
393 if (pReleaseLog)
394 {
395 rc = RTLogDestinations(pReleaseLog, "stdout");
396 if (RT_FAILURE(rc))
397 return RTMsgErrorExitFailure("Failed to redivert error output, rc=%Rrc", rc);
398 }
399 }
400
401 rc = RTCritSectInit(&g_critSect);
402 if (RT_FAILURE(rc))
403 VBClLogFatalError("Initialising critical section failed: %Rrc\n", rc);
404 if ((*g_pService)->getPidFilePath)
405 {
406 rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
407 if (RT_FAILURE(rc))
408 VBClLogFatalError("Getting home directory for PID file failed: %Rrc\n", rc);
409 rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile),
410 (*g_pService)->getPidFilePath());
411 if (RT_FAILURE(rc))
412 VBClLogFatalError("Creating PID file path failed: %Rrc\n", rc);
413 if (fDaemonise)
414 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn);
415 if (RT_FAILURE(rc))
416 VBClLogFatalError("Daemonizing failed: %Rrc\n", rc);
417 if (g_szPidFile[0])
418 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
419 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
420 return RTEXITCODE_SUCCESS;
421 if (RT_FAILURE(rc))
422 VBClLogFatalError("Creating PID file failed: %Rrc\n", rc);
423 }
424 /* Set signal handlers to clean up on exit. */
425 vboxClientSetSignalHandlers();
426#ifndef VBOXCLIENT_WITHOUT_X11
427 /* Set an X11 error handler, so that we don't die when we get unavoidable
428 * errors. */
429 XSetErrorHandler(vboxClientXLibErrorHandler);
430 /* Set an X11 I/O error handler, so that we can shutdown properly on
431 * fatal errors. */
432 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
433#endif
434 rc = (*g_pService)->init(g_pService);
435 if (RT_SUCCESS(rc))
436 {
437 rc = (*g_pService)->run(g_pService, fDaemonise);
438 if (RT_FAILURE(rc))
439 VBClLogError("Running service failed: %Rrc\n", rc);
440 }
441 else
442 {
443 /** @todo r=andy Should we return an appropriate exit code if the service failed to init?
444 * Must be tested carefully with our init scripts first. */
445 VBClLogError("Initializing service failed: %Rrc\n", rc);
446 }
447 VBClCleanUp(false /*fExit*/);
448 return RTEXITCODE_SUCCESS;
449}
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