summaryrefslogtreecommitdiffstats
path: root/src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh')
-rwxr-xr-xsrc/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh b/src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh
new file mode 100755
index 00000000..2cfa849f
--- /dev/null
+++ b/src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh
@@ -0,0 +1,369 @@
+#!/bin/sh
+# $Id: VBoxHeadlessXOrg.sh $
+## @file
+# VirtualBox X Server auto-start service.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+
+## Start one or several X servers in the background for use with headless
+# rendering. For details, options and configuration see the usage() function
+# further down.
+#
+# I have tried to follow the best practices I could find for writing a Linux
+# service (and doing it in shell script) which should work well with
+# traditional and modern service systems using minimal init or service files.
+# In our case this boils down to:
+# * Start with a single command line, stop using one of ${EXIT_SIGNALS} below.
+# * Stopping with a signal can be done safely using the pid stored in the
+# pid-file and our (presumably unique) command name. For this reason we
+# only support running one instance of the service though.
+# * Start in the foreground. Systems without proper service control can take
+# care of the backgrounding in the init script.
+# * Clean up all sub-processes (X servers) ourselves when we are stopped
+# cleanly and don't provide any other way to clean them up automatically (in
+# case we are stopped uncleanly) as we don't know of a generic safe way to
+# do so, though some service management systems (i.e. systemd) can do so.
+# (A more thorough automatic clean-up would be possible if Xorg didn't
+# potentially have to be run as root, so that we could run all processes
+# using a service-specific user account and just terminate all processes
+# run by that user to clean up.)
+
+## Default configuration file name.
+# @note This is not very nice - /etc/default is actually Debian-specific.
+CONFIGURATION_FILE=/etc/default/virtualbox
+## The name of this script.
+SCRIPT_NAME="$0"
+## The service name.
+SERVICE_NAME="vboxheadlessxorg"
+## The service description.
+SERVICE_DESCRIPTION="Headless rendering service"
+## Signals and conditions which may be used to terminate the service.
+EXIT_SIGNALS="EXIT HUP INT QUIT ABRT TERM"
+## The default run-time data folder.
+DEFAULT_RUN_FOLDER="/var/run/${SERVICE_NAME}/"
+## The default X server configuration directory.
+DEFAULT_CONFIGURATION_FOLDER="${DEFAULT_RUN_FOLDER}/xorg.conf.d/"
+## The extra data key used to provide the list of available X server displays.
+EXTRA_DATA_KEY_DISPLAYS="HeadlessXServer/Displays"
+## The extra data key used to specify the X server authority file.
+EXTRA_DATA_KEY_AUTH="HeadlessXServer/AuthFile"
+
+## Print usage information for the service script.
+## @todo Perhaps we should support some of the configuration file options from
+# the command line. Opinions welcome.
+## @todo Possibly extract this information for the user manual.
+usage() {
+ cat << EOF
+Usage:
+
+ $(basename "${SCRIPT_NAME}") [<options>]
+
+Start one or several X servers in the background for use with headless
+rendering. We only support X.Org Server at the moment. On service start-up available graphics devices are detected and an X server configuration file is
+generated for each. We attempt to start an X server process for each
+configuration file. The process is configurable by setting values in a file as
+described below.
+
+Options:
+
+ -c|--conf-file Specify an alternative locations for the configuration
+ file. The default location is:
+ "${CONFIGURATION_FILE}"
+
+ --help|--usage Print this text.
+
+The optional configuration file should contain a series of lines of the form
+"KEY=value". It will be read in as a command shell sub-script. Here is the
+current list of possible key settings with a short explanation. Usually it
+should be sufficient to change the value of \${HEADLESS_X_ORG_USERS} and to
+leave all other settings unchanged.
+
+ HEADLESS_X_ORG_CONFIGURATION_FOLDER
+ The folder where the X server configuration files are to be created.
+
+ HEADLESS_X_ORG_LOG_FOLDER
+ The folder where log files will be saved.
+
+ HEADLESS_X_ORG_LOG_FILE
+ The main log file name.
+
+ HEADLESS_X_ORG_RUN_FOLDER
+ The folder to store run-time data in.
+
+ HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES
+ Command to execute to wait until all dependencies for the X servers are
+ available. The default command waits until the udev event queue has
+ settled. The command may return failure to signal that it has given up.
+ No arguments may be passsed.
+
+ HEADLESS_X_ORG_USERS
+ List of users who will have access to the X servers started and for whom we
+ will provide the configuration details via VirtualBox extra data. This
+ variable is only used by the commands in the default configuration
+ (\${HEADLESS_X_ORG_SERVER_PRE_COMMAND} and
+ \${HEADLESS_X_ORG_SERVER_POST_COMMAND}), and not by the service itself.
+
+ HEADLESS_X_ORG_FIRST_DISPLAY
+ The first display number which will be used for a started X server. The
+ others will use the following numbers.
+
+ HEADLESS_X_ORG_SERVER_PRE_COMMAND
+ Command to execute once to perform any set-up needed before starting the
+ X servers, such as setting up the X server authentication. The default
+ command creates an authority file for each of the users in the list
+ \${HEADLESS_X_ORG_USERS} and generates server configuration files for all
+ detected graphics cards. No arguments may be passed.
+
+ HEADLESS_X_ORG_SERVER_COMMAND
+ The default X server start-up command. It will be passed three parameters
+ - in order, the screen number to use, the path of the X.Org configuration
+ file to use and the path of the X server log file to create.
+
+ HEADLESS_X_ORG_SERVER_POST_COMMAND
+ Command to execute once the X servers have been successfully started. It
+ will be passed a single parameter which is a space-separated list of the
+ X server screen numbers. By default this stores the service configuration
+ information to VirtualBox extra data for each of the users in the list
+ from the variable HEADLESS_X_ORG_USERS: the list of displays is set to the
+ key "${EXTRA_DATA_KEY_DISPLAYS}" and the path of the authority file to
+ "${EXTRA_DATA_KEY_AUTH}".
+EOF
+}
+
+# Default configuration.
+HEADLESS_X_ORG_CONFIGURATION_FOLDER="${DEFAULT_CONFIGURATION_FOLDER}"
+HEADLESS_X_ORG_LOG_FOLDER="/var/log/${SERVICE_NAME}"
+HEADLESS_X_ORG_LOG_FILE="${SERVICE_NAME}.log"
+HEADLESS_X_ORG_RUN_FOLDER="/var/run/${SERVICE_NAME}"
+HEADLESS_X_ORG_USERS=""
+HEADLESS_X_ORG_FIRST_DISPLAY=40
+X_AUTH_FILE="${HEADLESS_X_ORG_RUN_FOLDER}/xauth"
+
+default_wait_for_prerequisites()
+{
+ udevadm settle || udevsettle # Fails if no udevadm.
+}
+HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES="default_wait_for_prerequisites"
+
+default_pre_command()
+{
+ # Create new authority file.
+ echo > "${X_AUTH_FILE}"
+ # Create the xorg.conf files.
+ mkdir -p "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}" || return 1
+ display="${HEADLESS_X_ORG_FIRST_DISPLAY}"
+ for i in /sys/bus/pci/devices/*; do
+ read class < "${i}/class"
+ case "${class}" in *03????)
+ address="${i##*/}"
+ address="${address%%:*}${address#*:}"
+ address="PCI:${address%%.*}:${address#*.}"
+ read vendor < "${i}/vendor"
+ case "${vendor}" in *10de|*10DE) # NVIDIA
+ cat > "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}/xorg.conf.${display}" << EOF
+Section "Module"
+ Load "glx"
+EndSection
+Section "Device"
+ Identifier "Device${display}"
+ Driver "nvidia"
+ Option "UseDisplayDevice" "none"
+EndSection
+Section "Screen"
+ Identifier "Screen${display}"
+ Device "Device${display}"
+EndSection
+Section "ServerLayout"
+ Identifier "Layout${display}"
+ Screen "Screen${display}"
+ Option "AllowMouseOpenFail" "true"
+ Option "AutoAddDevices" "false"
+ Option "AutoAddGPU" "false"
+ Option "AutoEnableDevices" "false"
+ Option "IsolateDevice" "${address}"
+EndSection
+EOF
+ esac
+ # Add key to the authority file.
+ key="$(dd if=/dev/urandom count=1 bs=16 2>/dev/null | od -An -x)"
+ xauth -f "${X_AUTH_FILE}" add :${display} . "${key}"
+ display=`expr ${display} + 1`
+ esac
+ done
+ # Duplicate the authority file.
+ for i in ${HEADLESS_X_ORG_USERS}; do
+ cp "${X_AUTH_FILE}" "${X_AUTH_FILE}.${i}"
+ chown "${i}" "${X_AUTH_FILE}.${i}"
+ done
+}
+HEADLESS_X_ORG_SERVER_PRE_COMMAND="default_pre_command"
+
+default_command()
+{
+ auth="${HEADLESS_X_ORG_RUN_FOLDER}/xauth"
+ # screen=$1
+ # conf_file=$2
+ # log_file=$3
+ trap "kill \${PID}; sleep 5; kill -KILL \${PID} 2>/dev/null" ${EXIT_SIGNALS}
+ Xorg :"${1}" -auth "${auth}" -config "${2}" -logverbose 0 -logfile /dev/null -verbose 7 > "${3}" 2>&1 &
+ PID="$!"
+ wait
+ exit
+}
+HEADLESS_X_ORG_SERVER_COMMAND="default_command"
+
+default_post_command()
+{
+ # screens=$1
+ for i in ${HEADLESS_X_ORG_USERS}; do
+ su ${i} -c "VBoxManage setextradata global ${EXTRA_DATA_KEY_DISPLAYS} \"${1}\""
+ su ${i} -c "VBoxManage setextradata global ${EXTRA_DATA_KEY_AUTH} \"${HEADLESS_X_ORG_RUN_FOLDER}/xauth\""
+ done
+}
+HEADLESS_X_ORG_SERVER_POST_COMMAND="default_post_command"
+
+## The function definition at the start of every non-trivial shell script!
+abort() {
+ ## $@, ... Error text to output to standard error in printf format.
+ printf "$@" >&2
+ exit 1
+}
+
+## Milder version of abort, when we can't continue because of a valid condition.
+abandon() {
+ ## $@, ... Text to output to standard error in printf format.
+ printf "$@" >&2
+ exit 0
+}
+
+abort_usage() {
+ usage >&2
+ abort "$@"
+}
+
+# Print a banner message
+banner() {
+ cat << EOF
+${VBOX_PRODUCT} VBoxHeadless X Server start-up service Version ${VBOX_VERSION_STRING}
+(C) 2005-${VBOX_C_YEAR} ${VBOX_VENDOR}
+All rights reserved.
+
+EOF
+}
+
+# Get the directory where the script is located.
+SCRIPT_FOLDER=$(dirname "${SCRIPT_NAME}")"/"
+[ -r "${SCRIPT_FOLDER}generated.sh" ] ||
+ abort "${LOG_FILE}" "Failed to find installation information.\n"
+. "${SCRIPT_FOLDER}generated.sh"
+
+# Parse our arguments.
+while [ "$#" -gt 0 ]; do
+ case $1 in
+ -c|--conf-file)
+ [ "$#" -gt 1 ] ||
+ {
+ banner
+ abort "%s requires at least one argument.\n" "$1"
+ }
+ CONFIGURATION_FILE="$2"
+ shift
+ ;;
+ --help|--usage)
+ banner
+ usage
+ exit 0
+ ;;
+ *)
+ banner
+ abort_usage "Unknown argument $1.\n"
+ ;;
+ esac
+ shift
+done
+
+[ -r "${CONFIGURATION_FILE}" ] && . "${CONFIGURATION_FILE}"
+
+# Change to the root directory so we don't hold any other open.
+cd /
+
+# If something fails here we will catch it when we create the directory.
+[ -e "${HEADLESS_X_ORG_LOG_FOLDER}" ] &&
+ [ -d "${HEADLESS_X_ORG_LOG_FOLDER}" ] &&
+ rm -rf "${HEADLESS_X_ORG_LOG_FOLDER}.old" 2> /dev/null &&
+mv "${HEADLESS_X_ORG_LOG_FOLDER}" "${HEADLESS_X_ORG_LOG_FOLDER}.old" 2> /dev/null
+mkdir -p "${HEADLESS_X_ORG_LOG_FOLDER}" 2>/dev/null ||
+{
+ banner
+ abort "Failed to create log folder \"${HEADLESS_X_ORG_LOG_FOLDER}\".\n"
+}
+mkdir -p "${HEADLESS_X_ORG_RUN_FOLDER}" 2>/dev/null ||
+{
+ banner
+ abort "Failed to create run folder \"${HEADLESS_X_ORG_RUN_FOLDER}\".\n"
+}
+exec > "${HEADLESS_X_ORG_LOG_FOLDER}/${HEADLESS_X_ORG_LOG_FILE}" 2>&1
+
+banner
+
+# Wait for our dependencies to become available.
+if [ -n "${HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES}" ]; then
+ "${HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES}" ||
+ abort "Service prerequisites not available.\n"
+fi
+
+# Do any pre-start setup.
+if [ -n "${HEADLESS_X_ORG_SERVER_PRE_COMMAND}" ]; then
+ "${HEADLESS_X_ORG_SERVER_PRE_COMMAND}" ||
+ abort "Pre-requisite failed.\n"
+fi
+
+X_SERVER_PIDS=""
+X_SERVER_SCREENS=""
+trap "kill \${X_SERVER_PIDS} 2>/dev/null" ${EXIT_SIGNALS}
+space="" # Hack to put spaces between the pids but not before or after.
+for conf_file in "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}"/*; do
+ [ x"${conf_file}" = x"${HEADLESS_X_ORG_CONFIGURATION_FOLDER}/*" ] &&
+ ! [ -e "${conf_file}" ] &&
+ abort "No configuration files found.\n"
+ filename="$(basename "${conf_file}")"
+ screen="$(expr "${filename}" : "xorg\.conf\.\(.*\)")"
+ [ 0 -le "${screen}" ] 2>/dev/null ||
+ abort "Badly formed file name \"${conf_file}\".\n"
+ log_file="${HEADLESS_X_ORG_LOG_FOLDER}/Xorg.${screen}.log"
+ "${HEADLESS_X_ORG_SERVER_COMMAND}" "${screen}" "${conf_file}" "${log_file}" &
+ X_SERVER_PIDS="${X_SERVER_PIDS}${space}$!"
+ X_SERVER_SCREENS="${X_SERVER_SCREENS}${space}${screen}"
+ space=" "
+done
+
+# Do any post-start work.
+if [ -n "${HEADLESS_X_ORG_SERVER_POST_COMMAND}" ]; then
+ "${HEADLESS_X_ORG_SERVER_POST_COMMAND}" "${X_SERVER_SCREENS}" ||
+ abort "Post-command failed.\n"
+fi
+
+wait