summaryrefslogtreecommitdiff
path: root/src/VBox/Installer/linux/install_service
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-03-26 19:21:20 +0000
committer <>2014-05-08 15:03:54 +0000
commitfb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch)
treec2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Installer/linux/install_service
parent58ed4748338f9466599adfc8a9171280ed99e23f (diff)
downloadVirtualBox-master.tar.gz
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Installer/linux/install_service')
-rw-r--r--src/VBox/Installer/linux/install_service/Makefile.kmk49
-rw-r--r--src/VBox/Installer/linux/install_service/generate_service_file.cpp912
-rwxr-xr-xsrc/VBox/Installer/linux/install_service/init_template.sh317
-rwxr-xr-xsrc/VBox/Installer/linux/install_service/install_service.sh209
4 files changed, 1487 insertions, 0 deletions
diff --git a/src/VBox/Installer/linux/install_service/Makefile.kmk b/src/VBox/Installer/linux/install_service/Makefile.kmk
new file mode 100644
index 00000000..812a75ea
--- /dev/null
+++ b/src/VBox/Installer/linux/install_service/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Linux installer init file generator.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# generate_service_file
+#
+PROGRAMS.linux += generate_service_file
+
+generate_service_file_TEMPLATE = VBOXR3EXE
+generate_service_file_SOURCES = generate_service_file.cpp
+generate_service_file_INST = $(INST_BIN)helpers/
+generate_service_file_LIBS = $(LIB_RUNTIME)
+ifdef VBOX_WITH_RUNPATH
+ generate_service_file_LDFLAGS = '$(VBOX_GCC_RPATH_OPT)$(VBOX_WITH_RUNPATH)'
+else ifdef VBOX_WITH_RELATIVE_RUNPATH
+ generate_service_file_LDFLAGS = '$(VBOX_GCC_RPATH_OPT)$(VBOX_WITH_RELATIVE_RUNPATH)/..'
+endif
+
+INSTALLS.linux += linux-install-service-bin
+linux-install-service-bin_INST = bin/scripts/
+linux-install-service-bin_MODE = a+rx,u+w
+linux-install-service-bin_SOURCES = \
+ install_service.sh=>install_service
+
+INSTALLS.linux += linux-install-service-nobin
+linux-install-service-nobin_INST = bin/scripts/
+linux-install-service-nobin_MODE = a+r,u+w
+linux-install-service-nobin_SOURCES = \
+ init_template.sh
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Installer/linux/install_service/generate_service_file.cpp b/src/VBox/Installer/linux/install_service/generate_service_file.cpp
new file mode 100644
index 00000000..2ff27573
--- /dev/null
+++ b/src/VBox/Installer/linux/install_service/generate_service_file.cpp
@@ -0,0 +1,912 @@
+/* $Id: generate_service_file.cpp $ */
+/** @file
+ * Read a service file template from standard input and output a service file
+ * to standard output generated from the template based on arguments passed to
+ * the utility. See the usage text for more information.
+ */
+
+/*
+ * 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.
+ */
+
+/**
+ * Description of the generation process.
+ *
+ * A template for the service file to be generated is fed into standard input
+ * and the service file is sent to standard output. The following
+ * substitutions are performed based on the command line parameters supplied,
+ * with all quoting appropriate to the format of the template as specified on
+ * the command line.
+ *
+ * %COMMAND% -> path to the service binary or script.
+ * %ARGUMENTS% -> the arguments to pass to the binary when starting the
+ * service.
+ * %SERVICE_NAME% -> the name of the service.
+ * %DESCRIPTION% -> the short description of the service.
+ * %STOP_COMMAND% -> path to the command used to stop the service.
+ * %STOP_ARGUMENTS% -> the arguments for the stop command
+ * %STATUS_COMMAND% -> path to the command used to determine the service
+ * status.
+ * %STATUS_ARGUMENTS% -> the arguments for the status command
+
+ * %NO_STOP_COMMAND% -> if no stop command was specified, this and all text
+ * following it on the line (including the end-of-
+ * line) will be removed, otherwise only the marker
+ * will be removed.
+ * %HAVE_STOP_COMMAND% -> like above, but text on the line will be removed
+ * if a stop command was supplied.
+ * %NO_STATUS_COMMAND% -> Analogue to %NO_STOP_COMMAND% for the status
+ * command.
+ * %HAVE_STATUS_COMMAND% -> Analogue to %HAVE_STOP_COMMAND% for the status
+ * command.
+ * %HAVE_ONESHOT% -> like above, text on the line will be removed unless
+ * --one-shot was specified on the command line.
+ * %HAVE_DAEMON% -> the same if --one-shot was not specified.
+ *
+ * %% will be replaced with a single %.
+ */
+
+#include <VBox/version.h>
+
+#include <iprt/ctype.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#ifndef READ_SIZE
+/** How much of the input we read at a time. Override to something small for
+ * testing. */
+# define READ_SIZE _1M
+#endif
+
+/* Macros for the template substitution sequences to guard against mis-types. */
+#define COMMAND "%COMMAND%"
+#define ARGUMENTS "%ARGUMENTS%"
+#define DESCRIPTION "%DESCRIPTION%"
+#define SERVICE_NAME "%SERVICE_NAME%"
+#define HAVE_ONESHOT "%HAVE_ONESHOT%"
+#define HAVE_DAEMON "%HAVE_DAEMON%"
+#define STOP_COMMAND "%STOP_COMMAND%"
+#define STOP_ARGUMENTS "%STOP_ARGUMENTS%"
+#define HAVE_STOP_COMMAND "%HAVE_STOP_COMMAND%"
+#define NO_STOP_COMMAND "%NO_STOP_COMMAND%"
+#define STATUS_COMMAND "%STATUS_COMMAND%"
+#define STATUS_ARGUMENTS "%STATUS_ARGUMENTS%"
+#define HAVE_STATUS_COMMAND "%HAVE_STATUS_COMMAND%"
+#define NO_STATUS_COMMAND "%NO_STATUS_COMMAND%"
+
+void showLogo(void)
+{
+ static bool s_fShown; /* show only once */
+
+ RTPrintf(VBOX_PRODUCT " Service File Generator Version "
+ VBOX_VERSION_STRING "\n"
+ "(C) 2012" /* "-" VBOX_C_YEAR */ " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n");
+}
+
+static void showOptions(void);
+
+void showUsage(const char *pcszArgv0)
+{
+ const char *pcszName = strrchr(pcszArgv0, '/');
+ if (!pcszName)
+ pcszName = pcszArgv0;
+ RTPrintf(
+"Usage:\n"
+"\n"
+" %s --help|-h|-?|--version|-V|--format <format> <parameters...>\n\n",
+ pcszArgv0);
+ RTPrintf(
+"Read a service file template from standard input and output a service file to\n"
+"standard output which was generated from the template based on parameters\n"
+"passed on the utility's command line. Generation is done by replacing well-\n"
+"known text sequences in the template with strings based on the parameters.\n"
+"All strings should be in UTF-8 format. Processing will stop if a sequence is\n"
+"read which cannot be replace based on the parameters supplied.\n\n");
+
+ RTPrintf(
+" --help|-h|-?\n"
+" Print this help text and exit.\n\n"
+" --version|-V\n"
+" Print version information and exit.\n\n"
+" --format <shell>\n"
+" The format of the template. Currently only \"shell\" for shell script\n"
+" is supported. This affects escaping of strings substituted.\n\n");
+ RTPrintf(
+"Parameters:\n"
+"\n");
+ RTPrintf(
+" --command <command>\n"
+" The absolute path of the executable file to be started by the service.\n"
+" No form of quoting should be used here.\n\n");
+ RTPrintf(
+" --description <description>\n"
+" A short description of the service which can also be used in sentences\n"
+" like \"<description> failed to start.\", as a single parameter. Characters\n"
+" 0 to 31 and 127 should not be used.\n\n"
+ );
+ RTPrintf(
+" --arguments <arguments>\n"
+" The arguments to pass to the executable file when it is started, as a\n"
+" single parameter. Characters \" \", \"\\\" and \"%%\" must be escaped with\n"
+" back-slashes and C string-style back-slash escapes are recognised. Some\n"
+" systemd-style \"%%\" sequences may be added at a future time.\n\n");
+ RTPrintf(
+" --service-name <name>\n"
+" Specify the name of the service. By default the base name without the\n"
+" extension of the command binary is used. Only ASCII characters 33 to 126\n"
+" should be used.\n\n");
+ RTPrintf(
+" --one-shot\n"
+" The service command is expected to do some work and exit immediately with"
+" a status indicating success or failure.\n\n"
+ );
+ RTPrintf(
+" --stop-command <command>\n"
+" The command which should be used to stop the service before sending the\n"
+" termination signal to the main process. No form of quoting should be\n"
+" used here.\n\n"
+ );
+ RTPrintf(
+" --stop-arguments <arguments>\n"
+" Arguments for the stop command. This may only be used in combination\n"
+" with \"--stop-command\". Quoting is the same as for \"--arguments\".\n\n"
+ );
+ RTPrintf(
+" --status-command <command>\n"
+" The command which should be used to determine the status of the service.\n"
+" This may not be respected by all service management systems. The command\n"
+" should return an LSB status code. No form of quoting should be used.\n\n"
+ );
+ RTPrintf(
+" --stop-arguments <arguments>\n"
+" Arguments for the status command. This may only be used in combination\n"
+" with \"--status-command\". Quoting is the same as for \"--arguments\".\n\n"
+ );
+}
+
+/** @name Template format.
+ * @{
+ */
+enum ENMFORMAT
+{
+ /** No format selected. */
+ FORMAT_NONE = 0,
+ /** Shell script format. */
+ FORMAT_SHELL
+};
+/** @} */
+
+struct SERVICEPARAMETERS
+{
+ enum ENMFORMAT enmFormat;
+ const char *pcszCommand;
+ const char *pcszArguments;
+ const char *pcszDescription;
+ const char *pcszServiceName;
+ bool fOneShot;
+ const char *pcszStopCommand;
+ const char *pcszStopArguments;
+ const char *pcszStatusCommand;
+ const char *pcszStatusArguments;
+};
+
+static bool errorIfSet(const char *pcszName, bool isSet);
+static enum ENMFORMAT getFormat(const char *pcszName, const char *pcszValue);
+static bool checkAbsoluteFilePath(const char *pcszName, const char *pcszValue);
+static bool checkPrintable(const char *pcszName, const char *pcszValue);
+static bool checkGraphic(const char *pcszName, const char *pcszValue);
+static bool createServiceFile(struct SERVICEPARAMETERS *pParameters);
+
+int main(int cArgs, char **apszArgs)
+{
+ int rc = RTR3InitExe(cArgs, &apszArgs, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ enum
+ {
+ OPTION_FORMAT = 1,
+ OPTION_COMMAND,
+ OPTION_ARGUMENTS,
+ OPTION_DESCRIPTION,
+ OPTION_SERVICE_NAME,
+ OPTION_ONE_SHOT,
+ OPTION_STOP_COMMAND,
+ OPTION_STOP_ARGUMENTS,
+ OPTION_STATUS_COMMAND,
+ OPTION_STATUS_ARGUMENTS
+ };
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--format", OPTION_FORMAT,
+ RTGETOPT_REQ_STRING },
+ { "--command", OPTION_COMMAND,
+ RTGETOPT_REQ_STRING },
+ { "--arguments", OPTION_ARGUMENTS,
+ RTGETOPT_REQ_STRING },
+ { "--description", OPTION_DESCRIPTION,
+ RTGETOPT_REQ_STRING },
+ { "--service-name", OPTION_SERVICE_NAME,
+ RTGETOPT_REQ_STRING },
+ { "--one-shot", OPTION_ONE_SHOT,
+ RTGETOPT_REQ_NOTHING },
+ { "--stop-command", OPTION_STOP_COMMAND,
+ RTGETOPT_REQ_STRING },
+ { "--stop-arguments", OPTION_STOP_ARGUMENTS,
+ RTGETOPT_REQ_STRING },
+ { "--status-command", OPTION_STATUS_COMMAND,
+ RTGETOPT_REQ_STRING },
+ { "--status-arguments", OPTION_STATUS_ARGUMENTS,
+ RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ struct SERVICEPARAMETERS Parameters = { FORMAT_NONE };
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, cArgs, apszArgs, s_aOptions,
+ RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'h':
+ showUsage(apszArgs[0]);
+ return RTEXITCODE_SUCCESS;
+ break;
+
+ case 'V':
+ showLogo();
+ return RTEXITCODE_SUCCESS;
+ break;
+
+ case OPTION_FORMAT:
+ if (errorIfSet("--format",
+ Parameters.enmFormat != FORMAT_NONE))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.enmFormat
+ = getFormat("--format", ValueUnion.psz);
+ if (Parameters.enmFormat == FORMAT_NONE)
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_COMMAND:
+ if (errorIfSet("--command", Parameters.pcszCommand))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.pcszCommand = ValueUnion.psz;
+ if (!checkAbsoluteFilePath("--command",
+ Parameters.pcszCommand))
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_ARGUMENTS:
+ if (errorIfSet("--arguments",
+ Parameters.pcszArguments))
+ return(RTEXITCODE_SYNTAX);
+ /* Quoting will be checked while writing out the string. */
+ Parameters.pcszArguments = ValueUnion.psz;
+ break;
+
+ case OPTION_DESCRIPTION:
+ if (errorIfSet("--description",
+ Parameters.pcszDescription))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.pcszDescription = ValueUnion.psz;
+ if (!checkPrintable("--description",
+ Parameters.pcszDescription))
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_SERVICE_NAME:
+ if (errorIfSet("--service-name",
+ Parameters.pcszServiceName))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.pcszServiceName = ValueUnion.psz;
+ if (!checkGraphic("--service-name",
+ Parameters.pcszServiceName))
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_ONE_SHOT:
+ Parameters.fOneShot = true;
+ break;
+
+ case OPTION_STOP_COMMAND:
+ if (errorIfSet("--stop-command",
+ Parameters.pcszStopCommand))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.pcszStopCommand = ValueUnion.psz;
+ if (!checkAbsoluteFilePath("--stop-command",
+ Parameters.pcszStopCommand))
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_STOP_ARGUMENTS:
+ if (errorIfSet("--stop-arguments",
+ Parameters.pcszStopArguments))
+ return(RTEXITCODE_SYNTAX);
+ /* Quoting will be checked while writing out the string. */
+ Parameters.pcszStopArguments = ValueUnion.psz;
+ break;
+
+ case OPTION_STATUS_COMMAND:
+ if (errorIfSet("--status-command",
+ Parameters.pcszStatusCommand))
+ return(RTEXITCODE_SYNTAX);
+ Parameters.pcszStatusCommand = ValueUnion.psz;
+ if (!checkAbsoluteFilePath("--status-command",
+ Parameters.pcszStatusCommand))
+ return(RTEXITCODE_SYNTAX);
+ break;
+
+ case OPTION_STATUS_ARGUMENTS:
+ if (errorIfSet("--status-arguments",
+ Parameters.pcszStatusArguments))
+ return(RTEXITCODE_SYNTAX);
+ /* Quoting will be checked while writing out the string. */
+ Parameters.pcszStatusArguments = ValueUnion.psz;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (Parameters.enmFormat == FORMAT_NONE)
+ {
+ RTStrmPrintf(g_pStdErr, "--format must be specified.\n");
+ return(RTEXITCODE_SYNTAX);
+ }
+ if (Parameters.pcszArguments && !Parameters.pcszCommand)
+ {
+ RTStrmPrintf(g_pStdErr, "--arguments requires --command to be specified.\n");
+ return(RTEXITCODE_SYNTAX);
+ }
+ if (Parameters.pcszStopArguments && !Parameters.pcszStopCommand)
+ {
+ RTStrmPrintf(g_pStdErr, "--stop-arguments requires --stop-command to be specified.\n");
+ return(RTEXITCODE_SYNTAX);
+ }
+ if (Parameters.pcszStatusArguments && !Parameters.pcszStatusCommand)
+ {
+ RTStrmPrintf(g_pStdErr, "--status-arguments requires --status-command to be specified.\n");
+ return(RTEXITCODE_SYNTAX);
+ }
+ return createServiceFile(&Parameters)
+ ? RTEXITCODE_SUCCESS
+ : RTEXITCODE_FAILURE;
+}
+
+/** Print an error and return true if an option is already set. */
+bool errorIfSet(const char *pcszName, bool isSet)
+{
+ if (isSet)
+ RTStrmPrintf(g_pStdErr, "%s may only be specified once.\n", pcszName);
+ return isSet;
+}
+
+/** Match the string to a known format and return that (or "none" and print an
+ * error). */
+enum ENMFORMAT getFormat(const char *pcszName, const char *pcszValue)
+{
+ if (!strcmp(pcszValue, "shell"))
+ return FORMAT_SHELL;
+ RTStrmPrintf(g_pStdErr, "%s: unknown format %s.\n", pcszName, pcszValue);
+ return FORMAT_NONE;
+}
+
+/** Check that the string is an absolute path to a file or print an error. */
+bool checkAbsoluteFilePath(const char *pcszName, const char *pcszValue)
+{
+ if (RTPathFilename(pcszValue) && RTPathStartsWithRoot(pcszValue))
+ return true;
+ RTStrmPrintf(g_pStdErr, "%s: %s must be an absolute path of a file.\n", pcszName, pcszValue);
+ return false;
+}
+
+/** Check that the string does not contain any non-printable characters. */
+bool checkPrintable(const char *pcszName, const char *pcszValue)
+{
+ const char *pcch = pcszValue;
+ for (; *pcch; ++pcch)
+ {
+ if (!RT_C_IS_PRINT(*pcch))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: invalid character after \"%.*s\".\n",
+ pcszName, pcch - pcszValue, pcszValue);
+ return false;
+ }
+ }
+ return true;
+}
+
+/** Check that the string does not contain any non-graphic characters. */
+static bool checkGraphic(const char *pcszName, const char *pcszValue)
+{
+ const char *pcch = pcszValue;
+ for (; *pcch; ++pcch)
+ {
+ if (!RT_C_IS_GRAPH(*pcch))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: invalid character after \"%.*s\".\n",
+ pcszName, pcch - pcszValue, pcszValue);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool createServiceFileCore(char **ppachTemplate,
+ struct SERVICEPARAMETERS
+ *pParamters);
+
+/**
+ * Read standard input and write it to standard output, doing all substitutions
+ * as per the usage documentation.
+ * @note This is a wrapper around the actual function to simplify resource
+ * allocation without requiring a single point of exit.
+ */
+bool createServiceFile(struct SERVICEPARAMETERS *pParameters)
+{
+ char *pachTemplate = NULL;
+ bool rc = createServiceFileCore(&pachTemplate, pParameters);
+ RTMemFree(pachTemplate);
+ return rc;
+}
+
+static bool getSequence(const char *pach, size_t cch, size_t *pcchRead,
+ const char *pcszSequence, size_t cchSequence);
+static bool writeCommand(enum ENMFORMAT enmFormat, const char *pcszCommand);
+static bool writeQuoted(enum ENMFORMAT enmFormat, const char *pcszQuoted);
+static bool writePrintableString(enum ENMFORMAT enmFormat,
+ const char *pcszString);
+static void skipLine(const char *pach, size_t cch, size_t *pcchRead);
+
+/** The actual implemenation code for @a createServiceFile. */
+bool createServiceFileCore(char **ppachTemplate,
+ struct SERVICEPARAMETERS *pParameters)
+{
+ /* The size of the template data we have read. */
+ size_t cchTemplate = 0;
+ /* The size of the buffer we have allocated. */
+ size_t cbBuffer = 0;
+ /* How much of the template data we have written out. */
+ size_t cchWritten = 0;
+ int rc = VINF_SUCCESS;
+ /* First of all read in the file. */
+ while (rc != VINF_EOF)
+ {
+ size_t cchRead;
+
+ if (cchTemplate == cbBuffer)
+ {
+ cbBuffer += READ_SIZE;
+ *ppachTemplate = (char *)RTMemRealloc((void *)*ppachTemplate,
+ cbBuffer);
+ }
+ if (!*ppachTemplate)
+ {
+ RTStrmPrintf(g_pStdErr, "Out of memory.\n");
+ return false;
+ }
+ rc = RTStrmReadEx(g_pStdIn, *ppachTemplate + cchTemplate,
+ cbBuffer - cchTemplate, &cchRead);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Error reading input: %Rrc\n", rc);
+ return false;
+ }
+ if (!cchRead)
+ rc = VINF_EOF;
+ cchTemplate += cchRead;
+ }
+ while (true)
+ {
+ /* Find the next '%' character if any and write out up to there (or the
+ * end if there is no '%'). */
+ char *pchNext = (char *) memchr((void *)(*ppachTemplate + cchWritten),
+ '%', cchTemplate - cchWritten);
+ size_t cchToWrite = pchNext
+ ? pchNext - *ppachTemplate - cchWritten
+ : cchTemplate - cchWritten;
+ rc = RTStrmWrite(g_pStdOut, *ppachTemplate + cchWritten, cchToWrite);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc);
+ return false;
+ }
+ cchWritten += cchToWrite;
+ if (!pchNext)
+ break;
+ /* And substitute any of our well-known strings. We favour code
+ * readability over efficiency here. */
+ if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ COMMAND, sizeof(COMMAND) - 1))
+ {
+ if (!pParameters->pcszCommand)
+ {
+ RTStrmPrintf(g_pStdErr, "--command not specified.\n");
+ return false;
+ }
+ if (!writeCommand(pParameters->enmFormat,
+ pParameters->pcszCommand))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ ARGUMENTS, sizeof(ARGUMENTS) - 1))
+ {
+ if ( pParameters->pcszArguments
+ && !writeQuoted(pParameters->enmFormat,
+ pParameters->pcszArguments))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ DESCRIPTION, sizeof(DESCRIPTION) - 1))
+ {
+ if (!pParameters->pcszDescription)
+ {
+ RTStrmPrintf(g_pStdErr, "--description not specified.\n");
+ return false;
+ }
+ if (!writePrintableString(pParameters->enmFormat,
+ pParameters->pcszDescription))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ SERVICE_NAME, sizeof(SERVICE_NAME) - 1))
+ {
+ if ( !pParameters->pcszCommand
+ && !pParameters->pcszServiceName)
+ {
+ RTStrmPrintf(g_pStdErr, "Neither --command nor --service-name specified.\n");
+ return false;
+ }
+ if (pParameters->pcszServiceName)
+ {
+ if (!writePrintableString(pParameters->enmFormat,
+ pParameters->pcszServiceName))
+ return false;
+ }
+ else
+ {
+ const char *pcszFileName =
+ RTPathFilename(pParameters->pcszCommand);
+ const char *pcszExtension =
+ RTPathExt(pParameters->pcszCommand);
+ char *pszName = RTStrDupN(pcszFileName,
+ pcszExtension
+ ? pcszExtension - pcszFileName
+ : RTPATH_MAX);
+ bool fRc;
+ if (!pszName)
+ {
+ RTStrmPrintf(g_pStdErr, "Out of memory.\n");
+ return false;
+ }
+ fRc = writePrintableString(pParameters->enmFormat,
+ pszName);
+ RTStrFree(pszName);
+ if (!fRc)
+ return false;
+ }
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ HAVE_ONESHOT, sizeof(HAVE_ONESHOT) - 1))
+ {
+ if (!pParameters->fOneShot)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ HAVE_DAEMON, sizeof(HAVE_DAEMON) - 1))
+ {
+ if (pParameters->fOneShot)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ STOP_COMMAND, sizeof(STOP_COMMAND) - 1))
+ {
+ if ( pParameters->pcszStopCommand
+ && !writeCommand(pParameters->enmFormat,
+ pParameters->pcszStopCommand))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ STOP_ARGUMENTS, sizeof(STOP_ARGUMENTS) - 1))
+ {
+ if ( pParameters->pcszStopArguments
+ && !writeQuoted(pParameters->enmFormat,
+ pParameters->pcszStopArguments))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ HAVE_STOP_COMMAND, sizeof(HAVE_STOP_COMMAND) - 1))
+ {
+ if (!pParameters->pcszStopCommand)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ NO_STOP_COMMAND, sizeof(NO_STOP_COMMAND) - 1))
+ {
+ if (pParameters->pcszStopCommand)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ STATUS_COMMAND, sizeof(STATUS_COMMAND) - 1))
+ {
+ if ( pParameters->pcszStatusCommand
+ && !writeCommand(pParameters->enmFormat,
+ pParameters->pcszStatusCommand))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ STATUS_ARGUMENTS, sizeof(STATUS_ARGUMENTS) - 1))
+ {
+ if ( pParameters->pcszStatusArguments
+ && !writeQuoted(pParameters->enmFormat,
+ pParameters->pcszStatusArguments))
+ return false;
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ HAVE_STATUS_COMMAND,
+ sizeof(HAVE_STATUS_COMMAND) - 1))
+ {
+ if (!pParameters->pcszStatusCommand)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ NO_STATUS_COMMAND, sizeof(NO_STATUS_COMMAND) - 1))
+ {
+ if (pParameters->pcszStatusCommand)
+ skipLine(*ppachTemplate, cchTemplate, &cchWritten);
+ }
+ else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten,
+ "%%", 2))
+ {
+ rc = RTStrmPutCh(g_pStdOut, '%');
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc);
+ return false;
+ }
+ }
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Unknown substitution sequence in input at \"%.*s\"\n",
+ RT_MIN(16, cchTemplate - cchWritten),
+ *ppachTemplate + cchWritten);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool getSequence(const char *pach, size_t cch, size_t *pcchRead,
+ const char *pcszSequence, size_t cchSequence)
+{
+ if ( cch - *pcchRead >= cchSequence
+ && !RTStrNCmp(pach + *pcchRead, pcszSequence, cchSequence))
+ {
+ *pcchRead += cchSequence;
+ return true;
+ }
+ return false;
+}
+
+/** Write a character to standard output and print an error and return false on
+ * failure. */
+bool outputCharacter(char ch)
+{
+ int rc = RTStrmWrite(g_pStdOut, &ch, 1);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc);
+ return false;
+ }
+ return true;
+}
+
+/** Write a string to standard output and print an error and return false on
+ * failure. */
+bool outputString(const char *pcsz)
+{
+ int rc = RTStrmPutStr(g_pStdOut, pcsz);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc);
+ return false;
+ }
+ return true;
+}
+
+/** Write a character to standard output, adding any escaping needed for the
+ * format being written. */
+static bool escapeAndOutputCharacter(enum ENMFORMAT enmFormat, char ch)
+{
+ if (enmFormat == FORMAT_SHELL)
+ {
+ if (ch == '\'')
+ return outputString("\'\\\'\'");
+ return outputCharacter(ch);
+ }
+ RTStrmPrintf(g_pStdErr, "Error: unknown template format.\n");
+ return false;
+}
+
+/** Write a character to standard output, adding any escaping needed for the
+ * format being written. */
+static bool outputArgumentSeparator(enum ENMFORMAT enmFormat)
+{
+ if (enmFormat == FORMAT_SHELL)
+ return outputString("\' \'");
+ RTStrmPrintf(g_pStdErr, "Error: unknown template format.\n");
+ return false;
+}
+
+bool writeCommand(enum ENMFORMAT enmFormat, const char *pcszCommand)
+{
+ if (enmFormat == FORMAT_SHELL)
+ if (!outputCharacter('\''))
+ return false;
+ for (; *pcszCommand; ++pcszCommand)
+ if (enmFormat == FORMAT_SHELL)
+ {
+ if (*pcszCommand == '\'')
+ {
+ if (!outputString("\'\\\'\'"))
+ return false;
+ }
+ else if (!outputCharacter(*pcszCommand))
+ return false;
+ }
+ if (enmFormat == FORMAT_SHELL)
+ if (!outputCharacter('\''))
+ return false;
+ return true;
+}
+
+const char aachEscapes[][2] =
+{
+ { 'a', '\a' }, { 'b', '\b' }, { 'f', '\f' }, { 'n', '\n' }, { 'r', '\r' },
+ { 't', '\t' }, { 'v', '\v' }, { 0, 0 }
+};
+
+bool writeQuoted(enum ENMFORMAT enmFormat, const char *pcszQuoted)
+{
+ /* Was the last character seen a back slash? */
+ bool fEscaped = false;
+ /* Was the last character seen an argument separator (an unescaped space)?
+ */
+ bool fNextArgument = false;
+
+ if (enmFormat == FORMAT_SHELL)
+ if (!outputCharacter('\''))
+ return false;
+ for (; *pcszQuoted; ++pcszQuoted)
+ {
+ if (fEscaped)
+ {
+ bool fRc = true;
+ const char (*pachEscapes)[2];
+ fEscaped = false;
+ /* One-letter escapes. */
+ for (pachEscapes = aachEscapes; (*pachEscapes)[0]; ++pachEscapes)
+ if (*pcszQuoted == (*pachEscapes)[0])
+ {
+ if (!escapeAndOutputCharacter(enmFormat, (*pachEscapes)[1]))
+ return false;
+ break;
+ }
+ if ((*pachEscapes)[0])
+ continue;
+ /* Octal. */
+ if (*pcszQuoted >= '0' && *pcszQuoted <= '7')
+ {
+ uint8_t cNum;
+ char *pchNext;
+ char achDigits[4];
+ int rc;
+ RTStrCopy(achDigits, sizeof(achDigits), pcszQuoted);
+ rc = RTStrToUInt8Ex(achDigits, &pchNext, 8, &cNum);
+ if (rc == VWRN_NUMBER_TOO_BIG)
+ {
+ RTStrmPrintf(g_pStdErr, "Invalid octal sequence at \"%.16s\"\n",
+ pcszQuoted - 1);
+ return false;
+ }
+ if (!escapeAndOutputCharacter(enmFormat, cNum))
+ return false;
+ pcszQuoted += pchNext - achDigits - 1;
+ continue;
+ }
+ /* Hexadecimal. */
+ if (*pcszQuoted == 'x')
+ {
+ uint8_t cNum;
+ char *pchNext;
+ char achDigits[3];
+ int rc;
+ RTStrCopy(achDigits, sizeof(achDigits), pcszQuoted + 1);
+ rc = RTStrToUInt8Ex(achDigits, &pchNext, 16, &cNum);
+ if ( rc == VWRN_NUMBER_TOO_BIG
+ || rc == VWRN_NEGATIVE_UNSIGNED
+ || RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "Invalid hexadecimal sequence at \"%.16s\"\n",
+ pcszQuoted - 1);
+ return false;
+ }
+ if (!escapeAndOutputCharacter(enmFormat, cNum))
+ return false;
+ pcszQuoted += pchNext - achDigits;
+ continue;
+ }
+ /* Output anything else non-zero as is. */
+ if (*pcszQuoted)
+ {
+ if (!escapeAndOutputCharacter(enmFormat, *pcszQuoted))
+ return false;
+ continue;
+ }
+ RTStrmPrintf(g_pStdErr, "Trailing back slash in argument.\n");
+ return false;
+ }
+ /* Argument separator. */
+ if (*pcszQuoted == ' ')
+ {
+ if (!fNextArgument && !outputArgumentSeparator(enmFormat))
+ return false;
+ fNextArgument = true;
+ continue;
+ }
+ else
+ fNextArgument = false;
+ /* Start of escape sequence. */
+ if (*pcszQuoted == '\\')
+ {
+ fEscaped = true;
+ continue;
+ }
+ /* Anything else. */
+ if (!outputCharacter(*pcszQuoted))
+ return false;
+ }
+ if (enmFormat == FORMAT_SHELL)
+ if (!outputCharacter('\''))
+ return false;
+ return true;
+}
+
+bool writePrintableString(enum ENMFORMAT enmFormat, const char *pcszString)
+{
+ if (enmFormat == FORMAT_SHELL)
+ return outputString(pcszString);
+ RTStrmPrintf(g_pStdErr, "Error: unknown template format.\n");
+ return false;
+}
+
+void skipLine(const char *pach, size_t cch, size_t *pcchRead)
+{
+ while ( *pcchRead < cch
+ && (pach)[*pcchRead] != '\n'
+ && (pach)[*pcchRead] != '\r')
+ ++*pcchRead;
+ while ( *pcchRead < cch
+ && ( (pach)[*pcchRead] == '\n'
+ || (pach)[*pcchRead] == '\r'))
+ ++*pcchRead;
+}
diff --git a/src/VBox/Installer/linux/install_service/init_template.sh b/src/VBox/Installer/linux/install_service/init_template.sh
new file mode 100755
index 00000000..ed9c9a48
--- /dev/null
+++ b/src/VBox/Installer/linux/install_service/init_template.sh
@@ -0,0 +1,317 @@
+#!/bin/sh
+#
+# VirtualBox generic init script.
+#
+# 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.
+#
+
+### BEGIN INIT INFO
+# Required-Start: $local_fs
+# Should-Start: $syslog
+# Required-Stop: $local_fs
+# Should-Stop: $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: %DESCRIPTION%
+### END INIT INFO
+
+## @todo We should really replace the daemon starting, stopping and checking
+# code with a tool of our own written in C, which we could always use
+# instead of the LSB functions.
+
+cr="
+"
+tab=" "
+IFS=" ${cr}${tab}"
+'unset' -f unalias
+'unalias' -a
+unset -f command
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
+
+## A generic service script which can be used, after substituting some place-
+# holders with service-specific values, to run most services on LSB, System V
+# or BSD-compatible service management systems. As we control both the service
+# code and the init script we try to push as much as possible of the logic into
+# the service and out of the very system-dependent service configuration
+# scripts and files. See the help text of the "install_service.sh" helper
+# script for more details.
+#
+# Furthermore, to simplify deployment, we will install all init scripts using
+# this generic template manually during the post install phase or at run time
+# using LSB functions if they are available (as they should be on most common
+# modern distributions) or manually placing the file in the appropriate
+# directory and creating symbolic links on System V or writing to rc.local on
+# BSD-compatible systems. Systems requiring different treatment will be added
+# here when we add support for them, but we will try to keep everything as
+# generic as we can.
+#
+# In general, we try to behave as natively as we reasonably can on the most
+# important target systems we support and to work well enough on as many others
+# as possible, but in particular without trying to look perfectly native.
+#
+# See the inline documentation in the code for generate_service_file for
+# details of the generation process.
+
+## Time out in seconds when shutting down the service.
+SHUT_DOWN_TIME_OUT=5
+## If this is set to an empty value then the LSB init functions will not be
+# used. This is intended for testing the fallback commands.
+LSB_FUNCTIONS="/lib/lsb/init-functions"
+
+# Silently exit if the package was uninstalled but not purged.
+test -r %COMMAND% || exit 0
+
+## The function definition at the start of every non-trivial shell script!
+abort()
+{
+ log_failure_msg "$*"
+ exit 1
+}
+
+## Exit successfully.
+do_success()
+{
+ log_success_msg "%DESCRIPTION% successfully started."
+ exit 0
+}
+
+## Set the error message.
+set_error()
+{
+ test -z "${error}" && error="${1}"
+}
+
+# Gentoo/OpenRC perculiarity.
+if test "x${0}" = "x/sbin/rc" || test "x${0}" = "xrc"; then
+ shift
+fi
+
+# Process arguments.
+action=""
+error=""
+prefix="/var"
+while test x"${#}" != "x0"; do
+ case "${1}" in
+ --lsb-functions)
+ test x"${#}" = "x1" &&
+ set_error "${1}: missing argument."
+ LSB_FUNCTIONS="${2}"
+ shift 2;;
+ --prefix)
+ test x"${#}" = "x1" &&
+ set_error "${1}: missing argument."
+ prefix="${2}"
+ shift 2;;
+ --help)
+ cat << EOF
+Usage:
+
+ ${0} {start|stop|restart|status} [<options>]
+
+ start|stop|restart|status
+ Start/stop/restart/report status for the service.
+
+Options:
+
+ --lsb-functions <script>
+ Take the standard LSB init functions from <script> instead of from the
+ normal location, or use our own versions if <script> is an empty string.
+
+ --prefix <folder>
+ Use the folder <folder> for storing variable data instead of "/var". The
+ child folder "run" must exist.
+EOF
+ exit 0;;
+ start|stop|restart|force-reload|condrestart|try-restart|reload|status)
+ test -z "${action}" ||
+ set_error "More than one action requested."
+ action="${1}"
+ shift;;
+ *)
+ set_error "Unknown option \"${1}\". Try \"${0} --help\" for more information."
+ shift;;
+ esac
+done
+
+## Set Redhat and Fedora lock directory
+LOCK_FOLDER="${prefix}/lock/subsys/"
+LOCK_FILE="${LOCK_FOLDER}/%SERVICE_NAME%"
+
+# Use LSB functions if available. Success and failure messages default to just
+# "echo" if the LSB functions are not available, so call these functions with
+# messages which clearly read as success or failure messages.
+test -n "${LSB_FUNCTIONS}" && test -f "${LSB_FUNCTIONS}" &&
+ . "${LSB_FUNCTIONS}"
+
+type log_success_msg >/dev/null 2>&1 ||
+ log_success_msg()
+ {
+ cat << EOF
+${*}
+EOF
+ }
+
+type log_failure_msg >/dev/null 2>&1 ||
+ log_failure_msg()
+ {
+ cat << EOF
+${*}
+EOF
+ }
+
+## Get the LSB standard PID-file name for a binary.
+pidfilename()
+{
+ echo "${prefix}/run/${1##*/}.pid"
+}
+
+## Get the PID-file for a process like the LSB functions do ( "-p" or by name).
+pidfileofproc()
+{
+ if test x"${1}" = "x-p"; then
+ echo "${2}"
+ else
+ pidfilename "${1}"
+ fi
+}
+
+## Read the pids from an LSB PID-file, checking that they are positive numbers.
+pidsfromfile()
+{
+ pids=""
+ test -r "${1}" &&
+ read -r pids < "${1}" 2>/dev/null
+ for i in $pids; do
+ test 1 -le "${i}" || return 1
+ done
+ echo "${pids}"
+}
+
+## Check whether the binary $1 with the pids $2... is running.
+procrunning()
+{
+ binary="${1}"
+ shift
+ case "`ps -p "${@}" -f 2>/dev/null`" in *"${binary}"*)
+ return 0;;
+ esac
+ return 1
+}
+
+# We prefer our own implementations of pidofproc and killproc over falling back
+# to distribution ones with unknown quirks.
+# type pidofproc >/dev/null 2>&1 ||
+ pidofproc()
+ {
+ pidfile="`pidfileofproc "${@}"`"
+ test "x${1}" = "x-p" && shift 2
+ pids="`pidsfromfile "${pidfile}"`"
+ procrunning "${1}" ${pids} && echo "${pids}"
+ }
+
+# type killproc >/dev/null 2>&1 ||
+ killproc()
+ {
+ pidfile="`pidfileofproc "${@}"`"
+ test "x${1}" = "x-p" && shift 2
+ pids="`pidsfromfile "${pidfile}"`"
+ if test -n "${2}"; then
+ procrunning "${1}" ${pids} || return 1
+ kill "${2}" ${pids}
+ return 0
+ else
+ rm -f "${pidfile}"
+ procrunning "${1}" ${pids} || return 0
+ kill "${pids}"
+ # Short busy wait for the process to terminate.
+ stamp="`times`"
+ while test x"${stamp}" = x"`times`"; do
+ procrunning "${1}" ${pids} || return 0
+ done
+ # Slow sleeping wait if it is still running.
+ for high in "" 1 2 3 4 5 6 7 8 9; do
+ for time in ${high}0 ${high}1 ${high}2 ${high}3 ${high}4 ${high}5 ${high}6 ${high}7 ${high}8 ${high}9; do
+ sleep 1
+ procrunning "${1}" ${pids} || return 0
+ if test "${time}" = "${SHUT_DOWN_TIME_OUT}"; then
+ kill -9 "${pid}"
+ return 0
+ fi
+ done
+ done
+ return 0
+ fi
+ }
+
+start()
+{
+ test -d "${LOCK_FOLDER}" && touch "${LOCK_FILE}"
+ test -n "`pidofproc %COMMAND%`" && exit 0
+%HAVE_DAEMON% %COMMAND% %ARGUMENTS% >/dev/null 2>&1 &
+%HAVE_DAEMON% pid="$!"
+%HAVE_DAEMON% pidfile="`pidfilename %COMMAND%`"
+%HAVE_DAEMON% echo "${pid}" > "${pidfile}"
+%HAVE_ONESHOT% %COMMAND% %ARGUMENTS% >/dev/null 2>&1 || abort "%DESCRIPTION% failed to start!"
+ do_success
+}
+
+stop()
+{
+%HAVE_STOP_COMMAND% %STOP_COMMAND% %STOP_ARGUMENTS% || abort "%DESCRIPTION% failed to stop!"
+%HAVE_DAEMON% killproc %COMMAND% || abort "%DESCRIPTION% failed to stop!"
+ rm -f "${LOCK_FILE}"
+ log_success_msg "%DESCRIPTION% successfully stopped."
+ return 0
+}
+
+status()
+{
+%HAVE_STATUS_COMMAND% %STATUS_COMMAND% %STATUS_ARGUMENTS%
+%HAVE_STATUS_COMMAND% exit
+%NO_STATUS_COMMAND% pid="`pidofproc %COMMAND%`"
+%NO_STATUS_COMMAND% test -n "${pid}" &&
+%NO_STATUS_COMMAND% {
+%NO_STATUS_COMMAND% echo "%SERVICE_NAME% running, process ${pid}"
+%NO_STATUS_COMMAND% exit 0
+%NO_STATUS_COMMAND% }
+%NO_STATUS_COMMAND% test -f "`pidfilename %COMMAND%`" &&
+%NO_STATUS_COMMAND% {
+%NO_STATUS_COMMAND% echo "%SERVICE_NAME% not running but PID-file present."
+%NO_STATUS_COMMAND% exit 1
+%NO_STATUS_COMMAND% }
+%NO_STATUS_COMMAND% test -f "${LOCK_FILE}" &&
+%NO_STATUS_COMMAND% {
+%NO_STATUS_COMMAND% echo "%SERVICE_NAME% not running but lock file present."
+%NO_STATUS_COMMAND% exit 2
+%NO_STATUS_COMMAND% }
+%NO_STATUS_COMMAND% echo "%SERVICE_NAME% not running."
+%NO_STATUS_COMMAND% exit 3
+}
+
+test -z "${error}" || abort "${error}"
+
+case "${action}" in
+start)
+ start;;
+stop)
+ stop;;
+restart|force-reload)
+ start
+ stop;;
+condrestart|try-restart)
+ status || exit 0
+ stop
+ start;;
+reload)
+ ;;
+status)
+ status;;
+esac
diff --git a/src/VBox/Installer/linux/install_service/install_service.sh b/src/VBox/Installer/linux/install_service/install_service.sh
new file mode 100755
index 00000000..89311043
--- /dev/null
+++ b/src/VBox/Installer/linux/install_service/install_service.sh
@@ -0,0 +1,209 @@
+#!/bin/sh
+
+#
+# Script to install services within a VirtualBox installation.
+#
+# 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.
+#
+
+# Clean up before we start.
+cr="
+"
+tab=" "
+IFS=" ${cr}${tab}"
+'unset' -f unalias
+'unalias' -a 2>/dev/null
+'unset' -f command
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
+
+# Get the folder we are running from, as we need other files there.
+script_folder="`dirname "$0"`"
+
+## Script usage documentation.
+usage() {
+ cat << EOF
+Usage:
+
+ `basename $0` --help|--enable|--disable|--force-enable|--force-disable
+ |--remove [--prefix <prefix>]
+ -- <pass-through parameters>
+
+Create a system service which runs a command. In order to make it possible to
+do this in a simple and portable manner, we place a number of requirements on
+the command to be run:
+ - That it can be started safely even if all its dependencies are not started
+ and will sleep if necessary until it can start work. Ideally it should
+ start accepting input as early as it can, but delay handling it if
+ necessary, and delay accessing its dependencies until it actually needs
+ them.
+ - That it does not background to simplify service process management.
+ - That it can be safely shut down using SIGTERM.
+ - That if all running copies of the main process binary are stopped first the
+ service can be re-started and will do any necessary clean-up automatically.
+ - That any output which must not be lost go either to the system log or to the
+ service's private log.
+
+We currently support System V init only. This will probably soon be extended
+to BSD init, OpenRC and systemd, but probably not Upstart which currently
+requires modifying init files to disable a service. We also try to enable our
+service (if requested) in all init systems we find, as we do not know which one
+is in active use. We assume that this will not have any adverse effects.
+
+ --help|--usage
+ Print this help text and exit.
+
+ --enable|--disable|--force-enable|--force-disable
+ These actions install the service. If a version of the service was not
+ installed previously, "--enable" and "--force-enable" make it start when
+ entering normal user run-levels and "--disable" and "--force-disable"
+ prevents it from starting when entering any run-level. If a version of
+ the service was already installed previously, "--enable" and "--disable"
+ simply update it without changing when it starts; "--force-enable" and
+ "--force-disable" behave the same as when no previous version was found.
+ Only one of these options or "--remove" may be specified.
+
+ --remove
+ This action uninstalls the service. It may not be used in combination
+ with "--enable", "--disable", "--force-enable" or "--force-disable".
+
+Option:
+
+ --prefix <prefix>
+ Treat all paths as relative to <prefix> rather than /etc.
+
+Pass-through parameters will be passed through to the "generate_service_file"
+tool.
+EOF
+}
+
+## The function definition at the start of every non-trivial shell script!
+abort() {
+ ## $1 Error text to output to standard error.
+ cat >&2 << EOF
+$1
+EOF
+ exit 1
+}
+
+ACTION=""
+PREFIX="/etc/"
+SERVICE_NAME=""
+
+# Process arguments.
+while test x"${1}" != "x--"; do
+ case "${1}" in
+ "--help"|"--usage")
+ usage
+ exit 0;;
+ "--enable"|"--disable"|"--force-enable"|"--force-disable"|"--remove")
+ test -z "${ACTION}" || abort "More than one action specified."
+ ACTION="true"
+ ENABLE=""
+ INSTALL="true"
+ UPDATE=""
+ { test "${1}" = "--enable" || test "${1}" = "--disable"; } &&
+ UPDATE="true"
+ { test "${1}" = "--enable" || test "${1}" = "--force-enable"; } &&
+ ENABLE="true"
+ test "${1}" = "--remove" &&
+ INSTALL=""
+ shift;;
+ "--prefix")
+ test -z "${2}" && abort "${1}: missing argument."
+ PREFIX="${2}"
+ shift 2;;
+ *)
+ abort "Unknown option ${1}.";;
+ esac
+done
+shift
+
+# Check required options and set default values for others.
+test -z "${ACTION}" &&
+ abort "Please supply an install action."
+
+# Get the service name.
+SERVICE_NAME=`echo "%SERVICE_NAME%" |
+ "${script_folder}/../helpers/generate_service_file" --format shell "${@}"`
+test -z "${SERVICE_NAME}" &&
+ abort "Please supply a command path."
+
+# Keep track of whether we found at least one initialisation system.
+found_init=""
+
+# Find the best System V/BSD init path if any is present.
+for path in "${PREFIX}/init.d/rc.d" "${PREFIX}/init.d/" "${PREFIX}/rc.d/init.d" "${PREFIX}/rc.d"; do
+ if test -d "${path}"; then
+ # Check permissions for the init path.
+ test -w "${path}" || abort "No permission to write to \"${path}\"."
+ # And for the System V symlink directories.
+ for i in rc0.d rc1.d rc6.d rc.d/rc0.d rc.d/rc1.d rc.d/rc6.d; do
+ if test -d "${PREFIX}/${i}"; then
+ test -w "${PREFIX}/${i}" ||
+ abort "No permission to write to \"${PREFIX}/${i}\"."
+ fi
+ done
+ # And for the OpenRC symlink directories.
+ if test -d "${PREFIX}/runlevel/"; then
+ test -w "${PREFIX}/runlevel/" ||
+ abort "No permission to write to \"${PREFIX}/runlevel\"".
+ fi
+ found_init="true"
+ update=""
+ test -f "${path}/${SERVICE_NAME}" && update="${UPDATE}"
+ if test -n "${INSTALL}"; then
+ "${script_folder}/../helpers/generate_service_file" --format shell "${@}" < "${script_folder}/init_template.sh" > "${path}/${SERVICE_NAME}"
+ chmod a+x "${path}/${SERVICE_NAME}"
+ else
+ rm "${path}/${SERVICE_NAME}"
+ fi
+ # Attempt to install using both system V symlinks and OpenRC, assuming
+ # that both will not be in operation simultaneously (but may be
+ # switchable). BSD init expects the user to enable services
+ # explicitly.
+ if test -z "${update}"; then
+ # Various known combinations of sysvinit rc directories.
+ for i in "${PREFIX}"/rc*.d/[KS]??"${SERVICE_NAME}" "${PREFIX}"/rc.d/rc*.d/[KS]??"${SERVICE_NAME}"; do
+ rm -f "${i}"
+ done
+ # And OpenRC.
+ test -d "${PREFIX}/runlevel/" &&
+ for i in "/${PREFIX}/runlevel"/*/"${SERVICE_NAME}"; do
+ rm -f "${i}"
+ done
+ # Various known combinations of sysvinit rc directories.
+ if test -n "${ENABLE}"; then
+ for i in rc0.d rc1.d rc6.d rc.d/rc0.d rc.d/rc1.d rc.d/rc6.d; do
+ if test -d "${PREFIX}/${i}"; then
+ # Paranoia test first.
+ test -d "${PREFIX}/${i}/K80${SERVICE_NAME}" ||
+ ln -sf "${path}/${SERVICE_NAME}" "${PREFIX}/${i}/K80${SERVICE_NAME}"
+ fi
+ done
+ for i in rc2.d rc3.d rc4.d rc5.d rc.d/rc2.d rc.d/rc3.d rc.d/rc4.d rc.d/rc5.d; do
+ if test -d "${PREFIX}/${i}"; then
+ # Paranoia test first.
+ test -d "${PREFIX}/${i}/S20${SERVICE_NAME}" ||
+ ln -sf "${path}/${SERVICE_NAME}" "${PREFIX}/${i}/S20${SERVICE_NAME}"
+ fi
+ done
+ # And OpenRC.
+ test -d "${PREFIX}/runlevel/default" &&
+ ln -sf "${path}/${SERVICE_NAME}" "/${PREFIX}/runlevel/default/"
+ fi
+ fi
+ break
+ fi
+done
+
+test -z "${found_init}" &&
+ abort "No supported initialisation system found."
+exit 0