main.cpp revision b7b63052fd06828f62814f0777c48dc5ba44bb34
/** @file
*
* VirtualBox Guest Service:
* Linux guest.
*/
/*
* Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <stdlib.h> /* For exit */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <iprt/critsect.h>
#include <iprt/initterm.h>
#include <VBox/VBoxGuestLib.h>
#include "VBoxClient.h"
/** Object representing the service we are running. This has to be global
* so that the cleanup routine can access it. */
struct VBCLSERVICE **g_pService;
/** The name of our pidfile. It is global for the benefit of the cleanup
* routine. */
static char g_szPidFile[RTPATH_MAX];
/** The file handle of our pidfile. It is global for the benefit of the
* cleanup routine. */
static RTFILE g_hPidFile;
/** Global critical section held during the clean-up routine (to prevent it
* being called on multiple threads at once) or things which may not happen
* during clean-up (e.g. pausing and resuming the service).
*/
/** Counter of how often our deamon has been respawned. */
unsigned cRespawn = 0;
/** Exit with a fatal error. */
void vbclFatalError(char *pszMessage)
{
char *pszCommand;
if (pszMessage && cRespawn == 0)
{
if (pszCommand)
}
_exit(1);
}
/** Clean up if we get a signal or something. This is extern so that we
* can call it from other compilation units. */
void VBClCleanUp()
{
/* We never release this, as we end up with a call to exit(3) which is not
* async-safe. Unless we fix this application properly, we should be sure
* never to exit from anywhere except from this method. */
if (RT_FAILURE(rc))
if (g_pService)
if (g_szPidFile[0] && g_hPidFile)
exit(0);
}
/**
* A standard signal handler which cleans up and exits.
*/
static void vboxClientSignalHandler(int cSignal)
{
/** Disable seamless mode */
RTPrintf(("VBoxClient: terminating...\n"));
VBClCleanUp();
}
/**
* Xlib error handler for certain errors that we can't avoid.
*/
{
char errorText[1024];
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));
return 0; /* We should never reach this. */
}
/**
* Xlib error handler for fatal errors. This often means that the programme is still running
* when X exits.
*/
{
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"));
VBClCleanUp();
return 0; /* We should never reach this. */
}
/**
* Reset all standard termination signals to call our signal handler, which
* cleans up and exits.
*/
static void vboxClientSetSignalHandlers(void)
{
LogRelFlowFunc(("\n"));
LogRelFlowFunc(("returning\n"));
}
/** Check whether X.Org has acquired or lost the current virtual terminal and
* call the service @a pause() or @a resume() call-back if appropriate.
* The functionality is provided by the vboxvideo driver for pre-1.16 X servers
* and by 1.16 and later series servers.
* This can either be called directly from a service's event loop or the service
* can call VBClStartVTMonitor() to start an event loop in a separate thread.
* Property notification for the root window should be selected first. Services
* are not required to check VT changes if they do not need the information.
* @param pEvent an event received on a display connection which will be
* checked to see if it is change to the XFree86_has_VT property
*/
{
int actualFormat;
bool fHasVT = false;
unsigned long *pValue;
int rc;
return;
{
}
else
return;
if (fHasVT)
{
if (RT_FAILURE(rc))
VBClFatalError(("Error resuming the service: %Rrc\n"));
}
if (!fHasVT)
{
if (RT_FAILURE(rc))
VBClFatalError(("Error pausing the service: %Rrc\n"));
}
}
/**
* Thread which notifies the service when we switch to a different VT or back
* and cleans up when the X server exits.
* @note runs until programme exit.
*/
{
bool fHasVT = true;
if (!pDisplay)
VBClFatalError(("Failed to open the X11 display\n"));
while (true)
{
}
return VINF_SUCCESS; /* Should never be reached. */
}
/**
* Start a thread which notifies the service when we switch to a different
* VT or back, and terminates us when the X server exits. This should be called
* by most services which do not regularly run an X11 event loop.
*/
int VBClStartVTMonitor()
{
RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR");
}
/**
* Print out a usage message and exit with success.
*/
void vboxClientUsage(const char *pcszFileName)
{
RTPrintf("Usage: %s --clipboard|"
#ifdef VBOX_WITH_DRAG_AND_DROP
"--draganddrop|"
#endif
"--display|"
# ifdef VBOX_WITH_GUEST_PROPS
"--checkhostversion|"
#endif
"--seamless [-d|--nodaemon]\n", pcszFileName);
RTPrintf("Start the VirtualBox X Window System guest services.\n\n");
RTPrintf("Options:\n");
RTPrintf(" --clipboard start the shared clipboard service\n");
#ifdef VBOX_WITH_DRAG_AND_DROP
RTPrintf(" --draganddrop start the drag and drop service\n");
#endif
RTPrintf(" --display start the display management service\n");
#ifdef VBOX_WITH_GUEST_PROPS
RTPrintf(" --checkhostversion start the host version notifier service\n");
#endif
RTPrintf(" --seamless start the seamless windows service\n");
RTPrintf(" -d, --nodaemon continue running as a system service\n");
RTPrintf("\n");
exit(0);
}
/**
* The main loop for the VBoxClient daemon.
* @todo Clean up for readability.
*/
{
bool fDaemonise = true, fRespawn = true;
int rc;
const char *pcszFileName, *pcszStage;
/* Initialise our runtime before all else. */
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/* This should never be called twice in one process - in fact one Display
* object should probably never be used from multiple threads anyway. */
if (!XInitThreads())
VBClFatalError(("Failed to initialize X11 threads\n"));
/* Get our file name for error output. */
if (!pcszFileName)
pcszFileName = "VBoxClient";
/* Parse our option(s) */
/** @todo Use RTGetOpt() if the arguments become more complex. */
for (int i = 1; i < argc; ++i)
{
{
/* If the user is running in "no daemon" mode anyway, send critical
* logging to stdout as well. */
if (pReleaseLog)
RTPrintf("%s: failed to redivert error output, rc=%Rrc\n",
pcszFileName, rc);
fDaemonise = false;
}
{
fRespawn = false;
}
{
if (g_pService)
break;
}
{
if (g_pService)
break;
}
{
if (g_pService)
break;
}
{
if (g_pService)
break;
}
#ifdef VBOX_WITH_DRAG_AND_DROP
{
if (g_pService)
break;
}
#endif /* VBOX_WITH_DRAG_AND_DROP */
{
return 0;
}
else
{
return 1;
}
rc = VINF_SUCCESS;
}
{
return 1;
}
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
(*g_pService)->getPidFilePath());
if (RT_FAILURE(rc))
if (fDaemonise)
if (RT_FAILURE(rc))
if (g_szPidFile[0])
return 0;
if (RT_FAILURE(rc))
/* Set signal handlers to clean up on exit. */
#ifndef VBOXCLIENT_WITHOUT_X11
/* Set an X11 error handler, so that we don't die when we get unavoidable
* errors. */
/* Set an X11 I/O error handler, so that we can shutdown properly on
* fatal errors. */
#endif
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
VBClCleanUp();
return 0;
}