VBoxService.cpp revision 5cb0714e82727baaa0ee34404677cb649b736e40
/* $Id$ */
/** @file
* VBoxService - Guest Additions Service Skeleton.
*/
/*
* Copyright (C) 2007-2010 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
/** @todo LOG_GROUP*/
#ifndef _MSC_VER
# include <unistd.h>
#endif
#include <errno.h>
#ifndef RT_OS_WINDOWS
# include <signal.h>
#endif
#include "product-generated.h"
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
#include <VBox/VBoxGuestLib.h>
#include "VBoxServiceInternal.h"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The program name (derived from argv[0]). */
char *g_pszProgName = (char *)"";
/** The current verbosity level. */
int g_cVerbosity = 0;
/** The default service interval (the -i | --interval) option). */
/** Shutdown the main thread. (later, for signals.) */
bool volatile g_fShutdown;
/**
* The details of the services that has been compiled in.
*/
static struct
{
/** Pointer to the service descriptor. */
/** The worker thread. NIL_RTTHREAD if it's the main thread. */
/** Shutdown indicator. */
bool volatile fShutdown;
/** Indicator set by the service thread exiting. */
bool volatile fStopped;
/** Whether the service was started or not. */
bool fStarted;
/** Whether the service is enabled or not. */
bool fEnabled;
} g_aServices[] =
{
#ifdef VBOXSERVICE_CONTROL
{ &g_Control, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_TIMESYNC
{ &g_TimeSync, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_CLIPBOARD
{ &g_Clipboard, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_VMINFO
{ &g_VMInfo, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_EXEC
{ &g_Exec, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_CPUHOTPLUG
{ &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, true },
#endif
#ifdef VBOXSERVICE_MANAGEMENT
#ifdef VBOX_WITH_MEMBALLOON
{ &g_MemBalloon, NIL_RTTHREAD, false, false, false, true },
#endif
{ &g_VMStatistics, NIL_RTTHREAD, false, false, false, true },
#endif
};
/**
* Displays the program usage message.
*
* @returns 1.
*/
static int VBoxServiceUsage(void)
{
RTPrintf("usage: %s [-f|--foreground] [-v|--verbose] [-i|--interval <seconds>]\n"
" [--disable-<service>] [--enable-<service>] [-h|-?|--help]\n", g_pszProgName);
#ifdef RT_OS_WINDOWS
RTPrintf(" [-r|--register] [-u|--unregister]\n");
#endif
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
RTPrintf("\n"
"Options:\n"
" -i | --interval The default interval.\n"
" -f | --foreground Don't daemonzie the program. For debugging.\n"
" -v | --verbose Increment the verbosity level. For debugging.\n"
" -h | -? | --help Show this message and exit with status 1.\n"
);
#ifdef RT_OS_WINDOWS
RTPrintf(" -r | --register Installs the service.\n"
" -u | --unregister Uninstall service.\n");
#endif
RTPrintf("\n"
"Service specific options:\n");
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
RTPrintf(" --enable-%-10s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
RTPrintf(" --disable-%-9s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
}
RTPrintf("\n"
return 1;
}
/**
* Displays a syntax error message.
*
* @returns 1
* @param pszFormat The message text.
* @param ... Format arguments.
*/
int VBoxServiceSyntax(const char *pszFormat, ...)
{
return 1;
}
/**
* Displays an error message.
*
* @returns 1
* @param pszFormat The message text.
* @param ... Format arguments.
*/
int VBoxServiceError(const char *pszFormat, ...)
{
return 1;
}
/**
* Displays a verbose message.
*
* @returns 1
* @param pszFormat The message text.
* @param ... Format arguments.
*/
{
if (iLevel <= g_cVerbosity)
{
}
}
/**
* Gets a 32-bit value argument.
*
* @returns 0 on success, non-zero exit code on error.
* @param argc The argument count.
* @param argv The argument vector
* @param psz Where in *pi to start looking for the value argument.
* @param pi Where to find and perhaps update the argument index.
* @param pu32 Where to store the 32-bit value.
* @param u32Min The minimum value.
* @param u32Max The maximum value.
*/
int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
{
psz++;
if (!*psz)
{
}
char *pszNext;
return VBoxServiceSyntax("The timesync interval of %RU32 secconds is out of range [%RU32..%RU32].\n",
return 0;
}
/**
* The service thread.
*
* @returns Whatever the worker function returns.
* @param ThreadSelf My thread handle.
* @param pvUser The service index.
*/
{
#ifndef RT_OS_WINDOWS
/*
* Block all signals for this thread. Only the main thread will handle signals.
*/
#endif
return rc;
}
unsigned VBoxServiceGetStartedServices(void)
{
unsigned iMain = ~0U;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fEnabled)
{
iMain = j;
break;
}
return iMain; /* Return the index of the main service (must always come last!). */
}
/**
* Starts the service.
*
* @returns VBox status code, errors are fully bitched.
*
* @param iMain The index of the service that belongs to the main
* thread. Pass ~0U if none does.
*/
int VBoxServiceStartServices(unsigned iMain)
{
int rc;
/*
* Initialize the services.
*/
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fEnabled)
{
if (RT_FAILURE(rc))
{
VBoxServiceError("Service '%s' failed to initialize: %Rrc\n",
return rc;
}
}
/*
* Start the service(s).
*/
rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
if ( !g_aServices[j].fEnabled
|| j == iMain)
continue;
if (RT_FAILURE(rc))
{
break;
}
g_aServices[j].fStarted = true;
/* wait for the thread to initialize */
if (g_aServices[j].fShutdown)
{
}
}
if ( RT_SUCCESS(rc)
&& iMain != ~0U)
{
/* The final service runs in the main thread. */
if (rc != VINF_SUCCESS) /* Only complain if service returned an error. Otherwise the service is a one-timer. */
{
VBoxServiceError("Service '%s' stopped unexpected; rc=%Rrc\n", g_aServices[iMain].pDesc->pszName, rc);
}
}
return rc;
}
/**
* Stops and terminates the services.
*
* This should be called even when VBoxServiceStartServices fails so it can
* clean up anything that we succeeded in starting.
*/
int VBoxServiceStopServices(void)
{
int rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fStarted)
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fEnabled)
{
{
for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
{
if (RT_SUCCESS(rc))
break;
#ifdef RT_OS_WINDOWS
/* Notify SCM that it takes a bit longer ... */
#endif
}
if (RT_FAILURE(rc))
}
}
return rc;
}
#ifndef RT_OS_WINDOWS
/*
* Block all important signals, then explicitly wait until one of these signal arrives.
*/
static void VBoxServiceWaitSignal(void)
{
int iSignal;
}
#endif
{
int rc = VINF_SUCCESS;
/*
* Init globals and such.
*/
RTR3Init();
/*
* Connect to the kernel part before daemonizing so we can fail
* and complain if there is some kind of problem. We need to initialize
* the guest lib *before* we do the pre-init just in case one of services
* needs do to some initial stuff with it.
*/
rc = VbglR3Init();
if (RT_FAILURE(rc))
/* Do pre-init of services. */
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
if (RT_FAILURE(rc))
}
#ifdef RT_OS_WINDOWS
/* Make sure only one instance of VBoxService runs at a time. Create a global mutex for that.
Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
if ( hMutexAppRunning != NULL
&& GetLastError() == ERROR_ALREADY_EXISTS)
{
/* Close the mutex for this application instance. */
}
#endif
/*
* Parse the arguments.
*/
bool fDaemonize = true;
bool fDaemonized = false;
for (int i = 1; i < argc; i++)
{
if (*psz != '-')
psz++;
/* translate long argument to short */
if (*psz == '-')
{
psz++;
if (MATCHES("foreground"))
psz = "f";
else if (MATCHES("verbose"))
psz = "v";
else if (MATCHES("help"))
psz = "h";
else if (MATCHES("interval"))
psz = "i";
#ifdef RT_OS_WINDOWS
else if (MATCHES("register"))
psz = "r";
else if (MATCHES("unregister"))
psz = "u";
#endif
else if (MATCHES("daemonized"))
{
fDaemonized = true;
continue;
}
else
{
bool fFound = false;
g_aServices[j].fEnabled = true;
g_aServices[j].fEnabled = false;
if (!fFound)
{
if (fFound)
break;
if (rc != -1)
return rc;
}
if (!fFound)
continue;
}
}
/* handle the string of short options. */
do
{
switch (*psz)
{
case 'i':
if (rc)
return rc;
break;
case 'f':
fDaemonize = false;
break;
case 'v':
g_cVerbosity++;
break;
case 'h':
case '?':
return VBoxServiceUsage();
#ifdef RT_OS_WINDOWS
case 'r':
return VBoxServiceWinInstall();
case 'u':
return VBoxServiceWinUninstall();
#endif
default:
{
bool fFound = false;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
if (fFound)
break;
if (rc != -1)
return rc;
}
if (!fFound)
break;
}
}
}
/*
* Check that at least one service is enabled.
*/
unsigned iMain = VBoxServiceGetStartedServices();
if (iMain == ~0U)
return VBoxServiceSyntax("At least one service must be enabled.\n");
#ifndef RT_OS_WINDOWS
/*
* POSIX: No main service thread.
*/
iMain = ~0U;
#endif
VBoxServiceVerbose(0, "%s r%s started. Verbose level = %d\n",
/*
* Daemonize if requested.
*/
if (fDaemonize && !fDaemonized)
{
#ifdef RT_OS_WINDOWS
/** @todo Should do something like VBoxSVC here, OR automatically re-register
* the service and start it. Involving VbglR3Daemonize isn't an option
* here.
*
* Also, the idea here, IIRC, was to map the sub service to windows
* services. The todo below is for mimicking windows services on
* non-windows systems. Not sure if this is doable or not, but in anycase
* this code can be moved into -win.
*
* You should return when StartServiceCtrlDispatcher, btw., not
* continue.
*/
if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
return VBoxServiceError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!",
/* Service now lives in the control dispatcher registered above. */
#else
if (RT_FAILURE(rc))
/* in-child */
#endif
}
#ifdef RT_OS_WINDOWS
else
{
/* Run the app just like a console one if not daemonized. */
#endif
/*
* Windows: Start the services, enter the main threads' run loop and stop them
* again when it returns.
*
* POSIX: Start all services and return immediately.
*/
#ifndef RT_OS_WINDOWS
if (RT_SUCCESS(rc))
#endif
#ifdef RT_OS_WINDOWS
}
#endif
#ifdef RT_OS_WINDOWS
/*
* Release instance mutex if we got it.
*/
if (hMutexAppRunning != NULL)
{
}
#endif
VBoxServiceVerbose(0, "Ended.\n");
}