VBoxAutostart.cpp revision e366e4992f5d81a0b93412e10d2ef9dc06f930ed
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * VBoxAutostart - VirtualBox Autostart service.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Copyright (C) 2012 Oracle Corporation
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * This file is part of VirtualBox Open Source Edition (OSE), as
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * available from http://www.virtualbox.org. This file is free software;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * you can redistribute it and/or modify it under the terms of the GNU
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * General Public License (GPL) as published by the Free Software
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Foundation, in version 2 as it comes in the "COPYING" file of the
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys/*******************************************************************************
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys* Header Files *
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys*******************************************************************************/
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysusing namespace com;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Tokenizer instance data for the config data.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllystypedef struct CFGTOKENIZER
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** Config file handle. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** String buffer for the current line we are operating in. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** Size of the string buffer. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** Current position in the line. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** Current line in the config file. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * VM list entry.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllystypedef struct AUTOSTARTVM
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** ID of the VM to start. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** Startup delay of the VM. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic bool g_fVerbose = false;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys/** Logging parameters. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys/** Run in background. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic bool g_fDaemonize = false;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Command line arguments.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /** For displayHelp(). */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys#define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Reads the next line from the config stream.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns VBox status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pCfgTokenizer The config tokenizer.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Creates the config tokenizer from the given filename.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns VBox status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pszFilename Config filename.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Destroys the given config tokenizer.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns nothing.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pCfgTokenizer The config tokenizer to destroy.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Read the next token from the config file.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns VBox status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pCfgTokenizer The config tokenizer data.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param ppszToken Where to store the start to the next token on success.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pcchToken Where to store the number of characters of the next token
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * excluding the \0 terminator on success.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartConfigTokenizerReadNext(PCFGTOKENIZER pCfgTokenizer, const char **ppszToken,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Skip all spaces. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Check if we have to read a new line. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* start from the beginning. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Get the complete token. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, const char *pszTokCheck)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken, &cchToken);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTMsgError("Unexpected token at line %d, expected '%s'",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Returns the start of the next token without consuming it.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns VBox status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pCfgTokenizer Tokenizer instance data.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param ppszTok Where to store the start of the next token on success.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer, const char **ppszTok)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Skip all spaces. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Check if we have to read a new line. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* start from the beginning. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Check whether the given token is a reserved token.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns true if the token is reserved or false otherwise.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pszToken The token to check.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param cchToken Size of the token in characters.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic bool autostartConfigTokenizerIsReservedToken(const char *pszToken, size_t cchToken)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys return true;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys && ( !RTStrNCmp(pszToken, "default_policy", cchToken)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys || !RTStrNCmp(pszToken, "exception_list", cchToken)))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys return true;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys return false;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Parse the given configuration file and return the interesting config parameters.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns VBox status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pszFilename The config file to parse.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param pfAllowed Where to store the flag whether the user of this process
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * is allowed to start VMs automatically during system startup.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @param puStartupDelay Where to store the startup delay for the user.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic int autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys bool fDefaultAllow = false;
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys AssertPtrReturn(puStartupDelay, VERR_INVALID_POINTER);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = RTProcQueryUsernameA(RTProcSelf(), &pszUserProcess);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys if (!RTStrNCmp(pszToken, "default_policy", strlen("default_policy")))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys else if (!RTStrNCmp(pszToken, "deny", strlen("deny")))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTMsgError("Unexpected token at line %d, expected either 'allow' or 'deny'",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys else if (!RTStrNCmp(pszToken, "exception_list", strlen("exception_list")))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys if (autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTMsgError("Unexpected token at line %d, expected a username",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys else if (!RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Skip , */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerPeek(pCfgTokenizer, &pszToken);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, ",");
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys else if (!autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Treat as 'username = <base delay in seconds>. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszDelay,
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys && !RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTMsgError("Unexpected token at line %d, expected a number",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTMsgError("Unexpected token at line %d, expected a username",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic DECLCALLBACK(bool) autostartVMCmp(const AUTOSTARTVM &vm1, const AUTOSTARTVM &vm2)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Main routine for the autostart daemon.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * @returns exit status code.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllysstatic RTEXITCODE autostartMain(uint32_t uStartupDelay)
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys serviceLogVerbose(("Delay starting for %d seconds ...\n", uStartupDelay));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Build a list of all VMs we need to autostart first, apply the overrides
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * from the configuration and start the VMs afterwards.
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys HRESULT rc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys * Iterate through the collection
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartEnabled)(&fAutostart));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostartVM.strId.asOutParam()));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartDelay)(&autostartVM.uStartupDelay));
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys /* Sort by startup delay and apply base override. */
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys serviceLogVerbose(("Delay starting of the next VMs for %d seconds ...\n",
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys RTThreadSleep(((*it).uStartupDelay - uDelayCurr) * 1000);
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys CHECK_ERROR_BREAK(machine, LaunchVMProcess(g_pSession, Bstr("headless").raw(),
99ebb4ca412cb0a19d77a3899a87c055b9c30fa8wyllys serviceLogVerbose(("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw()));
return rcExit;
static void displayHeader()
#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
static int autostartSetup()
return VERR_COM_OBJECT_NOT_FOUND;
return VINF_SUCCESS;
static void autostartShutdown()
bool fQuiet = false;
bool fStart = false;
bool fStop = false;
g_fVerbose = true;
#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
g_fDaemonize = true;
fQuiet = true;
fStart = true;
fStop = true;
if (!pszConfigFile)
if (!fQuiet)
bool fAllowed = false;
return RTEXITCODE_FAILURE;
if (!fAllowed)
#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
if (g_fDaemonize)
using namespace com;
# ifdef VBOX_WITH_XPCOM
return RTEXITCODE_FAILURE;
return RTEXITCODE_FAILURE;
return rcExit;