VirtualBox

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

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

iprt/log.h,++: Added extended logger instance getters that also checks whether the given logger and group-flags are enabled, making the LogRel* checks more efficient in avoid uncessary RTLogLoggerEx parameter building and calls. Ditto for debug logging. The LOG_INSTANCE and LOG_REL_INSTANCE tricks are gone for now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.6 KB
Line 
1/* $Id: main.cpp 55980 2015-05-20 17:35:22Z 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; /* We should never reach this. */
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/** Check whether X.Org has acquired or lost the current virtual terminal and
155 * call the service @a pause() or @a resume() call-back if appropriate.
156 * The functionality is provided by the vboxvideo driver for pre-1.16 X servers
157 * and by 1.16 and later series servers.
158 * This can either be called directly from a service's event loop or the service
159 * can call VBClStartVTMonitor() to start an event loop in a separate thread.
160 * Property notification for the root window should be selected first. Services
161 * are not required to check VT changes if they do not need the information.
162 * @param pEvent an event received on a display connection which will be
163 * checked to see if it is change to the XFree86_has_VT property
164 */
165void VBClCheckXOrgVT(union _XEvent *pEvent)
166{
167 Atom actualType;
168 int actualFormat;
169 unsigned long cItems, cbLeft;
170 bool fHasVT = false;
171 unsigned long *pValue;
172 int rc;
173 Display *pDisplay = pEvent->xany.display;
174 Atom hasVT = XInternAtom(pDisplay, "XFree86_has_VT", False);
175
176 if ( pEvent->type != PropertyNotify
177 || pEvent->xproperty.window != DefaultRootWindow(pDisplay)
178 || pEvent->xproperty.atom != hasVT)
179 return;
180 XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay), hasVT, 0, 1,
181 False, XA_INTEGER, &actualType, &actualFormat, &cItems,
182 &cbLeft, (unsigned char **)&pValue);
183 if (cItems && actualFormat == 32)
184 {
185 fHasVT = *pValue != 0;
186 XFree(pValue);
187 }
188 else
189 return;
190 if (fHasVT)
191 {
192 rc = (*g_pService)->resume(g_pService);
193 if (RT_FAILURE(rc))
194 VBClFatalError(("Error resuming the service: %Rrc\n"));
195 }
196 if (!fHasVT)
197 {
198 rc = (*g_pService)->pause(g_pService);
199 if (RT_FAILURE(rc))
200 VBClFatalError(("Error pausing the service: %Rrc\n"));
201 }
202}
203
204/**
205 * Thread which notifies the service when we switch to a different VT or back
206 * and cleans up when the X server exits.
207 * @note runs until programme exit.
208 */
209static int pfnMonitorThread(RTTHREAD self, void *pvUser)
210{
211 Display *pDisplay;
212 bool fHasVT = true;
213
214 pDisplay = XOpenDisplay(NULL);
215 if (!pDisplay)
216 VBClFatalError(("Failed to open the X11 display\n"));
217 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask);
218 while (true)
219 {
220 XEvent event;
221
222 XNextEvent(pDisplay, &event);
223 VBClCheckXOrgVT(&event);
224 }
225 return VINF_SUCCESS; /* Should never be reached. */
226}
227
228/**
229 * Start a thread which notifies the service when we switch to a different
230 * VT or back, and terminates us when the X server exits. This should be called
231 * by most services which do not regularly run an X11 event loop.
232 */
233int VBClStartVTMonitor()
234{
235 return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0,
236 RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR");
237}
238
239/**
240 * Print out a usage message and exit with success.
241 */
242void vboxClientUsage(const char *pcszFileName)
243{
244 RTPrintf("Usage: %s --clipboard|"
245#ifdef VBOX_WITH_DRAG_AND_DROP
246 "--draganddrop|"
247#endif
248 "--display|"
249# ifdef VBOX_WITH_GUEST_PROPS
250 "--checkhostversion|"
251#endif
252 "--seamless [-d|--nodaemon]\n", pcszFileName);
253 RTPrintf("Starts the VirtualBox X Window System guest services.\n\n");
254 RTPrintf("Options:\n");
255 RTPrintf(" --clipboard starts the shared clipboard service\n");
256#ifdef VBOX_WITH_DRAG_AND_DROP
257 RTPrintf(" --draganddrop starts the drag and drop service\n");
258#endif
259 RTPrintf(" --display starts the display management service\n");
260#ifdef VBOX_WITH_GUEST_PROPS
261 RTPrintf(" --checkhostversion starts the host version notifier service\n");
262#endif
263 RTPrintf(" --seamless starts the seamless windows service\n");
264 RTPrintf(" -d, --nodaemon continues running as a system service\n");
265 RTPrintf(" -h, --help shows this help text\n");
266 RTPrintf(" -V, --version shows version information\n");
267 RTPrintf("\n");
268 exit(0);
269}
270
271/**
272 * The main loop for the VBoxClient daemon.
273 * @todo Clean up for readability.
274 */
275int main(int argc, char *argv[])
276{
277 bool fDaemonise = true, fRespawn = true;
278 int rc;
279 const char *pcszFileName, *pcszStage;
280
281 /* Initialise our runtime before all else. */
282 rc = RTR3InitExe(argc, &argv, 0);
283 if (RT_FAILURE(rc))
284 return RTMsgInitFailure(rc);
285 /* This should never be called twice in one process - in fact one Display
286 * object should probably never be used from multiple threads anyway. */
287 if (!XInitThreads())
288 VBClFatalError(("Failed to initialize X11 threads\n"));
289 /* Get our file name for error output. */
290 pcszFileName = RTPathFilename(argv[0]);
291 if (!pcszFileName)
292 pcszFileName = "VBoxClient";
293
294 /* Parse our option(s) */
295 /** @todo Use RTGetOpt() if the arguments become more complex. */
296 for (int i = 1; i < argc; ++i)
297 {
298 rc = VERR_INVALID_PARAMETER;
299 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--nodaemon"))
300 {
301 /* If the user is running in "no daemon" mode anyway, send critical
302 * logging to stdout as well. */
303 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
304 if (pReleaseLog)
305 rc = RTLogDestinations(pReleaseLog, "stdout");
306 if (pReleaseLog && RT_FAILURE(rc))
307 RTPrintf("%s: failed to redivert error output, rc=%Rrc\n",
308 pcszFileName, rc);
309 fDaemonise = false;
310 }
311 else if (!strcmp(argv[i], "--no-respawn"))
312 {
313 fRespawn = false;
314 }
315 else if (!strcmp(argv[i], "--clipboard"))
316 {
317 if (g_pService)
318 break;
319 g_pService = VBClGetClipboardService();
320 }
321 else if (!strcmp(argv[i], "--display"))
322 {
323 if (g_pService)
324 break;
325 g_pService = VBClGetDisplayService();
326 }
327 else if (!strcmp(argv[i], "--seamless"))
328 {
329 if (g_pService)
330 break;
331 g_pService = VBClGetSeamlessService();
332 }
333 else if (!strcmp(argv[i], "--checkhostversion"))
334 {
335 if (g_pService)
336 break;
337 g_pService = VBClGetHostVersionService();
338 }
339#ifdef VBOX_WITH_DRAG_AND_DROP
340 else if (!strcmp(argv[i], "--draganddrop"))
341 {
342 if (g_pService)
343 break;
344 g_pService = VBClGetDragAndDropService();
345 }
346#endif /* VBOX_WITH_DRAG_AND_DROP */
347 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
348 {
349 vboxClientUsage(pcszFileName);
350 return 0;
351 }
352 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
353 {
354 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
355 return 0;
356 }
357 else
358 {
359 RTPrintf("%s: unrecognized option `%s'\n", pcszFileName, argv[i]);
360 RTPrintf("Try `%s --help' for more information\n", pcszFileName);
361 return 1;
362 }
363 rc = VINF_SUCCESS;
364 }
365 if (RT_FAILURE(rc) || !g_pService)
366 {
367 vboxClientUsage(pcszFileName);
368 return 1;
369 }
370
371 rc = RTCritSectInit(&g_critSect);
372 if (RT_FAILURE(rc))
373 VBClFatalError(("Initialising critical section: %Rrc\n", rc));
374 rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
375 if (RT_FAILURE(rc))
376 VBClFatalError(("Getting home directory for pid-file: %Rrc\n", rc));
377 rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile),
378 (*g_pService)->getPidFilePath());
379 if (RT_FAILURE(rc))
380 VBClFatalError(("Creating pid-file path: %Rrc\n", rc));
381 if (fDaemonise)
382 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &cRespawn);
383 if (RT_FAILURE(rc))
384 VBClFatalError(("Daemonizing: %Rrc\n", rc));
385 if (g_szPidFile[0])
386 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
387 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
388 return 0;
389 if (RT_FAILURE(rc))
390 VBClFatalError(("Creating pid-file: %Rrc\n", rc));
391 /* Set signal handlers to clean up on exit. */
392 vboxClientSetSignalHandlers();
393#ifndef VBOXCLIENT_WITHOUT_X11
394 /* Set an X11 error handler, so that we don't die when we get unavoidable
395 * errors. */
396 XSetErrorHandler(vboxClientXLibErrorHandler);
397 /* Set an X11 I/O error handler, so that we can shutdown properly on
398 * fatal errors. */
399 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
400#endif
401 rc = (*g_pService)->init(g_pService);
402 if (RT_FAILURE(rc))
403 VBClFatalError(("Initialising service: %Rrc\n", rc));
404 rc = (*g_pService)->run(g_pService, fDaemonise);
405 if (RT_FAILURE(rc))
406 VBClFatalError(("Service main loop failed: %Rrc\n", rc));
407 VBClCleanUp();
408 return 0;
409}
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