VirtualBox

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

Last change on this file since 64670 was 64167, checked in by vboxsync, 8 years ago

bugref:7535: Additions: desktop notification feature for critical errors: when VBoxClient exits with a fatal error and notify-send is not available to notify the user, try xmessage then fall back to printing to the console.

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