VirtualBox

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

Last change on this file since 63100 was 62883, checked in by vboxsync, 9 years ago

Additions/*: gcc warnings.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette