summaryrefslogtreecommitdiff
path: root/src/VBox/Additions/common/VBoxService/VBoxService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxService/VBoxService.cpp')
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService.cpp203
1 files changed, 99 insertions, 104 deletions
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService.cpp b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
index cf696b2c8..4953d2736 100644
--- a/src/VBox/Additions/common/VBoxService/VBoxService.cpp
+++ b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
@@ -1,4 +1,4 @@
-/* $Id: VBoxService.cpp 29647 2010-05-18 15:59:51Z vboxsync $ */
+/* $Id: VBoxService.cpp 29817 2010-05-26 13:54:41Z vboxsync $ */
/** @file
* VBoxService - Guest Additions Service Skeleton.
*/
@@ -34,6 +34,7 @@
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
#include <iprt/path.h>
+#include <iprt/semaphore.h>
#include <iprt/string.h>
#include <iprt/stream.h>
#include <iprt/thread.h>
@@ -53,8 +54,12 @@ char *g_pszProgName = (char *)"";
int g_cVerbosity = 0;
/** The default service interval (the -i | --interval) option). */
uint32_t g_DefaultInterval = 0;
-/** Shutdown the main thread. (later, for signals.) */
-bool volatile g_fShutdown;
+#ifdef RT_OS_WINDOWS
+/** Signal shutdown to the Windows service thread. */
+static bool volatile g_fWindowsServiceShutdown;
+/** Event the Windows service thread waits for shutdown. */
+static RTSEMEVENT g_hEvtWindowsService;
+#endif
/**
* The details of the services that has been compiled in.
@@ -149,11 +154,11 @@ static int VBoxServiceUsage(void)
/**
* Displays a syntax error message.
*
- * @returns 1
+ * @returns RTEXITCODE_SYNTAX.
* @param pszFormat The message text.
* @param ... Format arguments.
*/
-int VBoxServiceSyntax(const char *pszFormat, ...)
+RTEXITCODE VBoxServiceSyntax(const char *pszFormat, ...)
{
RTStrmPrintf(g_pStdErr, "%s: syntax error: ", g_pszProgName);
@@ -162,18 +167,18 @@ int VBoxServiceSyntax(const char *pszFormat, ...)
RTStrmPrintfV(g_pStdErr, pszFormat, va);
va_end(va);
- return 1;
+ return RTEXITCODE_SYNTAX;
}
/**
* Displays an error message.
*
- * @returns 1
+ * @returns RTEXITCODE_FAILURE.
* @param pszFormat The message text.
* @param ... Format arguments.
*/
-int VBoxServiceError(const char *pszFormat, ...)
+RTEXITCODE VBoxServiceError(const char *pszFormat, ...)
{
RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
@@ -186,7 +191,7 @@ int VBoxServiceError(const char *pszFormat, ...)
LogRel(("%s: Error: %N", g_pszProgName, pszFormat, &va));
va_end(va);
- return 1;
+ return RTEXITCODE_FAILURE;
}
@@ -275,17 +280,16 @@ static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
}
-unsigned VBoxServiceGetStartedServices(void)
+/**
+ * Check if at least one service should be started.
+ */
+static bool VBoxServiceCheckStartedServices(void)
{
- unsigned iMain = ~0U;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fEnabled)
- {
- iMain = j;
- break;
- }
+ return true;
- return iMain; /* Return the index of the main service (must always come last!). */
+ return false;
}
@@ -293,11 +297,8 @@ unsigned VBoxServiceGetStartedServices(void)
* 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 VBoxServiceStartServices(void)
{
int rc;
@@ -331,8 +332,7 @@ int VBoxServiceStartServices(unsigned iMain)
rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
- if ( !g_aServices[j].fEnabled
- || j == iMain)
+ if (!g_aServices[j].fEnabled)
continue;
VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
@@ -353,18 +353,7 @@ int VBoxServiceStartServices(unsigned iMain)
rc = VERR_GENERAL_FAILURE;
}
}
- if ( RT_SUCCESS(rc)
- && iMain != ~0U)
- {
- /* The final service runs in the main thread. */
- VBoxServiceVerbose(1, "Starting '%s' in the main thread\n", g_aServices[iMain].pDesc->pszName);
- rc = g_aServices[iMain].pDesc->pfnWorker(&g_fShutdown);
- if (RT_SUCCESS(rc))
- VBoxServiceVerbose(1, "Main service '%s' successfully stopped.\n", g_aServices[iMain].pDesc->pszName);
- else /* 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);
- g_aServices[iMain].pDesc->pfnTerm();
- }
+
return rc;
}
@@ -378,58 +367,61 @@ int VBoxServiceStartServices(unsigned iMain)
int VBoxServiceStopServices(void)
{
int rc = VINF_SUCCESS;
- unsigned iMain = VBoxServiceGetStartedServices();
+ /*
+ * Signal all the services.
+ */
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
- ASMAtomicXchgBool(&g_aServices[j].fShutdown, true);
+ ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
+
+ /*
+ * Do the pfnStop callback on all running services.
+ */
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
if (g_aServices[j].fStarted)
{
VBoxServiceVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
g_aServices[j].pDesc->pfnStop();
}
- for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
- if ( !g_aServices[j].fEnabled /* Only stop services which were started before. */
- || j == iMain) /* Don't call the termination function for main service yet. */
- {
+ /*
+ * Wait for all the service threads to complete.
+ */
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
continue;
- }
- else
+ if (g_aServices[j].Thread != NIL_RTTHREAD)
{
- if (g_aServices[j].Thread != NIL_RTTHREAD)
+ VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
+ for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
{
- VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
- for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
- {
- rc = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
- if (RT_SUCCESS(rc))
- break;
+ rc = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
+ if (RT_SUCCESS(rc))
+ break;
#ifdef RT_OS_WINDOWS
- /* Notify SCM that it takes a bit longer ... */
- VBoxServiceWinSetStatus(SERVICE_STOP_PENDING, i);
+ /* Notify SCM that it takes a bit longer ... */
+ VBoxServiceWinSetStopPendingStatus(i + j*32);
#endif
- }
- if (RT_FAILURE(rc))
- VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc);
}
- VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
- g_aServices[j].pDesc->pfnTerm();
+ if (RT_FAILURE(rc))
+ VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc);
}
+ VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
+ g_aServices[j].pDesc->pfnTerm();
+ }
#ifdef RT_OS_WINDOWS
/*
- * As we're now done terminating all service threads,
- * we have to stop the main thread as well (if defined). Note that the termination
- * function will be called in a later context (when the main thread returns from the worker
- * function).
+ * Wake up and tell the main() thread that we're shutting down (it's
+ * sleeping in VBoxServiceMainWait).
*/
- if (iMain != ~0U)
+ if (g_hEvtWindowsService != NIL_RTSEMEVENT)
{
- VBoxServiceVerbose(3, "Stopping main service '%s' (%d) ...\n", g_aServices[iMain].pDesc->pszName, iMain);
-
- ASMAtomicXchgBool(&g_fShutdown, true);
- g_aServices[iMain].pDesc->pfnStop();
+ VBoxServiceVerbose(3, "Stopping the main thread...\n");
+ ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
+ rc = RTSemEventSignal(g_hEvtWindowsService);
+ AssertRC(rc);
}
#endif
@@ -438,14 +430,37 @@ int VBoxServiceStopServices(void)
}
-#ifndef RT_OS_WINDOWS
-/*
- * Block all important signals, then explicitly wait until one of these signal arrives.
+/**
+ * Block the main thread until the service shuts down.
*/
-static void VBoxServiceWaitSignal(void)
+void VBoxServiceMainWait(void)
{
+ int rc;
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Wait for the semaphore to be signalled.
+ */
+ VBoxServiceVerbose(1, "Waiting in main thread\n");
+ rc = RTSemEventCreate(&g_hEvtWindowsService);
+ AssertRC(rc);
+ while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown))
+ {
+ rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+ RTSemEventDestroy(g_hEvtWindowsService);
+ g_hEvtWindowsService = NIL_RTSEMEVENT;
+
+#else
+ /*
+ * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking
+ * all important signals.
+ *
+ * The annoying EINTR/ERESTART loop is for the benefit of Solaris where
+ * sigwait returns when we receive a SIGCHLD. Kind of makes sense since
+ */
sigset_t signalMask;
- int iSignal;
sigemptyset(&signalMask);
sigaddset(&signalMask, SIGHUP);
sigaddset(&signalMask, SIGINT);
@@ -454,18 +469,21 @@ static void VBoxServiceWaitSignal(void)
sigaddset(&signalMask, SIGTERM);
pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
- int rc;
+ int iSignal;
do
{
iSignal = -1;
rc = sigwait(&signalMask, &iSignal);
}
while ( rc == EINTR
- || rc == ERESTART);
+# ifdef ERESTART
+ || rc == ERESTART
+# endif
+ );
- VBoxServiceVerbose(3, "VBoxServiceWaitSignal: Received signal %d (rc=%d)\n", iSignal, rc);
-}
+ VBoxServiceVerbose(3, "VBoxServiceMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
#endif /* !RT_OS_WINDOWS */
+}
int main(int argc, char **argv)
@@ -635,43 +653,21 @@ int main(int argc, char **argv)
/*
* Check that at least one service is enabled.
*/
- unsigned iMain = VBoxServiceGetStartedServices();
- if (iMain == ~0U)
+ if (!VBoxServiceCheckStartedServices())
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",
RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
/*
* Daemonize if requested.
*/
+ RTEXITCODE rcExit;
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.
- */
VBoxServiceVerbose(2, "Starting service dispatcher ...\n");
- if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
- return VBoxServiceError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!",
- GetLastError(), g_pszProgName);
- /* Service now lives in the control dispatcher registered above. */
+ rcExit = VBoxServiceWinEnterCtrlDispatcher();
#else
VBoxServiceVerbose(1, "Daemonizing...\n");
rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */);
@@ -692,11 +688,10 @@ int main(int argc, char **argv)
* POSIX: This is used for both daemons and console runs. Start all services
* and return immediately.
*/
- rc = VBoxServiceStartServices(iMain);
-#ifndef RT_OS_WINDOWS
+ rc = VBoxServiceStartServices();
+ rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
if (RT_SUCCESS(rc))
- VBoxServiceWaitSignal();
-#endif
+ VBoxServiceMainWait();
VBoxServiceStopServices();
}
@@ -712,6 +707,6 @@ int main(int argc, char **argv)
#endif
VBoxServiceVerbose(0, "Ended.\n");
- return RT_SUCCESS(rc) ? 0 : 1;
+ return rcExit;
}