VirtualBox

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

Last change on this file since 61176 was 60980, checked in by vboxsync, 9 years ago

Additions/VBoxClient: Only run service if initialization was successful, use proper RTEXITODE exit codes.

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