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.cpp266
1 files changed, 175 insertions, 91 deletions
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService.cpp b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
index d8880addd..cb7b43efb 100644
--- a/src/VBox/Additions/common/VBoxService/VBoxService.cpp
+++ b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
@@ -1,10 +1,10 @@
-/* $Id: VBoxService.cpp $ */
+/* $Id: VBoxService.cpp 36745 2011-04-20 10:08:26Z vboxsync $ */
/** @file
* VBoxService - Guest Additions Service Skeleton.
*/
/*
- * Copyright (C) 2007-2010 Oracle Corporation
+ * Copyright (C) 2007-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;
@@ -39,13 +39,13 @@
#include <iprt/asm.h>
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
+#include <iprt/message.h>
#include <iprt/path.h>
#include <iprt/semaphore.h>
#include <iprt/string.h>
#include <iprt/stream.h>
#include <iprt/thread.h>
-#include <VBox/VBoxGuestLib.h>
#include <VBox/log.h>
#include "VBoxServiceInternal.h"
@@ -76,6 +76,8 @@ static struct
PCVBOXSERVICE pDesc;
/** The worker thread. NIL_RTTHREAD if it's the main thread. */
RTTHREAD Thread;
+ /** Whether Pre-init was called. */
+ bool fPreInited;
/** Shutdown indicator. */
bool volatile fShutdown;
/** Indicator set by the service thread exiting. */
@@ -87,31 +89,31 @@ static struct
} g_aServices[] =
{
#ifdef VBOXSERVICE_CONTROL
- { &g_Control, NIL_RTTHREAD, false, false, false, true },
+ { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOXSERVICE_TIMESYNC
- { &g_TimeSync, NIL_RTTHREAD, false, false, false, true },
+ { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOXSERVICE_CLIPBOARD
- { &g_Clipboard, NIL_RTTHREAD, false, false, false, true },
+ { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOXSERVICE_VMINFO
- { &g_VMInfo, NIL_RTTHREAD, false, false, false, true },
+ { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOXSERVICE_CPUHOTPLUG
- { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, true },
+ { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOXSERVICE_MANAGEMENT
# ifdef VBOX_WITH_MEMBALLOON
- { &g_MemBalloon, NIL_RTTHREAD, false, false, false, true },
+ { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
# endif
- { &g_VMStatistics, NIL_RTTHREAD, false, false, false, true },
+ { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
#endif
#if defined(VBOX_WITH_PAGE_SHARING) && defined(RT_OS_WINDOWS)
- { &g_PageSharing, NIL_RTTHREAD, false, false, false, true },
+ { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
#endif
#ifdef VBOX_WITH_SHARED_FOLDERS
- { &g_AutoMount, NIL_RTTHREAD, false, false, false, true },
+ { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
#endif
};
@@ -121,7 +123,7 @@ static struct
*
* @returns 1.
*/
-static int VBoxServiceUsage(void)
+static int vboxServiceUsage(void)
{
RTPrintf("Usage:\n"
" %-12s [-f|--foreground] [-v|--verbose] [-i|--interval <seconds>]\n"
@@ -137,6 +139,7 @@ static int VBoxServiceUsage(void)
" -i | --interval The default interval.\n"
" -f | --foreground Don't daemonize the program. For debugging.\n"
" -v | --verbose Increment the verbosity level. For debugging.\n"
+ " -V | --version Show version information.\n"
" -h | -? | --help Show this message and exit with status 1.\n"
);
#ifdef RT_OS_WINDOWS
@@ -228,6 +231,36 @@ void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
/**
+ * Reports the current VBoxService status to the host.
+ *
+ * This makes sure that the Failed state is sticky.
+ *
+ * @return IPRT status code.
+ * @param enmStatus Status to report to the host.
+ */
+int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus)
+{
+ /*
+ * VBoxGuestFacilityStatus_Failed is sticky.
+ */
+ static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
+ VBoxServiceVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
+ if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
+ {
+ int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService,
+ enmStatus, 0 /* Flags */);
+ if (RT_FAILURE(rc))
+ {
+ VBoxServiceError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
+ return rc;
+ }
+ s_enmLastStatus = enmStatus;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
* Gets a 32-bit value argument.
*
* @returns 0 on success, non-zero exit code on error.
@@ -268,7 +301,7 @@ int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32
* @param ThreadSelf My thread handle.
* @param pvUser The service index.
*/
-static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
+static DECLCALLBACK(int) vboxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
{
const unsigned i = (uintptr_t)pvUser;
@@ -289,15 +322,33 @@ static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
/**
- * Check if at least one service should be started.
+ * Lazily calls the pfnPreInit method on each service.
+ *
+ * @returns VBox status code, error message displayed.
*/
-static bool VBoxServiceCheckStartedServices(void)
+static RTEXITCODE vboxServiceLazyPreInit(void)
{
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
- if (g_aServices[j].fEnabled)
- return true;
+ if (!g_aServices[j].fPreInited)
+ {
+ int rc = g_aServices[j].pDesc->pfnPreInit();
+ if (RT_FAILURE(rc))
+ return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
+ g_aServices[j].fPreInited = true;
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
- return false;
+/**
+ * Count the number of enabled services.
+ */
+static unsigned vboxServiceCountEnabledServices(void)
+{
+ unsigned cEnabled = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ cEnabled += g_aServices[i].fEnabled;
+ return cEnabled;
}
@@ -310,6 +361,8 @@ int VBoxServiceStartServices(void)
{
int rc;
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Init);
+
/*
* Initialize the services.
*/
@@ -324,6 +377,7 @@ int VBoxServiceStartServices(void)
{
VBoxServiceError("Service '%s' failed to initialize: %Rrc\n",
g_aServices[j].pDesc->pszName, rc);
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
return rc;
}
g_aServices[j].fEnabled = false;
@@ -344,7 +398,7 @@ int VBoxServiceStartServices(void)
continue;
VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
- rc = RTThreadCreate(&g_aServices[j].Thread, VBoxServiceThread, (void *)(uintptr_t)j, 0,
+ rc = RTThreadCreate(&g_aServices[j].Thread, vboxServiceThread, (void *)(uintptr_t)j, 0,
RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
if (RT_FAILURE(rc))
{
@@ -353,13 +407,12 @@ int VBoxServiceStartServices(void)
}
g_aServices[j].fStarted = true;
- /* Wait for the thread to initialize.
- *
- * @todo There is a race between waiting and checking
- * the fShutdown flag of a thread here and processing
- * the thread's actual worker loop. If the thread decides
- * to exit the loop before we skipped the fShutdown check
- * below the service will fail to start! */
+ /* Wait for the thread to initialize. */
+ /** @todo There is a race between waiting and checking
+ * the fShutdown flag of a thread here and processing
+ * the thread's actual worker loop. If the thread decides
+ * to exit the loop before we skipped the fShutdown check
+ * below the service will fail to start! */
RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
if (g_aServices[j].fShutdown)
{
@@ -371,7 +424,10 @@ int VBoxServiceStartServices(void)
if (RT_SUCCESS(rc))
VBoxServiceVerbose(1, "All services started.\n");
else
+ {
VBoxServiceError("An error occcurred while the services!\n");
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
+ }
return rc;
}
@@ -384,7 +440,7 @@ int VBoxServiceStartServices(void)
*/
int VBoxServiceStopServices(void)
{
- int rc = VINF_SUCCESS;
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminating);
/*
* Signal all the services.
@@ -405,6 +461,7 @@ int VBoxServiceStopServices(void)
/*
* Wait for all the service threads to complete.
*/
+ int rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
@@ -412,18 +469,22 @@ int VBoxServiceStopServices(void)
if (g_aServices[j].Thread != NIL_RTTHREAD)
{
VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
+ int rc2 = VINF_SUCCESS;
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))
+ rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
+ if (RT_SUCCESS(rc2))
break;
#ifdef RT_OS_WINDOWS
/* 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);
+ if (RT_FAILURE(rc2))
+ {
+ VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
+ rc = rc2;
+ }
}
VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
g_aServices[j].pDesc->pfnTerm();
@@ -434,16 +495,17 @@ int VBoxServiceStopServices(void)
* Wake up and tell the main() thread that we're shutting down (it's
* sleeping in VBoxServiceMainWait).
*/
+ ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
if (g_hEvtWindowsService != NIL_RTSEMEVENT)
{
VBoxServiceVerbose(3, "Stopping the main thread...\n");
- ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
- rc = RTSemEventSignal(g_hEvtWindowsService);
- AssertRC(rc);
+ int rc2 = RTSemEventSignal(g_hEvtWindowsService);
+ AssertRC(rc2);
}
#endif
- VBoxServiceVerbose(2, "Stopping services returned: rc=%Rrc\n", rc);
+ VBoxServiceVerbose(2, "Stopping services returning: %Rrc\n", rc);
+ VBoxServiceReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
return rc;
}
@@ -455,13 +517,7 @@ void VBoxServiceMainWait(void)
{
int rc;
- /* Report the host that we're up and running! */
- rc = VbglR3ReportAdditionsStatus(VBoxGuestStatusFacility_VBoxService,
- VBoxGuestStatusCurrent_Active,
- 0 /* Flags */);
- if (RT_FAILURE(rc))
- VBoxServiceError("Could not report facility (%u) status %u, rc=%Rrc\n",
- VBoxGuestStatusFacility_VBoxService, VBoxGuestStatusCurrent_Active, rc);
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Active);
#ifdef RT_OS_WINDOWS
/*
@@ -519,11 +575,11 @@ int main(int argc, char **argv)
/*
* Init globals and such.
*/
- RTR3Init();
-
+ int rc = RTR3Init();
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
g_pszProgName = RTPathFilename(argv[0]);
-
#ifdef VBOXSERVICE_TOOLBOX
/*
* Run toolbox code before all other stuff since these things are simpler
@@ -542,9 +598,14 @@ int main(int argc, char **argv)
* do to some initial stuff with it.
*/
VBoxServiceVerbose(2, "Calling VbgR3Init()\n");
- int rc = VbglR3Init();
+ rc = VbglR3Init();
if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_ACCESS_DENIED)
+ return VBoxServiceError("Insufficient privileges to start %s! Please start with Administrator/root privileges!\n",
+ g_pszProgName);
return VBoxServiceError("VbglR3Init failed with rc=%Rrc.\n", rc);
+ }
#ifdef RT_OS_WINDOWS
/*
@@ -557,40 +618,9 @@ int main(int argc, char **argv)
#endif
/*
- * Do pre-init of services.
- */
- for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
- {
- rc = g_aServices[j].pDesc->pfnPreInit();
- if (RT_FAILURE(rc))
- return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, 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!
- */
- /** @todo r=bird: Use Global\\ prefix or this serves no purpose on terminal servers. */
- HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
- if ( hMutexAppRunning != NULL
- && GetLastError() == ERROR_ALREADY_EXISTS)
- {
- VBoxServiceError("%s is already running! Terminating.", g_pszProgName);
-
- /* Close the mutex for this application instance. */
- CloseHandle(hMutexAppRunning);
- hMutexAppRunning = NULL;
-
- /** @todo r=bird: How does this cause us to terminate? Btw. Why do
- * we do this before parsing parameters? 'VBoxService --help'
- * and 'VBoxService --version' won't work now when the service
- * is running... */
- }
-#endif
-
- /*
* Parse the arguments.
+ *
+ * Note! This code predates RTGetOpt, thus the manual parsing.
*/
bool fDaemonize = true;
bool fDaemonized = false;
@@ -612,6 +642,8 @@ int main(int argc, char **argv)
psz = "f";
else if (MATCHES("verbose"))
psz = "v";
+ else if (MATCHES("version"))
+ psz = "V";
else if (MATCHES("help"))
psz = "h";
else if (MATCHES("interval"))
@@ -642,6 +674,11 @@ int main(int argc, char **argv)
g_aServices[j].fEnabled = false;
if (!fFound)
+ {
+ rcExit = vboxServiceLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
{
rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
@@ -651,6 +688,7 @@ int main(int argc, char **argv)
if (rc != -1)
return rc;
}
+ }
if (!fFound)
return VBoxServiceSyntax("Unknown option '%s'\n", argv[i]);
continue;
@@ -679,9 +717,13 @@ int main(int argc, char **argv)
g_cVerbosity++;
break;
+ case 'V':
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return RTEXITCODE_SUCCESS;
+
case 'h':
case '?':
- return VBoxServiceUsage();
+ return vboxServiceUsage();
#ifdef RT_OS_WINDOWS
case 'r':
@@ -693,11 +735,15 @@ int main(int argc, char **argv)
default:
{
+ rcExit = vboxServiceLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
bool fFound = false;
for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
{
rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
- fFound = rc == 0;
+ fFound = rc == VINF_SUCCESS;
if (fFound)
break;
if (rc != -1)
@@ -710,11 +756,52 @@ int main(int argc, char **argv)
}
} while (psz && *++psz);
}
+
+ /* Check that at least one service is enabled. */
+ if (vboxServiceCountEnabledServices() == 0)
+ return VBoxServiceSyntax("At least one service must be enabled.\n");
+
+ /* Call pre-init if we didn't do it already. */
+ rcExit = vboxServiceLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+#ifdef RT_OS_WINDOWS
/*
- * Check that at least one service is enabled.
+ * Make sure only one instance of VBoxService runs at a time. Create a
+ * global mutex for that.
+ *
+ * Note! The \\Global\ namespace was introduced with Win2K, thus the
+ * version check.
+ * Note! If the mutex exists CreateMutex will open it and set last error to
+ * ERROR_ALREADY_EXISTS.
*/
- if (!VBoxServiceCheckStartedServices())
- return VBoxServiceSyntax("At least one service must be enabled.\n");
+ OSVERSIONINFOEX OSInfoEx;
+ RT_ZERO(OSInfoEx);
+ OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ SetLastError(NO_ERROR);
+ HANDLE hMutexAppRunning;
+ if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
+ && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */)
+ hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME);
+ else
+ hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
+ if (hMutexAppRunning == NULL)
+ {
+ VBoxServiceError("CreateMutex failed with last error %u! Terminating", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ VBoxServiceError("%s is already running! Terminating.", g_pszProgName);
+ CloseHandle(hMutexAppRunning);
+ return RTEXITCODE_FAILURE;
+ }
+#else /* !RT_OS_WINDOWS */
+ /** @todo Add PID file creation here? */
+#endif /* !RT_OS_WINDOWS */
VBoxServiceVerbose(0, "%s r%s started. Verbose level = %d\n",
RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
@@ -753,16 +840,13 @@ int main(int argc, char **argv)
VBoxServiceMainWait();
VBoxServiceStopServices();
}
+ VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminated);
#ifdef RT_OS_WINDOWS
/*
- * Release instance mutex if we got it.
+ * Cleanup mutex.
*/
- if (hMutexAppRunning != NULL)
- {
- ::CloseHandle(hMutexAppRunning);
- hMutexAppRunning = NULL;
- }
+ CloseHandle(hMutexAppRunning);
#endif
VBoxServiceVerbose(0, "Ended.\n");