diff options
Diffstat (limited to 'src/VBox/Frontends/VBoxAutostart')
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/Makefile.kmk | 22 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostart-posix.cpp (renamed from src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp) | 237 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp | 998 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h | 195 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp | 73 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp | 10 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp | 188 | ||||
| -rw-r--r-- | src/VBox/Frontends/VBoxAutostart/VBoxAutostartUtils.cpp | 294 |
8 files changed, 1898 insertions, 119 deletions
diff --git a/src/VBox/Frontends/VBoxAutostart/Makefile.kmk b/src/VBox/Frontends/VBoxAutostart/Makefile.kmk index f6e967a2..432da4d0 100644 --- a/src/VBox/Frontends/VBoxAutostart/Makefile.kmk +++ b/src/VBox/Frontends/VBoxAutostart/Makefile.kmk @@ -1,10 +1,10 @@ # $Id: Makefile.kmk $ ## @file -# VBoxBalloonCtrl - Memory balloon control. +# VBoxAutostart - VM autostart service. # # -# Copyright (C) 2011-2012 Oracle Corporation +# Copyright (C) 2012 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; @@ -18,12 +18,24 @@ SUB_DEPTH = ../../../.. include $(KBUILD_PATH)/subheader.kmk -PROGRAMS += VBoxAutostart +ifeq ($(KBUILD_TARGET),win) + PROGRAMS += VBoxAutostartSvc + VBoxAutostartSvc_TEMPLATE = VBOXMAINCLIENTEXE + VBoxAutostartSvc_SOURCES = \ + VBoxAutostartCfg.cpp \ + VBoxAutostartStart.cpp \ + VBoxAutostartStop.cpp \ + VBoxAutostartUtils.cpp \ + VBoxAutostart-win.cpp +else + PROGRAMS += VBoxAutostart VBoxAutostart_TEMPLATE = VBOXMAINCLIENTEXE VBoxAutostart_SOURCES = \ - VBoxAutostart.cpp \ VBoxAutostartCfg.cpp \ VBoxAutostartStart.cpp \ - VBoxAutostartStop.cpp + VBoxAutostartStop.cpp \ + VBoxAutostartUtils.cpp \ + VBoxAutostart-posix.cpp +endif include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-posix.cpp index dc57ad6d..75cfaba7 100644 --- a/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-posix.cpp @@ -1,10 +1,10 @@ -/* $Id: VBoxAutostart.cpp $ */ +/* $Id: VBoxAutostart-posix.cpp $ */ /** @file * VBoxAutostart - VirtualBox Autostart service. */ /* - * Copyright (C) 2012 Oracle Corporation + * Copyright (C) 2012-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,7 +26,7 @@ #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> +#include <VBox/com/NativeEventQueue.h> #include <VBox/com/listeners.h> #include <VBox/com/VirtualBox.h> @@ -98,18 +98,159 @@ static const RTGETOPTDEF g_aOptions[] = { { "--quiet", 'Q', RTGETOPT_REQ_NOTHING } }; +/** Set by the signal handler. */ +static volatile bool g_fCanceled = false; -DECLHIDDEN(void) serviceLog(const char *pszFormat, ...) + +/** + * Signal handler that sets g_fCanceled. + * + * This can be executed on any thread in the process, on Windows it may even be + * a thread dedicated to delivering this signal. Do not doing anything + * unnecessary here. + */ +static void showProgressSignalHandler(int iSignal) +{ + NOREF(iSignal); + ASMAtomicWriteBool(&g_fCanceled, true); +} + +/** + * Print out progress on the console. + * + * This runs the main event queue every now and then to prevent piling up + * unhandled things (which doesn't cause real problems, just makes things + * react a little slower than in the ideal case). + */ +DECLHIDDEN(HRESULT) showProgress(ComPtr<IProgress> progress) +{ + using namespace com; + + BOOL fCompleted = FALSE; + ULONG ulCurrentPercent = 0; + ULONG ulLastPercent = 0; + + ULONG ulLastOperationPercent = (ULONG)-1; + + ULONG ulLastOperation = (ULONG)-1; + Bstr bstrOperationDescription; + + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + + ULONG cOperations = 1; + HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc); + RTStrmFlush(g_pStdErr); + return hrc; + } + + /* + * Note: Outputting the progress info to stderr (g_pStdErr) is intentional + * to not get intermixed with other (raw) stdout data which might get + * written in the meanwhile. + */ + RTStrmPrintf(g_pStdErr, "0%%..."); + RTStrmFlush(g_pStdErr); + + /* setup signal handling if cancelable */ + bool fCanceledAlready = false; + BOOL fCancelable; + hrc = progress->COMGETTER(Cancelable)(&fCancelable); + if (FAILED(hrc)) + fCancelable = FALSE; + if (fCancelable) + { + signal(SIGINT, showProgressSignalHandler); +#ifdef SIGBREAK + signal(SIGBREAK, showProgressSignalHandler); +#endif + } + + hrc = progress->COMGETTER(Completed(&fCompleted)); + while (SUCCEEDED(hrc)) + { + progress->COMGETTER(Percent(&ulCurrentPercent)); + + /* did we cross a 10% mark? */ + if (ulCurrentPercent / 10 > ulLastPercent / 10) + { + /* make sure to also print out missed steps */ + for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10) + { + if (curVal < 100) + { + RTStrmPrintf(g_pStdErr, "%u%%...", curVal); + RTStrmFlush(g_pStdErr); + } + } + ulLastPercent = (ulCurrentPercent / 10) * 10; + } + + if (fCompleted) + break; + + /* process async cancelation */ + if (g_fCanceled && !fCanceledAlready) + { + hrc = progress->Cancel(); + if (SUCCEEDED(hrc)) + fCanceledAlready = true; + else + g_fCanceled = false; + } + + /* make sure the loop is not too tight */ + progress->WaitForCompletion(100); + + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + hrc = progress->COMGETTER(Completed(&fCompleted)); + } + + /* undo signal handling */ + if (fCancelable) + { + signal(SIGINT, SIG_DFL); +#ifdef SIGBREAK + signal(SIGBREAK, SIG_DFL); +#endif + } + + /* complete the line. */ + LONG iRc = E_FAIL; + hrc = progress->COMGETTER(ResultCode)(&iRc); + if (SUCCEEDED(hrc)) + { + if (SUCCEEDED(iRc)) + RTStrmPrintf(g_pStdErr, "100%%\n"); + else if (g_fCanceled) + RTStrmPrintf(g_pStdErr, "CANCELED\n"); + else + { + RTStrmPrintf(g_pStdErr, "\n"); + RTStrmPrintf(g_pStdErr, "Progress state: %Rhrc\n", iRc); + } + hrc = iRc; + } + else + { + RTStrmPrintf(g_pStdErr, "\n"); + RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc); + } + RTStrmFlush(g_pStdErr); + return hrc; +} + +DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType) { va_list args; - va_start(args, pszFormat); - char *psz = NULL; - RTStrAPrintfV(&psz, pszFormat, args); - va_end(args); - LogRel(("%s", psz)); + if ( enmLogType == AUTOSTARTLOGTYPE_VERBOSE + && !g_fVerbose) + return; - RTStrFree(psz); + LogRel(("%s", pszMsg)); } static void displayHeader() @@ -193,42 +334,6 @@ static void displayHelp(const char *pszImage) RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n"); } -/** - * Creates all global COM objects. - * - * @return HRESULT - */ -static int autostartSetup() -{ - serviceLogVerbose(("Setting up ...\n")); - - /* - * Setup VirtualBox + session interfaces. - */ - HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam()); - if (SUCCEEDED(rc)) - { - rc = g_pSession.createInprocObject(CLSID_Session); - if (FAILED(rc)) - RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc); - } - else - RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc); - - if (FAILED(rc)) - return VERR_COM_OBJECT_NOT_FOUND; - - return VINF_SUCCESS; -} - -static void autostartShutdown() -{ - serviceLogVerbose(("Shutting down ...\n")); - - g_pSession.setNull(); - g_pVirtualBox.setNull(); -} - int main(int argc, char *argv[]) { /* @@ -439,38 +544,7 @@ int main(int argc, char *argv[]) } #endif - /* - * Initialize COM. - */ - using namespace com; - HRESULT hrc = com::Initialize(); -# ifdef VBOX_WITH_XPCOM - if (hrc == NS_ERROR_FILE_ACCESS_DENIED) - { - char szHome[RTPATH_MAX] = ""; - com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome)); - return RTMsgErrorExit(RTEXITCODE_FAILURE, - "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome); - } -# endif - if (FAILED(hrc)) - return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc); - - hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); - if (FAILED(hrc)) - { - RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc); - com::ErrorInfo info; - if (!info.isFullAvailable() && !info.isBasicAvailable()) - { - com::GluePrintRCMessage(hrc); - RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start."); - } - else - com::GluePrintErrorInfo(info); - return RTEXITCODE_FAILURE; - } - + /* Set up COM */ rc = autostartSetup(); if (RT_FAILURE(rc)) return RTEXITCODE_FAILURE; @@ -485,14 +559,9 @@ int main(int argc, char *argv[]) } autostartConfigAstDestroy(pCfgAst); - EventQueue::getMainEventQueue()->processEventQueue(0); + NativeEventQueue::getMainEventQueue()->processEventQueue(0); autostartShutdown(); - - g_pVirtualBoxClient.setNull(); - - com::Shutdown(); - return rcExit; } diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp new file mode 100644 index 00000000..cc3f237c --- /dev/null +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp @@ -0,0 +1,998 @@ +/* $Id: VBoxAutostart-win.cpp $ */ +/** @file + * VirtualBox Autostart Service - Windows Specific Code. + */ + +/* + * Copyright (C) 2012-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <Windows.h> + +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> + +#include <VBox/com/NativeEventQueue.h> +#include <VBox/com/listeners.h> +#include <VBox/com/VirtualBox.h> + +#include <VBox/log.h> +#include <VBox/version.h> +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/getopt.h> +#include <iprt/semaphore.h> +#include <iprt/thread.h> + +#include "VBoxAutostart.h" + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** The service name. */ +#define AUTOSTART_SERVICE_NAME "VBoxAutostartSvc" +/** The service display name. */ +#define AUTOSTART_SERVICE_DISPLAY_NAME "VirtualBox Autostart Service" + +ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL; +bool g_fVerbose = false; +ComPtr<IVirtualBox> g_pVirtualBox = NULL; +ComPtr<ISession> g_pSession = NULL; + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The service control handler handle. */ +static SERVICE_STATUS_HANDLE g_hSupSvcWinCtrlHandler = NULL; +/** The service status. */ +static uint32_t volatile g_u32SupSvcWinStatus = SERVICE_STOPPED; +/** The semaphore the main service thread is waiting on in autostartSvcWinServiceMain. */ +static RTSEMEVENTMULTI g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static SC_HANDLE autostartSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess); + +/** + * Print out progress on the console. + * + * This runs the main event queue every now and then to prevent piling up + * unhandled things (which doesn't cause real problems, just makes things + * react a little slower than in the ideal case). + */ +DECLHIDDEN(HRESULT) showProgress(ComPtr<IProgress> progress) +{ + using namespace com; + + BOOL fCompleted = FALSE; + ULONG ulCurrentPercent = 0; + ULONG ulLastPercent = 0; + + ULONG ulLastOperationPercent = (ULONG)-1; + + ULONG ulLastOperation = (ULONG)-1; + Bstr bstrOperationDescription; + + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + + ULONG cOperations = 1; + HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations); + if (FAILED(hrc)) + return hrc; + + /* setup signal handling if cancelable */ + bool fCanceledAlready = false; + BOOL fCancelable; + hrc = progress->COMGETTER(Cancelable)(&fCancelable); + if (FAILED(hrc)) + fCancelable = FALSE; + + hrc = progress->COMGETTER(Completed(&fCompleted)); + while (SUCCEEDED(hrc)) + { + progress->COMGETTER(Percent(&ulCurrentPercent)); + + if (fCompleted) + break; + + /* process async cancelation */ + if (!fCanceledAlready) + { + hrc = progress->Cancel(); + if (SUCCEEDED(hrc)) + fCanceledAlready = true; + } + + /* make sure the loop is not too tight */ + progress->WaitForCompletion(100); + + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + hrc = progress->COMGETTER(Completed(&fCompleted)); + } + + /* complete the line. */ + LONG iRc = E_FAIL; + hrc = progress->COMGETTER(ResultCode)(&iRc); + if (SUCCEEDED(hrc)) + { + hrc = iRc; + } + + return hrc; +} + +DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType) +{ + HANDLE hEventLog = RegisterEventSourceA(NULL /* local computer */, "VBoxAutostartSvc"); + AssertReturnVoid(hEventLog != NULL); + WORD wType = 0; + const char *apsz[2]; + apsz[0] = "VBoxAutostartSvc"; + apsz[1] = pszMsg; + + switch (enmLogType) + { + case AUTOSTARTLOGTYPE_INFO: + wType = 0; + break; + case AUTOSTARTLOGTYPE_ERROR: + wType = EVENTLOG_ERROR_TYPE; + break; + case AUTOSTARTLOGTYPE_WARNING: + wType = EVENTLOG_WARNING_TYPE; + break; + case AUTOSTARTLOGTYPE_VERBOSE: + if (!g_fVerbose) + return; + wType = EVENTLOG_INFORMATION_TYPE; + break; + default: + AssertMsgFailed(("Invalid log type %d\n", enmLogType)); + } + + BOOL fRc = ReportEventA(hEventLog, /* hEventLog */ + wType, /* wType */ + 0, /* wCategory */ + 0 /** @todo mc */, /* dwEventID */ + NULL, /* lpUserSid */ + RT_ELEMENTS(apsz), /* wNumStrings */ + 0, /* dwDataSize */ + apsz, /* lpStrings */ + NULL); /* lpRawData */ + AssertMsg(fRc, ("%d\n", GetLastError())); + DeregisterEventSource(hEventLog); +} + +/** + * Opens the service control manager. + * + * When this fails, an error message will be displayed. + * + * @returns Valid handle on success. + * NULL on failure, will display an error message. + * + * @param pszAction The action which is requesting access to SCM. + * @param dwAccess The desired access. + */ +static SC_HANDLE autostartSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess) +{ + SC_HANDLE hSCM = OpenSCManager(NULL /* lpMachineName*/, NULL /* lpDatabaseName */, dwAccess); + if (hSCM == NULL) + { + DWORD err = GetLastError(); + switch (err) + { + case ERROR_ACCESS_DENIED: + autostartSvcDisplayError("%s - OpenSCManager failure: access denied\n", pszAction); + break; + default: + autostartSvcDisplayError("%s - OpenSCManager failure: %d\n", pszAction, err); + break; + } + } + return hSCM; +} + + +/** + * Opens the service. + * + * Last error is preserved on failure and set to 0 on success. + * + * @returns Valid service handle on success. + * NULL on failure, will display an error message unless it's ignored. + * + * @param pszAction The action which is requesting access to the service. + * @param dwSCMAccess The service control manager access. + * @param dwSVCAccess The desired service access. + * @param cIgnoredErrors The number of ignored errors. + * @param ... Errors codes that should not cause a message to be displayed. + */ +static SC_HANDLE autostartSvcWinOpenService(const char *pszAction, DWORD dwSCMAccess, DWORD dwSVCAccess, + unsigned cIgnoredErrors, ...) +{ + SC_HANDLE hSCM = autostartSvcWinOpenSCManager(pszAction, dwSCMAccess); + if (!hSCM) + return NULL; + + SC_HANDLE hSvc = OpenServiceA(hSCM, AUTOSTART_SERVICE_NAME, dwSVCAccess); + if (hSvc) + { + CloseServiceHandle(hSCM); + SetLastError(0); + } + else + { + DWORD err = GetLastError(); + bool fIgnored = false; + va_list va; + va_start(va, cIgnoredErrors); + while (!fIgnored && cIgnoredErrors-- > 0) + fIgnored = va_arg(va, long) == err; + va_end(va); + if (!fIgnored) + { + switch (err) + { + case ERROR_ACCESS_DENIED: + autostartSvcDisplayError("%s - OpenService failure: access denied\n", pszAction); + break; + case ERROR_SERVICE_DOES_NOT_EXIST: + autostartSvcDisplayError("%s - OpenService failure: The service does not exist. Reinstall it.\n", pszAction); + break; + default: + autostartSvcDisplayError("%s - OpenService failure: %d\n", pszAction, err); + break; + } + } + + CloseServiceHandle(hSCM); + SetLastError(err); + } + return hSvc; +} + +static RTEXITCODE autostartSvcWinInterrogate(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"interrogate\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinStop(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"stop\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinContinue(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"continue\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinPause(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"pause\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinStart(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"start\" action is not implemented.\n"); + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE autostartSvcWinQueryDescription(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"qdescription\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinQueryConfig(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"qconfig\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +static RTEXITCODE autostartSvcWinDisable(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"disable\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + +static RTEXITCODE autostartSvcWinEnable(int argc, char **argv) +{ + RTPrintf("VBoxAutostartSvc: The \"enable\" action is not implemented.\n"); + return RTEXITCODE_FAILURE; +} + + +/** + * Handle the 'delete' action. + * + * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static int autostartSvcWinDelete(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fVerbose = false; + static const RTGETOPTDEF s_aOptions[] = + { + { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + }; + int ch; + unsigned iArg = 0; + RTGETOPTUNION Value; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((ch = RTGetOpt(&GetState, &Value))) + { + switch (ch) + { + case 'v': + fVerbose = true; + break; + case VINF_GETOPT_NOT_OPTION: + return autostartSvcDisplayTooManyArgsError("delete", argc, argv, iArg); + default: + return autostartSvcDisplayGetOptError("delete", ch, argc, argv, iArg, &Value); + } + + iArg++; + } + + /* + * Create the service. + */ + int rc = RTEXITCODE_FAILURE; + SC_HANDLE hSvc = autostartSvcWinOpenService("delete", SERVICE_CHANGE_CONFIG, DELETE, + 1, ERROR_SERVICE_DOES_NOT_EXIST); + if (hSvc) + { + if (DeleteService(hSvc)) + { + RTPrintf("Successfully deleted the %s service.\n", AUTOSTART_SERVICE_NAME); + rc = RTEXITCODE_SUCCESS; + } + else + autostartSvcDisplayError("delete - DeleteService failed, err=%d.\n", GetLastError()); + CloseServiceHandle(hSvc); + } + else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + { + + if (fVerbose) + RTPrintf("The service %s was not installed, nothing to be done.", AUTOSTART_SERVICE_NAME); + else + RTPrintf("Successfully deleted the %s service.\n", AUTOSTART_SERVICE_NAME); + rc = RTEXITCODE_SUCCESS; + } + return rc; +} + + +/** + * Handle the 'create' action. + * + * @returns 0 or 1. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static RTEXITCODE autostartSvcWinCreate(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fVerbose = false; + const char *pszUser = NULL; + const char *pszPwd = NULL; + static const RTGETOPTDEF s_aOptions[] = + { + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "--user", 'u', RTGETOPT_REQ_STRING }, + { "--password", 'p', RTGETOPT_REQ_STRING } + }; + int iArg = 0; + int ch; + RTGETOPTUNION Value; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((ch = RTGetOpt(&GetState, &Value))) + { + switch (ch) + { + case 'v': + fVerbose = true; + break; + case 'u': + pszUser = Value.psz; + break; + case 'p': + pszPwd = Value.psz; + break; + default: + return autostartSvcDisplayGetOptError("create", ch, argc, argv, iArg, &Value); + } + iArg++; + } + if (iArg != argc) + return autostartSvcDisplayTooManyArgsError("create", argc, argv, iArg); + + /* + * Create the service. + */ + RTEXITCODE rc = RTEXITCODE_FAILURE; + SC_HANDLE hSCM = autostartSvcWinOpenSCManager("create", SC_MANAGER_CREATE_SERVICE); /*SC_MANAGER_ALL_ACCESS*/ + if (hSCM) + { + char szExecPath[MAX_PATH]; + if (GetModuleFileNameA(NULL /* the executable */, szExecPath, sizeof(szExecPath))) + { + if (fVerbose) + RTPrintf("Creating the %s service, binary \"%s\"...\n", + AUTOSTART_SERVICE_NAME, szExecPath); /* yea, the binary name isn't UTF-8, but wtf. */ + + SC_HANDLE hSvc = CreateServiceA(hSCM, /* hSCManager */ + AUTOSTART_SERVICE_NAME, /* lpServiceName */ + AUTOSTART_SERVICE_DISPLAY_NAME, /* lpDisplayName */ + SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, /* dwDesiredAccess */ + SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */ + SERVICE_AUTO_START, /* dwStartType */ + SERVICE_ERROR_NORMAL, /* dwErrorControl */ + szExecPath, /* lpBinaryPathName */ + NULL, /* lpLoadOrderGroup */ + NULL, /* lpdwTagId */ + NULL, /* lpDependencies */ + pszUser, /* lpServiceStartName (NULL => LocalSystem) */ + pszPwd); /* lpPassword */ + if (hSvc) + { + RTPrintf("Successfully created the %s service.\n", AUTOSTART_SERVICE_NAME); + /** @todo Set the service description or it'll look weird in the vista service manager. + * Anything else that should be configured? Start access or something? */ + rc = RTEXITCODE_SUCCESS; + CloseServiceHandle(hSvc); + } + else + { + DWORD err = GetLastError(); + switch (err) + { + case ERROR_SERVICE_EXISTS: + autostartSvcDisplayError("create - The service already exists.\n"); + break; + default: + autostartSvcDisplayError("create - CreateService failed, err=%d.\n", GetLastError()); + break; + } + } + CloseServiceHandle(hSvc); + } + else + autostartSvcDisplayError("create - Failed to obtain the executable path: %d\n", GetLastError()); + } + return rc; +} + + +/** + * Sets the service status, just a SetServiceStatus Wrapper. + * + * @returns See SetServiceStatus. + * @param dwStatus The current status. + * @param iWaitHint The wait hint, if < 0 then supply a default. + * @param dwExitCode The service exit code. + */ +static bool autostartSvcWinSetServiceStatus(DWORD dwStatus, int iWaitHint, DWORD dwExitCode) +{ + SERVICE_STATUS SvcStatus; + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SvcStatus.dwWin32ExitCode = dwExitCode; + SvcStatus.dwServiceSpecificExitCode = 0; + SvcStatus.dwWaitHint = iWaitHint >= 0 ? iWaitHint : 3000; + SvcStatus.dwCurrentState = dwStatus; + LogFlow(("autostartSvcWinSetServiceStatus: %d -> %d\n", g_u32SupSvcWinStatus, dwStatus)); + g_u32SupSvcWinStatus = dwStatus; + switch (dwStatus) + { + case SERVICE_START_PENDING: + SvcStatus.dwControlsAccepted = 0; + break; + default: + SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + break; + } + + static DWORD dwCheckPoint = 0; + switch (dwStatus) + { + case SERVICE_RUNNING: + case SERVICE_STOPPED: + SvcStatus.dwCheckPoint = 0; + default: + SvcStatus.dwCheckPoint = ++dwCheckPoint; + break; + } + return SetServiceStatus(g_hSupSvcWinCtrlHandler, &SvcStatus) != FALSE; +} + + +/** + * Service control handler (extended). + * + * @returns Windows status (see HandlerEx). + * @retval NO_ERROR if handled. + * @retval ERROR_CALL_NOT_IMPLEMENTED if not handled. + * + * @param dwControl The control code. + * @param dwEventType Event type. (specific to the control?) + * @param pvEventData Event data, specific to the event. + * @param pvContext The context pointer registered with the handler. + * Currently not used. + */ +static DWORD WINAPI autostartSvcWinServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID pvContext) +{ + LogFlow(("autostartSvcWinServiceCtrlHandlerEx: dwControl=%#x dwEventType=%#x pvEventData=%p\n", + dwControl, dwEventType, pvEventData)); + + switch (dwControl) + { + /* + * Interrogate the service about it's current status. + * MSDN says that this should just return NO_ERROR and does + * not need to set the status again. + */ + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + /* + * Request to stop the service. + */ + case SERVICE_CONTROL_STOP: + { + /* + * Check if the real services can be stopped and then tell them to stop. + */ + autostartSvcWinSetServiceStatus(SERVICE_STOP_PENDING, 3000, NO_ERROR); + /* + * Notify the main thread that we're done, it will wait for the + * VMs to stop, and set the windows service status to SERVICE_STOPPED + * and return. + */ + int rc = RTSemEventMultiSignal(g_hSupSvcWinEvent); + if (RT_FAILURE(rc)) + autostartSvcLogError("SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc); + + return NO_ERROR; + } + + case SERVICE_CONTROL_PAUSE: + case SERVICE_CONTROL_CONTINUE: + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_PARAMCHANGE: + case SERVICE_CONTROL_NETBINDADD: + case SERVICE_CONTROL_NETBINDREMOVE: + case SERVICE_CONTROL_NETBINDENABLE: + case SERVICE_CONTROL_NETBINDDISABLE: + case SERVICE_CONTROL_DEVICEEVENT: + case SERVICE_CONTROL_HARDWAREPROFILECHANGE: + case SERVICE_CONTROL_POWEREVENT: + case SERVICE_CONTROL_SESSIONCHANGE: +#ifdef SERVICE_CONTROL_PRESHUTDOWN /* vista */ + case SERVICE_CONTROL_PRESHUTDOWN: +#endif + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } + + NOREF(dwEventType); + NOREF(pvEventData); + NOREF(pvContext); + return NO_ERROR; +} + +static int autostartWorker(RTTHREAD ThreadSelf, void *pvUser) +{ + int rc = autostartSetup(); + + /** @todo: Implement config options. */ + rc = autostartStartMain(NULL); + if (RT_FAILURE(rc)) + autostartSvcLogError("Starting VMs failed, rc=%Rrc", rc); + + return rc; +} + +/** + * Windows Service Main. + * + * This is invoked when the service is started and should not return until + * the service has been stopped. + * + * @param cArgs Argument count. + * @param papszArgs Argument vector. + */ +static VOID WINAPI autostartSvcWinServiceMain(DWORD cArgs, LPTSTR *papszArgs) +{ + LogFlowFuncEnter(); + + /* + * Register the control handler function for the service and report to SCM. + */ + Assert(g_u32SupSvcWinStatus == SERVICE_STOPPED); + g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerExA(AUTOSTART_SERVICE_NAME, autostartSvcWinServiceCtrlHandlerEx, NULL); + if (g_hSupSvcWinCtrlHandler) + { + DWORD err = ERROR_GEN_FAILURE; + if (autostartSvcWinSetServiceStatus(SERVICE_START_PENDING, 3000, NO_ERROR)) + { + if (cArgs == 1) + { + /* + * Create the event semaphore we'll be waiting on and + * then instantiate the actual services. + */ + int rc = RTSemEventMultiCreate(&g_hSupSvcWinEvent); + if (RT_SUCCESS(rc)) + { + /* + * Update the status and enter the work loop. + */ + if (autostartSvcWinSetServiceStatus(SERVICE_RUNNING, 0, 0)) + { + LogFlow(("autostartSvcWinServiceMain: calling RTSemEventMultiWait\n")); + RTTHREAD hWorker; + RTThreadCreate(&hWorker, autostartWorker, NULL, 0, RTTHREADTYPE_DEFAULT, 0, "WorkerThread"); + + LogFlow(("autostartSvcWinServiceMain: woke up\n")); + err = NO_ERROR; + rc = RTSemEventMultiWait(g_hSupSvcWinEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + LogFlow(("autostartSvcWinServiceMain: woke up\n")); + /** @todo: Autostop part. */ + err = NO_ERROR; + } + else + autostartSvcLogError("RTSemEventWait failed, rc=%Rrc", rc); + + autostartShutdown(); + } + else + { + err = GetLastError(); + autostartSvcLogError("SetServiceStatus failed, err=%d", err); + } + + RTSemEventMultiDestroy(g_hSupSvcWinEvent); + g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI; + } + else + autostartSvcLogError("RTSemEventMultiCreate failed, rc=%Rrc", rc); + } + else + autostartSvcLogTooManyArgsError("main", cArgs, NULL, 0); + } + else + { + err = GetLastError(); + autostartSvcLogError("SetServiceStatus failed, err=%d", err); + } + autostartSvcWinSetServiceStatus(SERVICE_STOPPED, 0, err); + } + else + autostartSvcLogError("RegisterServiceCtrlHandlerEx failed, err=%d", GetLastError()); + + LogFlowFuncLeave(); +} + + +/** + * Handle the 'create' action. + * + * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static int autostartSvcWinRunIt(int argc, char **argv) +{ + LogFlowFuncEnter(); + + /* + * Initialize release logging. + */ + /** @todo release logging of the system-wide service. */ + + /* + * Parse the arguments. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--dummy", 'd', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTGETOPTUNION Value; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((ch = RTGetOpt(&GetState, &Value))) + switch (ch) + { + default: return autostartSvcDisplayGetOptError("runit", ch, argc, argv, iArg, &Value); + } + if (iArg != argc) + return autostartSvcDisplayTooManyArgsError("runit", argc, argv, iArg); + + /* + * Register the service with the service control manager + * and start dispatching requests from it (all done by the API). + */ + static SERVICE_TABLE_ENTRY const s_aServiceStartTable[] = + { + { _T(AUTOSTART_SERVICE_NAME), autostartSvcWinServiceMain }, + { NULL, NULL} + }; + if (StartServiceCtrlDispatcher(&s_aServiceStartTable[0])) + { + LogFlowFuncLeave(); + return RTEXITCODE_SUCCESS; /* told to quit, so quit. */ + } + + DWORD err = GetLastError(); + switch (err) + { + case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: + autostartSvcWinServiceMain(0, NULL);//autostartSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n"); + break; + default: + autostartSvcLogError("StartServiceCtrlDispatcher failed, err=%d", err); + break; + } + return RTEXITCODE_FAILURE; +} + + +/** + * Show the version info. + * + * @returns RTEXITCODE_SUCCESS. + */ +static RTEXITCODE autostartSvcWinShowVersion(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fBrief = false; + static const RTGETOPTDEF s_aOptions[] = + { + { "--brief", 'b', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTGETOPTUNION Value; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((ch = RTGetOpt(&GetState, &Value))) + switch (ch) + { + case 'b': fBrief = true; break; + default: return autostartSvcDisplayGetOptError("version", ch, argc, argv, iArg, &Value); + + } + if (iArg != argc) + return autostartSvcDisplayTooManyArgsError("version", argc, argv, iArg); + + /* + * Do the printing. + */ + if (fBrief) + RTPrintf("%s\n", VBOX_VERSION_STRING); + else + RTPrintf("VirtualBox Autostart Service Version %s\n" + "(C) 2012 Oracle Corporation\n" + "All rights reserved.\n", + VBOX_VERSION_STRING); + return RTEXITCODE_SUCCESS; +} + + +/** + * Show the usage help screen. + * + * @returns RTEXITCODE_SUCCESS. + */ +static RTEXITCODE autostartSvcWinShowHelp(void) +{ + RTPrintf("VirtualBox Autostart Service Version %s\n" + "(C) 2012 Oracle Corporation\n" + "All rights reserved.\n" + "\n", + VBOX_VERSION_STRING); + RTPrintf("Usage:\n" + "\n" + "VBoxAutostartSvc\n" + " Runs the service.\n" + "VBoxAutostartSvc <version|-v|--version> [-brief]\n" + " Displays the version.\n" + "VBoxAutostartSvc <help|-?|-h|--help> [...]\n" + " Displays this help screen.\n" + "\n" + "VBoxAutostartSvc <install|/RegServer|/i>\n" + " Installs the service.\n" + "VBoxAutostartSvc <uninstall|delete|/UnregServer|/u>\n" + " Uninstalls the service.\n" + ); + return RTEXITCODE_SUCCESS; +} + + +/** + * VBoxAutostart main(), Windows edition. + * + * + * @returns 0 on success. + * + * @param argc Number of arguments in argv. + * @param argv Argument vector. + */ +int main(int argc, char **argv) +{ + /* + * Initialize the IPRT first of all. + */ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + { + autostartSvcLogError("RTR3InitExe failed with rc=%Rrc", rc); + return RTEXITCODE_FAILURE; + } + + RTThreadSleep(10 * 1000); + + /* + * Parse the initial arguments to determine the desired action. + */ + enum + { + kAutoSvcAction_RunIt, + + kAutoSvcAction_Create, + kAutoSvcAction_Delete, + + kAutoSvcAction_Enable, + kAutoSvcAction_Disable, + kAutoSvcAction_QueryConfig, + kAutoSvcAction_QueryDescription, + + kAutoSvcAction_Start, + kAutoSvcAction_Pause, + kAutoSvcAction_Continue, + kAutoSvcAction_Stop, + kAutoSvcAction_Interrogate, + + kAutoSvcAction_End + } enmAction = kAutoSvcAction_RunIt; + int iArg = 1; + if (argc > 1) + { + if ( !stricmp(argv[iArg], "/RegServer") + || !stricmp(argv[iArg], "install") + || !stricmp(argv[iArg], "/i")) + enmAction = kAutoSvcAction_Create; + else if ( !stricmp(argv[iArg], "/UnregServer") + || !stricmp(argv[iArg], "/u") + || !stricmp(argv[iArg], "uninstall") + || !stricmp(argv[iArg], "delete")) + enmAction = kAutoSvcAction_Delete; + + else if (!stricmp(argv[iArg], "enable")) + enmAction = kAutoSvcAction_Enable; + else if (!stricmp(argv[iArg], "disable")) + enmAction = kAutoSvcAction_Disable; + else if (!stricmp(argv[iArg], "qconfig")) + enmAction = kAutoSvcAction_QueryConfig; + else if (!stricmp(argv[iArg], "qdescription")) + enmAction = kAutoSvcAction_QueryDescription; + + else if ( !stricmp(argv[iArg], "start") + || !stricmp(argv[iArg], "/t")) + enmAction = kAutoSvcAction_Start; + else if (!stricmp(argv[iArg], "pause")) + enmAction = kAutoSvcAction_Start; + else if (!stricmp(argv[iArg], "continue")) + enmAction = kAutoSvcAction_Continue; + else if (!stricmp(argv[iArg], "stop")) + enmAction = kAutoSvcAction_Stop; + else if (!stricmp(argv[iArg], "interrogate")) + enmAction = kAutoSvcAction_Interrogate; + else if ( !stricmp(argv[iArg], "help") + || !stricmp(argv[iArg], "?") + || !stricmp(argv[iArg], "/?") + || !stricmp(argv[iArg], "-?") + || !stricmp(argv[iArg], "/h") + || !stricmp(argv[iArg], "-h") + || !stricmp(argv[iArg], "/help") + || !stricmp(argv[iArg], "-help") + || !stricmp(argv[iArg], "--help")) + return autostartSvcWinShowHelp(); + else if ( !stricmp(argv[iArg], "version") + || !stricmp(argv[iArg], "/v") + || !stricmp(argv[iArg], "-v") + || !stricmp(argv[iArg], "/version") + || !stricmp(argv[iArg], "-version") + || !stricmp(argv[iArg], "--version")) + return autostartSvcWinShowVersion(argc - iArg - 1, argv + iArg + 1); + else + iArg--; + iArg++; + } + + /* + * Dispatch it. + */ + switch (enmAction) + { + case kAutoSvcAction_RunIt: + return autostartSvcWinRunIt(argc - iArg, argv + iArg); + + case kAutoSvcAction_Create: + return autostartSvcWinCreate(argc - iArg, argv + iArg); + case kAutoSvcAction_Delete: + return autostartSvcWinDelete(argc - iArg, argv + iArg); + + case kAutoSvcAction_Enable: + return autostartSvcWinEnable(argc - iArg, argv + iArg); + case kAutoSvcAction_Disable: + return autostartSvcWinDisable(argc - iArg, argv + iArg); + case kAutoSvcAction_QueryConfig: + return autostartSvcWinQueryConfig(argc - iArg, argv + iArg); + case kAutoSvcAction_QueryDescription: + return autostartSvcWinQueryDescription(argc - iArg, argv + iArg); + + case kAutoSvcAction_Start: + return autostartSvcWinStart(argc - iArg, argv + iArg); + case kAutoSvcAction_Pause: + return autostartSvcWinPause(argc - iArg, argv + iArg); + case kAutoSvcAction_Continue: + return autostartSvcWinContinue(argc - iArg, argv + iArg); + case kAutoSvcAction_Stop: + return autostartSvcWinStop(argc - iArg, argv + iArg); + case kAutoSvcAction_Interrogate: + return autostartSvcWinInterrogate(argc - iArg, argv + iArg); + + default: + AssertMsgFailed(("enmAction=%d\n", enmAction)); + return RTEXITCODE_FAILURE; + } +} + diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h index 80fe87f2..09026404 100644 --- a/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h @@ -21,6 +21,9 @@ /******************************************************************************* * Header Files * *******************************************************************************/ +#include <iprt/getopt.h> +#include <iprt/types.h> + #include <VBox/cdefs.h> #include <VBox/types.h> @@ -92,24 +95,58 @@ typedef struct CFGAST } CFGAST, *PCFGAST; /** Flag whether we are in verbose logging mode. */ -extern bool g_fVerbose; +extern bool g_fVerbose; /** Handle to the VirtualBox interface. */ -extern ComPtr<IVirtualBox> g_pVirtualBox; +extern ComPtr<IVirtualBox> g_pVirtualBox; /** Handle to the session interface. */ -extern ComPtr<ISession> g_pSession; - +extern ComPtr<ISession> g_pSession; +/** handle to the VirtualBox interface. */ +extern ComPtr<IVirtualBoxClient> g_pVirtualBoxClient; /** - * Log information in verbose mode. + * System log type. */ -#define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; } +typedef enum AUTOSTARTLOGTYPE +{ + /** Invalid log type. */ + AUTOSTARTLOGTYPE_INVALID = 0, + /** Log info message. */ + AUTOSTARTLOGTYPE_INFO, + /** Log error message. */ + AUTOSTARTLOGTYPE_ERROR, + /** Log warning message. */ + AUTOSTARTLOGTYPE_WARNING, + /** Log verbose message, only if verbose mode is activated. */ + AUTOSTARTLOGTYPE_VERBOSE, + /** Famous 32bit hack. */ + AUTOSTARTLOGTYPE_32BIT_HACK = 0x7fffffff +} AUTOSTARTLOGTYPE; /** - * Log messages to the release log. + * Log messages to the system and release log. * * @returns nothing. - * @param pszFormat Format string. + * @param pszMsg Message to log. + * @param enmLogType Log type to use. + */ +DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType); + +/** + * Print out progress on the console. + * + * This runs the main event queue every now and then to prevent piling up + * unhandled things (which doesn't cause real problems, just makes things + * react a little slower than in the ideal case). */ -DECLHIDDEN(void) serviceLog(const char *pszFormat, ...); +DECLHIDDEN(HRESULT) showProgress(ComPtr<IProgress> progress); + +/** + * Converts the machine state to a human readable string. + * + * @returns Pointer to the human readable state. + * @param enmMachineState Machine state to convert. + * @param fShort Flag whether to return a short form. + */ +DECLHIDDEN(const char *) machineStateToName(MachineState_T enmMachineState, bool fShort); /** * Parse the given configuration file and return the interesting config parameters. @@ -154,5 +191,145 @@ DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst); */ DECLHIDDEN(RTEXITCODE) autostartStopMain(PCFGAST pCfgAst); +/** + * Logs a verbose message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogVerboseV(const char *pszFormat, va_list va); + +/** + * Logs a verbose message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogVerbose(const char *pszFormat, ...); + +/** + * Logs a warning message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogWarningV(const char *pszFormat, va_list va); + +/** + * Logs a warning message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogWarning(const char *pszFormat, ...); + +/** + * Logs a info message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogInfoV(const char *pszFormat, va_list va); + +/** + * Logs a info message to the appropriate system log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcLogInfo(const char *pszFormat, ...); + +/** + * Logs the message to the appropriate system log. + * + * In debug builds this will also put it in the debug log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + * + * @todo This should later be replaced by the release logger and callback destination(s). + */ +DECLHIDDEN(void) autostartSvcLogErrorV(const char *pszFormat, va_list va); + +/** + * Logs the error message to the appropriate system log. + * + * In debug builds this will also put it in the debug log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + * + * @todo This should later be replaced by the release logger and callback destination(s). + */ +DECLHIDDEN(void) autostartSvcLogError(const char *pszFormat, ...); + +/** + * Deals with RTGetOpt failure, bitching in the system log. + * + * @returns 1 + * @param pszAction The action name. + * @param rc The RTGetOpt return value. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + * @param pValue The value returned by RTGetOpt. + */ +DECLHIDDEN(RTEXITCODE) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue); + +/** + * Bitch about too many arguments (after RTGetOpt stops) in the system log. + * + * @returns 1 + * @param pszAction The action name. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + */ +DECLHIDDEN(RTEXITCODE) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg); + +/** + * Prints an error message to the screen. + * + * @param pszFormat The message format string. + * @param va Format arguments. + */ +DECLHIDDEN(void) autostartSvcDisplayErrorV(const char *pszFormat, va_list va); + +/** + * Prints an error message to the screen. + * + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +DECLHIDDEN(void) autostartSvcDisplayError(const char *pszFormat, ...); + +/** + * Deals with RTGetOpt failure. + * + * @returns 1 + * @param pszAction The action name. + * @param rc The RTGetOpt return value. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + * @param pValue The value returned by RTGetOpt. + */ +DECLHIDDEN(RTEXITCODE) autostartSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue); + +/** + * Bitch about too many arguments (after RTGetOpt stops). + * + * @returns RTEXITCODE_FAILURE + * @param pszAction The action name. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + */ +DECLHIDDEN(RTEXITCODE) autostartSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg); + +DECLHIDDEN(int) autostartSetup(); + +DECLHIDDEN(void) autostartShutdown(); + #endif /* __VBoxAutostart_h__ */ diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp index a26a5883..c483966e 100644 --- a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp @@ -380,9 +380,15 @@ static int autostartConfigTokenizerGetNextToken(PCFGTOKENIZER pCfgTokenizer, return autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL, &pCfgTokenizer->pTokenNext); } -static const char *autostartConfigTokenToString(PCFGTOKEN pToken) +/** + * Returns a stringified version of the token type. + * + * @returns Stringified version of the token type. + * @param enmType Token type. + */ +static const char *autostartConfigTokenTypeToStr(CFGTOKENTYPE enmType) { - switch (pToken->enmType) + switch (enmType) { case CFGTOKENTYPE_COMMA: return ","; @@ -395,7 +401,7 @@ static const char *autostartConfigTokenToString(PCFGTOKEN pToken) case CFGTOKENTYPE_EOF: return "<EOF>"; case CFGTOKENTYPE_ID: - return pToken->u.Id.achToken; + return "<Identifier>"; default: AssertFailed(); return "<Invalid>"; @@ -405,6 +411,26 @@ static const char *autostartConfigTokenToString(PCFGTOKEN pToken) return NULL; } +/** + * Returns a stringified version of the token. + * + * @returns Stringified version of the token type. + * @param pToken Token. + */ +static const char *autostartConfigTokenToString(PCFGTOKEN pToken) +{ + if (pToken->enmType == CFGTOKENTYPE_ID) + return pToken->u.Id.achToken; + else + return autostartConfigTokenTypeToStr(pToken->enmType); +} + +/** + * Returns the length of the token in characters (without zero terminator). + * + * @returns Token length. + * @param pToken Token. + */ static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken) { switch (pToken->enmType) @@ -427,12 +453,19 @@ static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken) return 0; } +/** + * Log unexpected token error. + * + * @returns nothing. + * @param pToken The token which caused the error. + * @param pszExpected String of the token which was expected. + */ static void autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected) { - RTMsgError("Unexpected token '%s' at %d:%d.%d, expected '%s'", - autostartConfigTokenToString(pToken), - pToken->iLine, pToken->cchStart, - pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected); + autostartSvcLogError("Unexpected token '%s' at %d:%d.%d, expected '%s'", + autostartConfigTokenToString(pToken), + pToken->iLine, pToken->cchStart, + pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected); } /** @@ -452,7 +485,7 @@ static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, { if (pCfgToken->enmType != enmType) { - autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, "@todo"); + autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType)); rc = VERR_INVALID_PARAMETER; } @@ -569,6 +602,7 @@ static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const c return VERR_NO_MEMORY; pCfgAst->enmType = CFGASTNODETYPE_COMPOUND; + pCfgAst->u.Compound.cAstNodes = 0; pCfgAst->pszKey = RTStrDup(pszScopeId); if (!pCfgAst->pszKey) { @@ -618,10 +652,22 @@ static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const c /* Add to the current compound node. */ if (RT_SUCCESS(rc)) { - Assert(idxAstNodeCur < cAstNodesMax); - pCfgAst->u.Compound.apAstNodes[idxAstNodeCur] = pAstNode; - idxAstNodeCur++; - /** @todo: realloc if array is getting to small. */ + if (pCfgAst->u.Compound.cAstNodes >= cAstNodesMax) + { + cAstNodesMax += 10; + + PCFGAST pCfgAstNew = (PCFGAST)RTMemRealloc(pCfgAst, RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax])); + if (!pCfgAstNew) + rc = VERR_NO_MEMORY; + else + pCfgAst = pCfgAstNew; + } + + if (RT_SUCCESS(rc)) + { + pCfgAst->u.Compound.apAstNodes[pCfgAst->u.Compound.cAstNodes] = pAstNode; + pCfgAst->u.Compound.cAstNodes++; + } } autostartConfigTokenFree(pCfgTokenizer, pToken); @@ -629,10 +675,7 @@ static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const c } while (RT_SUCCESS(rc)); if (RT_SUCCESS(rc)) - { - pCfgAst->u.Compound.cAstNodes = idxAstNodeCur; *ppCfgAst = pCfgAst; - } else autostartConfigAstDestroy(pCfgAst); diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp index 1d1acd1f..9387c4da 100644 --- a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp @@ -71,7 +71,7 @@ DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst) if (uStartupDelay) { - serviceLogVerbose(("Delay starting for %d seconds ...\n", uStartupDelay)); + autostartSvcLogVerbose("Delay starting for %d seconds ...\n", uStartupDelay); vrc = RTThreadSleep(uStartupDelay * 1000); } @@ -128,8 +128,8 @@ DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst) if ((*it).uStartupDelay > uDelayCurr) { - serviceLogVerbose(("Delay starting of the next VMs for %d seconds ...\n", - (*it).uStartupDelay - uDelayCurr)); + autostartSvcLogVerbose("Delay starting of the next VMs for %d seconds ...\n", + (*it).uStartupDelay - uDelayCurr); RTThreadSleep(((*it).uStartupDelay - uDelayCurr) * 1000); uDelayCurr = (*it).uStartupDelay; } @@ -141,7 +141,7 @@ DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst) Bstr("").raw(), progress.asOutParam())); if (SUCCEEDED(rc) && !progress.isNull()) { - serviceLogVerbose(("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw())); + autostartSvcLogVerbose("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw()); CHECK_ERROR(progress, WaitForCompletion(-1)); if (SUCCEEDED(rc)) { @@ -161,7 +161,7 @@ DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst) com::GluePrintErrorInfo(info); } else - serviceLogVerbose(("VM \"%ls\" has been successfully started.\n", (*it).strId.raw())); + autostartSvcLogVerbose("VM \"%ls\" has been successfully started.\n", (*it).strId.raw()); } } } diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp index a2ee69ae..19d814ae 100644 --- a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp @@ -26,6 +26,7 @@ #include <iprt/stream.h> #include <iprt/log.h> #include <iprt/assert.h> +#include <iprt/message.h> #include <algorithm> #include <list> @@ -35,11 +36,196 @@ using namespace com; +/** + * VM list entry. + */ +typedef struct AUTOSTOPVM +{ + /** ID of the VM to start. */ + Bstr strId; + /** Action to do with the VM. */ + AutostopType_T enmAutostopType; +} AUTOSTOPVM; + +static HRESULT autostartSaveVMState(ComPtr<IConsole> &console) +{ + HRESULT rc = S_OK; + ComPtr<IProgress> progress; + + do + { + /* first pause so we don't trigger a live save which needs more time/resources */ + bool fPaused = false; + rc = console->Pause(); + if (FAILED(rc)) + { + bool fError = true; + if (rc == VBOX_E_INVALID_VM_STATE) + { + /* check if we are already paused */ + MachineState_T machineState; + CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState)); + /* the error code was lost by the previous instruction */ + rc = VBOX_E_INVALID_VM_STATE; + if (machineState != MachineState_Paused) + { + RTMsgError("Machine in invalid state %d -- %s\n", + machineState, machineStateToName(machineState, false)); + } + else + { + fError = false; + fPaused = true; + } + } + if (fError) + break; + } + + CHECK_ERROR(console, SaveState(progress.asOutParam())); + if (FAILED(rc)) + { + if (!fPaused) + console->Resume(); + break; + } + + rc = showProgress(progress); + CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state")); + if (FAILED(rc)) + { + if (!fPaused) + console->Resume(); + } + } while (0); + + return rc; +} + DECLHIDDEN(RTEXITCODE) autostartStopMain(PCFGAST pCfgAst) { RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + int vrc = VINF_SUCCESS; + std::list<AUTOSTOPVM> listVM; + + /* + * Build a list of all VMs we need to autostop first, apply the overrides + * from the configuration and start the VMs afterwards. + */ + com::SafeIfaceArray<IMachine> machines; + HRESULT rc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)); + if (SUCCEEDED(rc)) + { + /* + * Iterate through the collection and construct a list of machines + * we have to check. + */ + for (size_t i = 0; i < machines.size(); ++i) + { + if (machines[i]) + { + BOOL fAccessible; + CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible)); + if (!fAccessible) + continue; + + AutostopType_T enmAutostopType; + CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostopType)(&enmAutostopType)); + if (enmAutostopType != AutostopType_Disabled) + { + AUTOSTOPVM autostopVM; + + CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostopVM.strId.asOutParam())); + autostopVM.enmAutostopType = enmAutostopType; + + listVM.push_back(autostopVM); + } + } + } + + if ( SUCCEEDED(rc) + && listVM.size()) + { + std::list<AUTOSTOPVM>::iterator it; + for (it = listVM.begin(); it != listVM.end(); it++) + { + MachineState_T enmMachineState; + ComPtr<IMachine> machine; + + CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(), + machine.asOutParam())); + + CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState)); + + /* Wait until the VM changes from a transient state back. */ + while ( enmMachineState >= MachineState_FirstTransient + && enmMachineState <= MachineState_LastTransient) + { + RTThreadSleep(1000); + CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState)); + } + + /* Only power off running machines. */ + if ( enmMachineState == MachineState_Running + || enmMachineState == MachineState_Paused) + { + ComPtr<IConsole> console; + ComPtr<IProgress> progress; + + /* open a session for the VM */ + CHECK_ERROR_BREAK(machine, LockMachine(g_pSession, LockType_Shared)); + + /* get the associated console */ + CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam())); + + switch ((*it).enmAutostopType) + { + case AutostopType_SaveState: + { + rc = autostartSaveVMState(console); + break; + } + case AutostopType_PowerOff: + { + CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam())); + + rc = showProgress(progress); + CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine")); + break; + } + case AutostopType_AcpiShutdown: + { + BOOL fGuestEnteredACPI = false; + CHECK_ERROR_BREAK(console, GetGuestEnteredACPIMode(&fGuestEnteredACPI)); + if (fGuestEnteredACPI && enmMachineState == MachineState_Running) + { + CHECK_ERROR_BREAK(console, PowerButton()); + + autostartSvcLogInfo("Waiting for VM \"%ls\" to power off...\n", (*it).strId.raw()); - AssertMsgFailed(("Not implemented yet!\n")); + do + { + RTThreadSleep(1000); + CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState)); + } while (enmMachineState == MachineState_Running); + } + else + { + /* Use save state instead and log this to the console. */ + autostartSvcLogWarning("The guest of VM \"%ls\" does not support ACPI shutdown or is currently paused, saving state...\n", + (*it).strId.raw()); + rc = autostartSaveVMState(console); + } + break; + } + default: + autostartSvcLogWarning("Unknown autostop type for VM \"%ls\"\n", (*it).strId.raw()); + } + g_pSession->UnlockMachine(); + } + } + } + } return rcExit; } diff --git a/src/VBox/Frontends/VBoxAutostart/VBoxAutostartUtils.cpp b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartUtils.cpp new file mode 100644 index 00000000..9033aee2 --- /dev/null +++ b/src/VBox/Frontends/VBoxAutostart/VBoxAutostartUtils.cpp @@ -0,0 +1,294 @@ +/* $Id: VBoxAutostartUtils.cpp $ */ +/** @file + * VBoxAutostart - VirtualBox Autostart service, start machines during system boot. + * Utils used by the windows and posix frontends. + */ + +/* + * Copyright (C) 2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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 <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> + +#include <VBox/err.h> + +#include <iprt/message.h> +#include <iprt/thread.h> +#include <iprt/stream.h> +#include <iprt/log.h> +#include <iprt/path.h> + +#include <algorithm> +#include <list> +#include <string> + +#include "VBoxAutostart.h" + +using namespace com; + +DECLHIDDEN(const char *) machineStateToName(MachineState_T machineState, bool fShort) +{ + switch (machineState) + { + case MachineState_PoweredOff: + return fShort ? "poweroff" : "powered off"; + case MachineState_Saved: + return "saved"; + case MachineState_Aborted: + return "aborted"; + case MachineState_Teleported: + return "teleported"; + case MachineState_Running: + return "running"; + case MachineState_Paused: + return "paused"; + case MachineState_Stuck: + return fShort ? "gurumeditation" : "guru meditation"; + case MachineState_LiveSnapshotting: + return fShort ? "livesnapshotting" : "live snapshotting"; + case MachineState_Teleporting: + return "teleporting"; + case MachineState_Starting: + return "starting"; + case MachineState_Stopping: + return "stopping"; + case MachineState_Saving: + return "saving"; + case MachineState_Restoring: + return "restoring"; + case MachineState_TeleportingPausedVM: + return fShort ? "teleportingpausedvm" : "teleporting paused vm"; + case MachineState_TeleportingIn: + return fShort ? "teleportingin" : "teleporting (incoming)"; + case MachineState_RestoringSnapshot: + return fShort ? "restoringsnapshot" : "restoring snapshot"; + case MachineState_DeletingSnapshot: + return fShort ? "deletingsnapshot" : "deleting snapshot"; + case MachineState_DeletingSnapshotOnline: + return fShort ? "deletingsnapshotlive" : "deleting snapshot live"; + case MachineState_DeletingSnapshotPaused: + return fShort ? "deletingsnapshotlivepaused" : "deleting snapshot live paused"; + case MachineState_SettingUp: + return fShort ? "settingup" : "setting up"; + default: + break; + } + return "unknown"; +} + +DECLHIDDEN(void) autostartSvcLogErrorV(const char *pszFormat, va_list va) +{ + if (*pszFormat) + { + char *pszMsg = NULL; + if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) + { + autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_ERROR); + RTStrFree(pszMsg); + } + else + autostartSvcOsLogStr(pszFormat, AUTOSTARTLOGTYPE_ERROR); + } +} + +DECLHIDDEN(void) autostartSvcLogError(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + autostartSvcLogErrorV(pszFormat, va); + va_end(va); +} + +DECLHIDDEN(void) autostartSvcLogVerboseV(const char *pszFormat, va_list va) +{ + if (*pszFormat) + { + char *pszMsg = NULL; + if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) + { + autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_VERBOSE); + RTStrFree(pszMsg); + } + else + autostartSvcOsLogStr(pszFormat, AUTOSTARTLOGTYPE_VERBOSE); + } +} + +DECLHIDDEN(void) autostartSvcLogVerbose(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + autostartSvcLogVerboseV(pszFormat, va); + va_end(va); +} + +DECLHIDDEN(void) autostartSvcLogWarningV(const char *pszFormat, va_list va) +{ + if (*pszFormat) + { + char *pszMsg = NULL; + if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) + { + autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_WARNING); + RTStrFree(pszMsg); + } + else + autostartSvcOsLogStr(pszFormat, AUTOSTARTLOGTYPE_WARNING); + } +} + +DECLHIDDEN(void) autostartSvcLogInfo(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + autostartSvcLogInfoV(pszFormat, va); + va_end(va); +} + +DECLHIDDEN(void) autostartSvcLogInfoV(const char *pszFormat, va_list va) +{ + if (*pszFormat) + { + char *pszMsg = NULL; + if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) + { + autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_INFO); + RTStrFree(pszMsg); + } + else + autostartSvcOsLogStr(pszFormat, AUTOSTARTLOGTYPE_INFO); + } +} + +DECLHIDDEN(void) autostartSvcLogWarning(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + autostartSvcLogWarningV(pszFormat, va); + va_end(va); +} + +DECLHIDDEN(RTEXITCODE) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue) +{ + NOREF(pValue); + autostartSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s", + pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); + return RTEXITCODE_FAILURE; +} + +DECLHIDDEN(RTEXITCODE) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) +{ + Assert(iArg < argc); + autostartSvcLogError("%s - Too many arguments: %s", pszAction, argv[iArg]); + for ( ; iArg < argc; iArg++) + LogRel(("arg#%i: %s\n", iArg, argv[iArg])); + return RTEXITCODE_FAILURE; +} + +DECLHIDDEN(void) autostartSvcDisplayErrorV(const char *pszFormat, va_list va) +{ + RTStrmPrintf(g_pStdErr, "VBoxSupSvc error: "); + RTStrmPrintfV(g_pStdErr, pszFormat, va); + Log(("autostartSvcDisplayErrorV: %s", pszFormat)); /** @todo format it! */ +} + +DECLHIDDEN(void) autostartSvcDisplayError(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + autostartSvcDisplayErrorV(pszFormat, va); + va_end(va); +} + +DECLHIDDEN(RTEXITCODE) autostartSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue) +{ + autostartSvcDisplayError("%s - RTGetOpt failure, %Rrc (%d): %s\n", + pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); + return RTEXITCODE_FAILURE; +} + +DECLHIDDEN(RTEXITCODE) autostartSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) +{ + Assert(iArg < argc); + autostartSvcDisplayError("%s - Too many arguments: %s\n", pszAction, argv[iArg]); + return RTEXITCODE_FAILURE; +} + +DECLHIDDEN(int) autostartSetup() +{ + autostartSvcOsLogStr("Setting up ...\n", AUTOSTARTLOGTYPE_VERBOSE); + + /* + * Initialize COM. + */ + using namespace com; + HRESULT hrc = com::Initialize(); +# ifdef VBOX_WITH_XPCOM + if (hrc == NS_ERROR_FILE_ACCESS_DENIED) + { + char szHome[RTPATH_MAX] = ""; + com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome)); + return RTMsgErrorExit(RTEXITCODE_FAILURE, + "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome); + } +# endif + if (FAILED(hrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc); + + hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); + if (FAILED(hrc)) + { + RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc); + com::ErrorInfo info; + if (!info.isFullAvailable() && !info.isBasicAvailable()) + { + com::GluePrintRCMessage(hrc); + RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start."); + } + else + com::GluePrintErrorInfo(info); + return RTEXITCODE_FAILURE; + } + + /* + * Setup VirtualBox + session interfaces. + */ + HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam()); + if (SUCCEEDED(rc)) + { + rc = g_pSession.createInprocObject(CLSID_Session); + if (FAILED(rc)) + RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc); + } + else + RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc); + + if (FAILED(rc)) + return VERR_COM_OBJECT_NOT_FOUND; + + return VINF_SUCCESS; +} + +DECLHIDDEN(void) autostartShutdown() +{ + autostartSvcOsLogStr("Shutting down ...\n", AUTOSTARTLOGTYPE_VERBOSE); + + g_pSession.setNull(); + g_pVirtualBox.setNull(); + g_pVirtualBoxClient.setNull(); + com::Shutdown(); +} + |
