VirtualBox

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

Last change on this file since 54647 was 54185, checked in by vboxsync, 10 years ago

Additions/x11/VBoxClient: drop the time-out on warning messages (it was wrong anyway).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.3 KB
Line 
1/** @file
2 *
3 * VirtualBox Guest Service:
4 * Linux guest.
5 */
6
7/*
8 * Copyright (C) 2006-2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <sys/types.h>
20#include <stdlib.h> /* For exit */
21#include <stdio.h>
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25#include <poll.h>
26#include <signal.h>
27
28#include <X11/Xlib.h>
29#include <X11/Xatom.h>
30
31#include <iprt/critsect.h>
32#include <iprt/env.h>
33#include <iprt/file.h>
34#include <iprt/initterm.h>
35#include <iprt/message.h>
36#include <iprt/path.h>
37#include <iprt/param.h>
38#include <iprt/stream.h>
39#include <iprt/string.h>
40#include <iprt/types.h>
41#include <VBox/VBoxGuestLib.h>
42#include <VBox/log.h>
43
44#include "VBoxClient.h"
45
46static int (*gpfnOldIOErrorHandler)(Display *) = NULL;
47
48/** Object representing the service we are running. This has to be global
49 * so that the cleanup routine can access it. */
50struct VBCLSERVICE **g_pService;
51/** The name of our pidfile. It is global for the benefit of the cleanup
52 * routine. */
53static char g_szPidFile[RTPATH_MAX];
54/** The file handle of our pidfile. It is global for the benefit of the
55 * cleanup routine. */
56static RTFILE g_hPidFile;
57/** Global critical section held during the clean-up routine (to prevent it
58 * being called on multiple threads at once) or things which may not happen
59 * during clean-up (e.g. pausing and resuming the service).
60 */
61RTCRITSECT g_critSect;
62/** Counter of how often our deamon has been respawned. */
63unsigned cRespawn = 0;
64
65/** Exit with a fatal error. */
66void vbclFatalError(char *pszMessage)
67{
68 char *pszCommand;
69 if (pszMessage && cRespawn == 0)
70 {
71 pszCommand = RTStrAPrintf2("notify-send \"VBoxClient: %s\"", pszMessage);
72 if (pszCommand)
73 system(pszCommand);
74 }
75 _exit(1);
76}
77
78/** Clean up if we get a signal or something. This is extern so that we
79 * can call it from other compilation units. */
80void VBClCleanUp()
81{
82 /* We never release this, as we end up with a call to exit(3) which is not
83 * async-safe. Unless we fix this application properly, we should be sure
84 * never to exit from anywhere except from this method. */
85 int rc = RTCritSectEnter(&g_critSect);
86 if (RT_FAILURE(rc))
87 VBClFatalError(("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc));
88 if (g_pService)
89 (*g_pService)->cleanup(g_pService);
90 if (g_szPidFile[0] && g_hPidFile)
91 VbglR3ClosePidFile(g_szPidFile, g_hPidFile);
92 exit(0);
93}
94
95/**
96 * A standard signal handler which cleans up and exits.
97 */
98static void vboxClientSignalHandler(int cSignal)
99{
100 LogRel(("VBoxClient: terminated with signal %d\n", cSignal));
101 /** Disable seamless mode */
102 RTPrintf(("VBoxClient: terminating...\n"));
103 VBClCleanUp();
104}
105
106/**
107 * Xlib error handler for certain errors that we can't avoid.
108 */
109static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
110{
111 char errorText[1024];
112
113 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
114 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));
115 return 0; /* We should never reach this. */
116}
117
118/**
119 * Xlib error handler for fatal errors. This often means that the programme is still running
120 * when X exits.
121 */
122static int vboxClientXLibIOErrorHandler(Display *pDisplay)
123{
124 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"));
125 VBClCleanUp();
126 return 0; /* We should never reach this. */
127}
128
129/**
130 * Reset all standard termination signals to call our signal handler, which
131 * cleans up and exits.
132 */
133static void vboxClientSetSignalHandlers(void)
134{
135 struct sigaction sigAction;
136
137 LogRelFlowFunc(("\n"));
138 sigAction.sa_handler = vboxClientSignalHandler;
139 sigemptyset(&sigAction.sa_mask);
140 sigAction.sa_flags = 0;
141 sigaction(SIGHUP, &sigAction, NULL);
142 sigaction(SIGINT, &sigAction, NULL);
143 sigaction(SIGQUIT, &sigAction, NULL);
144 sigaction(SIGPIPE, &sigAction, NULL);
145 sigaction(SIGALRM, &sigAction, NULL);
146 sigaction(SIGTERM, &sigAction, NULL);
147 sigaction(SIGUSR1, &sigAction, NULL);
148 sigaction(SIGUSR2, &sigAction, NULL);
149 LogRelFlowFunc(("returning\n"));
150}
151
152/** Check whether X.Org has acquired or lost the current virtual terminal and
153 * call the service @a pause() or @a resume() call-back if appropriate.
154 * The functionality is provided by the vboxvideo driver for pre-1.16 X servers
155 * and by 1.16 and later series servers.
156 * This can either be called directly from a service's event loop or the service
157 * can call VBClStartVTMonitor() to start an event loop in a separate thread.
158 * Property notification for the root window should be selected first. Services
159 * are not required to check VT changes if they do not need the information.
160 * @param pEvent an event received on a display connection which will be
161 * checked to see if it is change to the XFree86_has_VT property
162 */
163void VBClCheckXOrgVT(union _XEvent *pEvent)
164{
165 Atom actualType;
166 int actualFormat;
167 unsigned long cItems, cbLeft;
168 bool fHasVT = false;
169 unsigned long *pValue;
170 int rc;
171 Display *pDisplay = pEvent->xany.display;
172 Atom hasVT = XInternAtom(pDisplay, "XFree86_has_VT", False);
173
174 if ( pEvent->type != PropertyNotify
175 || pEvent->xproperty.window != DefaultRootWindow(pDisplay)
176 || pEvent->xproperty.atom != hasVT)
177 return;
178 XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay), hasVT, 0, 1,
179 False, XA_INTEGER, &actualType, &actualFormat, &cItems,
180 &cbLeft, (unsigned char **)&pValue);
181 if (cItems && actualFormat == 32)
182 {
183 fHasVT = *pValue != 0;
184 XFree(pValue);
185 }
186 else
187 return;
188 if (fHasVT)
189 {
190 rc = (*g_pService)->resume(g_pService);
191 if (RT_FAILURE(rc))
192 VBClFatalError(("Error resuming the service: %Rrc\n"));
193 }
194 if (!fHasVT)
195 {
196 rc = (*g_pService)->pause(g_pService);
197 if (RT_FAILURE(rc))
198 VBClFatalError(("Error pausing the service: %Rrc\n"));
199 }
200}
201
202/**
203 * Thread which notifies the service when we switch to a different VT or back
204 * and cleans up when the X server exits.
205 * @note runs until programme exit.
206 */
207static int pfnMonitorThread(RTTHREAD self, void *pvUser)
208{
209 Display *pDisplay;
210 bool fHasVT = true;
211
212 pDisplay = XOpenDisplay(NULL);
213 if (!pDisplay)
214 VBClFatalError(("Failed to open the X11 display\n"));
215 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask);
216 while (true)
217 {
218 XEvent event;
219
220 XNextEvent(pDisplay, &event);
221 VBClCheckXOrgVT(&event);
222 }
223 return VINF_SUCCESS; /* Should never be reached. */
224}
225
226/**
227 * Start a thread which notifies the service when we switch to a different
228 * VT or back, and terminates us when the X server exits. This should be called
229 * by most services which do not regularly run an X11 event loop.
230 */
231int VBClStartVTMonitor()
232{
233 return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0,
234 RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR");
235}
236
237/**
238 * Print out a usage message and exit with success.
239 */
240void vboxClientUsage(const char *pcszFileName)
241{
242 RTPrintf("Usage: %s --clipboard|"
243#ifdef VBOX_WITH_DRAG_AND_DROP
244 "--draganddrop|"
245#endif
246 "--display|"
247# ifdef VBOX_WITH_GUEST_PROPS
248 "--checkhostversion|"
249#endif
250 "--seamless [-d|--nodaemon]\n", pcszFileName);
251 RTPrintf("Start the VirtualBox X Window System guest services.\n\n");
252 RTPrintf("Options:\n");
253 RTPrintf(" --clipboard start the shared clipboard service\n");
254#ifdef VBOX_WITH_DRAG_AND_DROP
255 RTPrintf(" --draganddrop start the drag and drop service\n");
256#endif
257 RTPrintf(" --display start the display management service\n");
258#ifdef VBOX_WITH_GUEST_PROPS
259 RTPrintf(" --checkhostversion start the host version notifier service\n");
260#endif
261 RTPrintf(" --seamless start the seamless windows service\n");
262 RTPrintf(" -d, --nodaemon continue running as a system service\n");
263 RTPrintf("\n");
264 exit(0);
265}
266
267/**
268 * The main loop for the VBoxClient daemon.
269 * @todo Clean up for readability.
270 */
271int main(int argc, char *argv[])
272{
273 bool fDaemonise = true, fRespawn = true;
274 int rc;
275 const char *pcszFileName, *pcszStage;
276
277 /* Initialise our runtime before all else. */
278 rc = RTR3InitExe(argc, &argv, 0);
279 if (RT_FAILURE(rc))
280 return RTMsgInitFailure(rc);
281 /* This should never be called twice in one process - in fact one Display
282 * object should probably never be used from multiple threads anyway. */
283 if (!XInitThreads())
284 VBClFatalError(("Failed to initialize X11 threads\n"));
285 /* Get our file name for error output. */
286 pcszFileName = RTPathFilename(argv[0]);
287 if (!pcszFileName)
288 pcszFileName = "VBoxClient";
289
290 /* Parse our option(s) */
291 /** @todo Use RTGetOpt() if the arguments become more complex. */
292 for (int i = 1; i < argc; ++i)
293 {
294 rc = VERR_INVALID_PARAMETER;
295 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--nodaemon"))
296 {
297 /* If the user is running in "no daemon" mode anyway, send critical
298 * logging to stdout as well. */
299 PRTLOGGER pReleaseLog = RTLogRelDefaultInstance();
300
301 if (pReleaseLog)
302 rc = RTLogDestinations(pReleaseLog, "stdout");
303 if (pReleaseLog && RT_FAILURE(rc))
304 RTPrintf("%s: failed to redivert error output, rc=%Rrc\n",
305 pcszFileName, rc);
306 fDaemonise = false;
307 }
308 else if (!strcmp(argv[i], "--no-respawn"))
309 {
310 fRespawn = false;
311 }
312 else if (!strcmp(argv[i], "--clipboard"))
313 {
314 if (g_pService)
315 break;
316 g_pService = VBClGetClipboardService();
317 }
318 else if (!strcmp(argv[i], "--display"))
319 {
320 if (g_pService)
321 break;
322 g_pService = VBClGetDisplayService();
323 }
324 else if (!strcmp(argv[i], "--seamless"))
325 {
326 if (g_pService)
327 break;
328 g_pService = VBClGetSeamlessService();
329 }
330 else if (!strcmp(argv[i], "--checkhostversion"))
331 {
332 if (g_pService)
333 break;
334 g_pService = VBClGetHostVersionService();
335 }
336#ifdef VBOX_WITH_DRAG_AND_DROP
337 else if (!strcmp(argv[i], "--draganddrop"))
338 {
339 if (g_pService)
340 break;
341 g_pService = VBClGetDragAndDropService();
342 }
343#endif /* VBOX_WITH_DRAG_AND_DROP */
344 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
345 {
346 vboxClientUsage(pcszFileName);
347 return 0;
348 }
349 else
350 {
351 RTPrintf("%s: unrecognized option `%s'\n", pcszFileName, argv[i]);
352 RTPrintf("Try `%s --help' for more information\n", pcszFileName);
353 return 1;
354 }
355 rc = VINF_SUCCESS;
356 }
357 if (RT_FAILURE(rc) || !g_pService)
358 {
359 vboxClientUsage(pcszFileName);
360 return 1;
361 }
362
363 rc = RTCritSectInit(&g_critSect);
364 if (RT_FAILURE(rc))
365 VBClFatalError(("Initialising critical section: %Rrc\n", rc));
366 rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
367 if (RT_FAILURE(rc))
368 VBClFatalError(("Getting home directory for pid-file: %Rrc\n", rc));
369 rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile),
370 (*g_pService)->getPidFilePath());
371 if (RT_FAILURE(rc))
372 VBClFatalError(("Creating pid-file path: %Rrc\n", rc));
373 if (fDaemonise)
374 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &cRespawn);
375 if (RT_FAILURE(rc))
376 VBClFatalError(("Daemonizing: %Rrc\n", rc));
377 if (g_szPidFile[0])
378 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
379 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
380 return 0;
381 if (RT_FAILURE(rc))
382 VBClFatalError(("Creating pid-file: %Rrc\n", rc));
383 /* Set signal handlers to clean up on exit. */
384 vboxClientSetSignalHandlers();
385#ifndef VBOXCLIENT_WITHOUT_X11
386 /* Set an X11 error handler, so that we don't die when we get unavoidable
387 * errors. */
388 XSetErrorHandler(vboxClientXLibErrorHandler);
389 /* Set an X11 I/O error handler, so that we can shutdown properly on
390 * fatal errors. */
391 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
392#endif
393 rc = (*g_pService)->init(g_pService);
394 if (RT_FAILURE(rc))
395 VBClFatalError(("Initialising service: %Rrc\n", rc));
396 rc = (*g_pService)->run(g_pService, fDaemonise);
397 if (RT_FAILURE(rc))
398 VBClFatalError(("Service main loop failed: %Rrc\n", rc));
399 VBClCleanUp();
400 return 0;
401}
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