VirtualBox

source: vbox/trunk/src/VBox/Installer/linux/scripts/VBoxHeadlessXOrg.sh@ 107044

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1#!/bin/sh
2# $Id: VBoxHeadlessXOrg.sh 106061 2024-09-16 14:03:52Z vboxsync $
3## @file
4# VirtualBox X Server auto-start service.
5#
6
7#
8# Copyright (C) 2012-2024 Oracle and/or its affiliates.
9#
10# This file is part of VirtualBox base platform packages, as
11# available from https://www.virtualbox.org.
12#
13# This program is free software; you can redistribute it and/or
14# modify it under the terms of the GNU General Public License
15# as published by the Free Software Foundation, in version 3 of the
16# License.
17#
18# This program is distributed in the hope that it will be useful, but
19# WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21# General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, see <https://www.gnu.org/licenses>.
25#
26# SPDX-License-Identifier: GPL-3.0-only
27#
28
29PATH=$PATH:/bin:/sbin:/usr/sbin
30
31## Start one or several X servers in the background for use with headless
32# rendering. For details, options and configuration see the usage() function
33# further down.
34#
35# I have tried to follow the best practices I could find for writing a Linux
36# service (and doing it in shell script) which should work well with
37# traditional and modern service systems using minimal init or service files.
38# In our case this boils down to:
39# * Start with a single command line, stop using one of ${EXIT_SIGNALS} below.
40# * Stopping with a signal can be done safely using the pid stored in the
41# pid-file and our (presumably unique) command name. For this reason we
42# only support running one instance of the service though.
43# * Start in the foreground. Systems without proper service control can take
44# care of the backgrounding in the init script.
45# * Clean up all sub-processes (X servers) ourselves when we are stopped
46# cleanly and don't provide any other way to clean them up automatically (in
47# case we are stopped uncleanly) as we don't know of a generic safe way to
48# do so, though some service management systems (i.e. systemd) can do so.
49# (A more thorough automatic clean-up would be possible if Xorg didn't
50# potentially have to be run as root, so that we could run all processes
51# using a service-specific user account and just terminate all processes
52# run by that user to clean up.)
53
54## Default configuration file name.
55# @note This is not very nice - /etc/default is actually Debian-specific.
56CONFIGURATION_FILE=/etc/default/virtualbox
57## The name of this script.
58SCRIPT_NAME="$0"
59## The service name.
60SERVICE_NAME="vboxheadlessxorg"
61## The service description.
62SERVICE_DESCRIPTION="Headless rendering service"
63## Signals and conditions which may be used to terminate the service.
64EXIT_SIGNALS="EXIT HUP INT QUIT ABRT TERM"
65## The default run-time data folder.
66DEFAULT_RUN_FOLDER="/var/run/${SERVICE_NAME}/"
67## The default X server configuration directory.
68DEFAULT_CONFIGURATION_FOLDER="${DEFAULT_RUN_FOLDER}/xorg.conf.d/"
69## The extra data key used to provide the list of available X server displays.
70EXTRA_DATA_KEY_DISPLAYS="HeadlessXServer/Displays"
71## The extra data key used to specify the X server authority file.
72EXTRA_DATA_KEY_AUTH="HeadlessXServer/AuthFile"
73
74## Print usage information for the service script.
75## @todo Perhaps we should support some of the configuration file options from
76# the command line. Opinions welcome.
77## @todo Possibly extract this information for the user manual.
78usage() {
79 cat << EOF
80Usage:
81
82 $(basename "${SCRIPT_NAME}") [<options>]
83
84Start one or several X servers in the background for use with headless
85rendering. 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
86generated for each. We attempt to start an X server process for each
87configuration file. The process is configurable by setting values in a file as
88described below.
89
90Options:
91
92 -c|--conf-file Specify an alternative locations for the configuration
93 file. The default location is:
94 "${CONFIGURATION_FILE}"
95
96 --help|--usage Print this text.
97
98The optional configuration file should contain a series of lines of the form
99"KEY=value". It will be read in as a command shell sub-script. Here is the
100current list of possible key settings with a short explanation. Usually it
101should be sufficient to change the value of \${HEADLESS_X_ORG_USERS} and to
102leave all other settings unchanged.
103
104 HEADLESS_X_ORG_CONFIGURATION_FOLDER
105 The folder where the X server configuration files are to be created.
106
107 HEADLESS_X_ORG_LOG_FOLDER
108 The folder where log files will be saved.
109
110 HEADLESS_X_ORG_LOG_FILE
111 The main log file name.
112
113 HEADLESS_X_ORG_RUN_FOLDER
114 The folder to store run-time data in.
115
116 HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES
117 Command to execute to wait until all dependencies for the X servers are
118 available. The default command waits until the udev event queue has
119 settled. The command may return failure to signal that it has given up.
120 No arguments may be passsed.
121
122 HEADLESS_X_ORG_USERS
123 List of users who will have access to the X servers started and for whom we
124 will provide the configuration details via VirtualBox extra data. This
125 variable is only used by the commands in the default configuration
126 (\${HEADLESS_X_ORG_SERVER_PRE_COMMAND} and
127 \${HEADLESS_X_ORG_SERVER_POST_COMMAND}), and not by the service itself.
128
129 HEADLESS_X_ORG_FIRST_DISPLAY
130 The first display number which will be used for a started X server. The
131 others will use the following numbers.
132
133 HEADLESS_X_ORG_SERVER_PRE_COMMAND
134 Command to execute once to perform any set-up needed before starting the
135 X servers, such as setting up the X server authentication. The default
136 command creates an authority file for each of the users in the list
137 \${HEADLESS_X_ORG_USERS} and generates server configuration files for all
138 detected graphics cards. No arguments may be passed.
139
140 HEADLESS_X_ORG_SERVER_COMMAND
141 The default X server start-up command. It will be passed three parameters
142 - in order, the screen number to use, the path of the X.Org configuration
143 file to use and the path of the X server log file to create.
144
145 HEADLESS_X_ORG_SERVER_POST_COMMAND
146 Command to execute once the X servers have been successfully started. It
147 will be passed a single parameter which is a space-separated list of the
148 X server screen numbers. By default this stores the service configuration
149 information to VirtualBox extra data for each of the users in the list
150 from the variable HEADLESS_X_ORG_USERS: the list of displays is set to the
151 key "${EXTRA_DATA_KEY_DISPLAYS}" and the path of the authority file to
152 "${EXTRA_DATA_KEY_AUTH}".
153EOF
154}
155
156# Default configuration.
157HEADLESS_X_ORG_CONFIGURATION_FOLDER="${DEFAULT_CONFIGURATION_FOLDER}"
158HEADLESS_X_ORG_LOG_FOLDER="/var/log/${SERVICE_NAME}"
159HEADLESS_X_ORG_LOG_FILE="${SERVICE_NAME}.log"
160HEADLESS_X_ORG_RUN_FOLDER="/var/run/${SERVICE_NAME}"
161HEADLESS_X_ORG_USERS=""
162HEADLESS_X_ORG_FIRST_DISPLAY=40
163X_AUTH_FILE="${HEADLESS_X_ORG_RUN_FOLDER}/xauth"
164
165default_wait_for_prerequisites()
166{
167 udevadm settle || udevsettle # Fails if no udevadm.
168}
169HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES="default_wait_for_prerequisites"
170
171default_pre_command()
172{
173 # Create new authority file.
174 echo > "${X_AUTH_FILE}"
175 # Create the xorg.conf files.
176 mkdir -p "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}" || return 1
177 display="${HEADLESS_X_ORG_FIRST_DISPLAY}"
178 for i in /sys/bus/pci/devices/*; do
179 read class < "${i}/class"
180 case "${class}" in *03????)
181 address="${i##*/}"
182 address="${address%%:*}${address#*:}"
183 address="PCI:${address%%.*}:${address#*.}"
184 read vendor < "${i}/vendor"
185 case "${vendor}" in *10de|*10DE) # NVIDIA
186 cat > "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}/xorg.conf.${display}" << EOF
187Section "Module"
188 Load "glx"
189EndSection
190Section "Device"
191 Identifier "Device${display}"
192 Driver "nvidia"
193 Option "UseDisplayDevice" "none"
194EndSection
195Section "Screen"
196 Identifier "Screen${display}"
197 Device "Device${display}"
198EndSection
199Section "ServerLayout"
200 Identifier "Layout${display}"
201 Screen "Screen${display}"
202 Option "AllowMouseOpenFail" "true"
203 Option "AutoAddDevices" "false"
204 Option "AutoAddGPU" "false"
205 Option "AutoEnableDevices" "false"
206 Option "IsolateDevice" "${address}"
207EndSection
208EOF
209 esac
210 # Add key to the authority file.
211 key="$(dd if=/dev/urandom count=1 bs=16 2>/dev/null | od -An -x)"
212 xauth -f "${X_AUTH_FILE}" add :${display} . "${key}"
213 display=`expr ${display} + 1`
214 esac
215 done
216 # Duplicate the authority file.
217 for i in ${HEADLESS_X_ORG_USERS}; do
218 cp "${X_AUTH_FILE}" "${X_AUTH_FILE}.${i}"
219 chown "${i}" "${X_AUTH_FILE}.${i}"
220 done
221}
222HEADLESS_X_ORG_SERVER_PRE_COMMAND="default_pre_command"
223
224default_command()
225{
226 auth="${HEADLESS_X_ORG_RUN_FOLDER}/xauth"
227 # screen=$1
228 # conf_file=$2
229 # log_file=$3
230 trap "kill \${PID}; sleep 5; kill -KILL \${PID} 2>/dev/null" ${EXIT_SIGNALS}
231 Xorg :"${1}" -auth "${auth}" -config "${2}" -logverbose 0 -logfile /dev/null -verbose 7 > "${3}" 2>&1 &
232 PID="$!"
233 wait
234 exit
235}
236HEADLESS_X_ORG_SERVER_COMMAND="default_command"
237
238default_post_command()
239{
240 # screens=$1
241 for i in ${HEADLESS_X_ORG_USERS}; do
242 su ${i} -c "VBoxManage setextradata global ${EXTRA_DATA_KEY_DISPLAYS} \"${1}\""
243 su ${i} -c "VBoxManage setextradata global ${EXTRA_DATA_KEY_AUTH} \"${HEADLESS_X_ORG_RUN_FOLDER}/xauth\""
244 done
245}
246HEADLESS_X_ORG_SERVER_POST_COMMAND="default_post_command"
247
248## The function definition at the start of every non-trivial shell script!
249abort() {
250 ## $@, ... Error text to output to standard error in printf format.
251 printf "$@" >&2
252 exit 1
253}
254
255## Milder version of abort, when we can't continue because of a valid condition.
256abandon() {
257 ## $@, ... Text to output to standard error in printf format.
258 printf "$@" >&2
259 exit 0
260}
261
262abort_usage() {
263 usage >&2
264 abort "$@"
265}
266
267# Print a banner message
268banner() {
269 cat << EOF
270${VBOX_PRODUCT} VBoxHeadless X Server start-up service Version ${VBOX_VERSION_STRING}
271(C) 2005-${VBOX_C_YEAR} ${VBOX_VENDOR}
272All rights reserved.
273
274EOF
275}
276
277# Get the directory where the script is located.
278SCRIPT_FOLDER=$(dirname "${SCRIPT_NAME}")"/"
279[ -r "${SCRIPT_FOLDER}generated.sh" ] ||
280 abort "${LOG_FILE}" "Failed to find installation information.\n"
281. "${SCRIPT_FOLDER}generated.sh"
282
283# Parse our arguments.
284while [ "$#" -gt 0 ]; do
285 case $1 in
286 -c|--conf-file)
287 [ "$#" -gt 1 ] ||
288 {
289 banner
290 abort "%s requires at least one argument.\n" "$1"
291 }
292 CONFIGURATION_FILE="$2"
293 shift
294 ;;
295 --help|--usage)
296 banner
297 usage
298 exit 0
299 ;;
300 *)
301 banner
302 abort_usage "Unknown argument $1.\n"
303 ;;
304 esac
305 shift
306done
307
308[ -r "${CONFIGURATION_FILE}" ] && . "${CONFIGURATION_FILE}"
309
310# Change to the root directory so we don't hold any other open.
311cd /
312
313# If something fails here we will catch it when we create the directory.
314[ -e "${HEADLESS_X_ORG_LOG_FOLDER}" ] &&
315 [ -d "${HEADLESS_X_ORG_LOG_FOLDER}" ] &&
316 rm -rf "${HEADLESS_X_ORG_LOG_FOLDER}.old" 2> /dev/null &&
317mv "${HEADLESS_X_ORG_LOG_FOLDER}" "${HEADLESS_X_ORG_LOG_FOLDER}.old" 2> /dev/null
318mkdir -p "${HEADLESS_X_ORG_LOG_FOLDER}" 2>/dev/null ||
319{
320 banner
321 abort "Failed to create log folder \"${HEADLESS_X_ORG_LOG_FOLDER}\".\n"
322}
323mkdir -p "${HEADLESS_X_ORG_RUN_FOLDER}" 2>/dev/null ||
324{
325 banner
326 abort "Failed to create run folder \"${HEADLESS_X_ORG_RUN_FOLDER}\".\n"
327}
328exec > "${HEADLESS_X_ORG_LOG_FOLDER}/${HEADLESS_X_ORG_LOG_FILE}" 2>&1
329
330banner
331
332# Wait for our dependencies to become available.
333if [ -n "${HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES}" ]; then
334 "${HEADLESS_X_ORG_WAIT_FOR_PREREQUISITES}" ||
335 abort "Service prerequisites not available.\n"
336fi
337
338# Do any pre-start setup.
339if [ -n "${HEADLESS_X_ORG_SERVER_PRE_COMMAND}" ]; then
340 "${HEADLESS_X_ORG_SERVER_PRE_COMMAND}" ||
341 abort "Pre-requisite failed.\n"
342fi
343
344X_SERVER_PIDS=""
345X_SERVER_SCREENS=""
346trap "kill \${X_SERVER_PIDS} 2>/dev/null" ${EXIT_SIGNALS}
347space="" # Hack to put spaces between the pids but not before or after.
348for conf_file in "${HEADLESS_X_ORG_CONFIGURATION_FOLDER}"/*; do
349 [ x"${conf_file}" = x"${HEADLESS_X_ORG_CONFIGURATION_FOLDER}/*" ] &&
350 ! [ -e "${conf_file}" ] &&
351 abort "No configuration files found.\n"
352 filename="$(basename "${conf_file}")"
353 screen="$(expr "${filename}" : "xorg\.conf\.\(.*\)")"
354 [ 0 -le "${screen}" ] 2>/dev/null ||
355 abort "Badly formed file name \"${conf_file}\".\n"
356 log_file="${HEADLESS_X_ORG_LOG_FOLDER}/Xorg.${screen}.log"
357 "${HEADLESS_X_ORG_SERVER_COMMAND}" "${screen}" "${conf_file}" "${log_file}" &
358 X_SERVER_PIDS="${X_SERVER_PIDS}${space}$!"
359 X_SERVER_SCREENS="${X_SERVER_SCREENS}${space}${screen}"
360 space=" "
361done
362
363# Do any post-start work.
364if [ -n "${HEADLESS_X_ORG_SERVER_POST_COMMAND}" ]; then
365 "${HEADLESS_X_ORG_SERVER_POST_COMMAND}" "${X_SERVER_SCREENS}" ||
366 abort "Post-command failed.\n"
367fi
368
369wait
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette