mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-07-13 06:22:51 +03:00
267 lines
7.6 KiB
Bash
Executable File
267 lines
7.6 KiB
Bash
Executable File
#!/bin/sh
|
|
# xvfb-run-eepp: xvfb-run variant with race-safe automatic display selection.
|
|
# Defaults tuned for eepp GUI tests:
|
|
# --auto-servernum enabled by default
|
|
# --server-args "-screen 0 1280x1024x24" enabled by default
|
|
# ASAN_OPTIONS includes detect_leaks=0 for the child process
|
|
|
|
set -e
|
|
|
|
PROGNAME=${0##*/}
|
|
SERVERNUM=99
|
|
MAX_SERVERNUM=599
|
|
AUTO_SERVERNUM=yes
|
|
AUTHFILE=
|
|
ERRORFILE=/dev/null
|
|
STARTWAIT=3
|
|
XVFBARGS="-screen 0 1280x1024x24"
|
|
LISTENTCP="-nolisten tcp"
|
|
XAUTHPROTO=.
|
|
LOCKDIR=
|
|
XVFB_RUN_TMPDIR=
|
|
XVFBPID=
|
|
|
|
DEFCOLUMNS=$(stty size 2>/dev/null | awk '{print $2}') || true
|
|
case "$DEFCOLUMNS" in
|
|
*[!0-9]*|'') DEFCOLUMNS=80 ;;
|
|
esac
|
|
|
|
message() {
|
|
echo "$PROGNAME: $*" | fmt -t -w "${COLUMNS:-$DEFCOLUMNS}"
|
|
}
|
|
|
|
error() {
|
|
message "error: $*" >&2
|
|
}
|
|
|
|
usage() {
|
|
[ -n "$*" ] && message "usage error: $*"
|
|
cat <<EOF_USAGE
|
|
Usage: $PROGNAME [OPTION ...] COMMAND
|
|
Run COMMAND in a virtual X server environment.
|
|
|
|
Defaults differ from Debian xvfb-run:
|
|
--auto-servernum is enabled by default
|
|
--server-args defaults to "$XVFBARGS"
|
|
ASAN_OPTIONS gets detect_leaks=0 for COMMAND
|
|
|
|
Options:
|
|
-a --auto-servernum try to get a free server number, starting at --server-num [default]
|
|
--no-auto-servernum use exactly --server-num
|
|
-e FILE --error-file=FILE file used to store xauth errors and Xvfb output (default: $ERRORFILE)
|
|
-f FILE --auth-file=FILE file used to store auth cookie
|
|
-h --help display this usage message and exit
|
|
-n NUM --server-num=NUM first/exact server number to use (default: $SERVERNUM)
|
|
--max-server-num=NUM highest server number to try in auto mode (default: $MAX_SERVERNUM)
|
|
-l --listen-tcp enable TCP port listening in Xvfb
|
|
-p PROTO --xauth-protocol=PROTO X authority protocol name to use (default: xauth default)
|
|
-s ARGS --server-args=ARGS arguments to pass to Xvfb (default: "$XVFBARGS")
|
|
-w DELAY --wait=DELAY seconds to wait for Xvfb to start when xdpyinfo is unavailable (default: $STARTWAIT)
|
|
EOF_USAGE
|
|
}
|
|
|
|
cleanup() {
|
|
status=$?
|
|
|
|
if [ -n "$XVFBPID" ]; then
|
|
kill "$XVFBPID" >/dev/null 2>&1 || true
|
|
wait "$XVFBPID" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
if [ -n "$AUTHFILE" ]; then
|
|
XAUTHORITY=$AUTHFILE xauth remove ":$SERVERNUM" >"$ERRORFILE" 2>&1 || true
|
|
fi
|
|
|
|
[ -n "$XVFB_RUN_TMPDIR" ] && rm -rf "$XVFB_RUN_TMPDIR" || true
|
|
[ -n "$LOCKDIR" ] && rmdir "$LOCKDIR" >/dev/null 2>&1 || true
|
|
|
|
exit "$status"
|
|
}
|
|
|
|
is_display_busy() {
|
|
n=$1
|
|
|
|
# Xorg/Xvfb convention: lock file plus socket. Check both because stale
|
|
# lock files and still-existing sockets can happen independently.
|
|
[ -e "/tmp/.X$n-lock" ] && return 0
|
|
[ -e "/tmp/.X11-unix/X$n" ] && return 0
|
|
|
|
return 1
|
|
}
|
|
|
|
claim_display() {
|
|
n=$1
|
|
lock="${TMPDIR:-/tmp}/$PROGNAME-display-$n.lock"
|
|
|
|
# mkdir is atomic on local filesystems and avoids the race in classic
|
|
# xvfb-run -a, where many jobs can pick the same currently-free display.
|
|
if mkdir "$lock" 2>/dev/null; then
|
|
LOCKDIR=$lock
|
|
SERVERNUM=$n
|
|
# Helpful for manual cleanup/debugging. Not used as synchronization.
|
|
echo "$$" > "$LOCKDIR/pid" 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
|
|
# Older versions of this script claimed displays from a command substitution,
|
|
# which left lock directories behind because LOCKDIR was set in a subshell.
|
|
# Recover those stale claims, but never touch a lock owned by a live process.
|
|
if [ -f "$lock/pid" ]; then
|
|
oldpid=$(cat "$lock/pid" 2>/dev/null || true)
|
|
case "$oldpid" in
|
|
*[!0-9]*|'') oldpid= ;;
|
|
esac
|
|
if [ -z "$oldpid" ] || ! kill -0 "$oldpid" >/dev/null 2>&1; then
|
|
rm -f "$lock/pid" 2>/dev/null || true
|
|
rmdir "$lock" 2>/dev/null || true
|
|
if mkdir "$lock" 2>/dev/null; then
|
|
LOCKDIR=$lock
|
|
SERVERNUM=$n
|
|
echo "$$" > "$LOCKDIR/pid" 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
find_and_claim_display() {
|
|
i=$SERVERNUM
|
|
while [ "$i" -le "$MAX_SERVERNUM" ]; do
|
|
if ! is_display_busy "$i" && claim_display "$i"; then
|
|
return 0
|
|
fi
|
|
i=$((i + 1))
|
|
done
|
|
return 1
|
|
}
|
|
|
|
wait_for_xvfb() {
|
|
if command -v xdpyinfo >/dev/null 2>&1; then
|
|
i=0
|
|
# Poll for up to about 10 seconds. Avoid requiring sleep fractions.
|
|
while [ "$i" -lt 100 ]; do
|
|
DISPLAY=:$SERVERNUM XAUTHORITY=$AUTHFILE xdpyinfo >/dev/null 2>&1 && return 0
|
|
kill -0 "$XVFBPID" >/dev/null 2>&1 || return 1
|
|
sleep 0.1 2>/dev/null || sleep 1
|
|
i=$((i + 1))
|
|
done
|
|
return 1
|
|
fi
|
|
|
|
sleep "$STARTWAIT"
|
|
kill -0 "$XVFBPID" >/dev/null 2>&1
|
|
}
|
|
|
|
ARGS=$(getopt --options +ae:f:hn:lp:s:w: \
|
|
--long auto-servernum,no-auto-servernum,error-file:,auth-file:,help,server-num:,max-server-num:,listen-tcp,xauth-protocol:,server-args:,wait: \
|
|
--name "$PROGNAME" -- "$@")
|
|
GETOPT_STATUS=$?
|
|
|
|
if [ "$GETOPT_STATUS" -ne 0 ]; then
|
|
error "internal error; getopt exited with status $GETOPT_STATUS"
|
|
exit 6
|
|
fi
|
|
|
|
eval set -- "$ARGS"
|
|
|
|
SHOWHELP=
|
|
while :; do
|
|
case "$1" in
|
|
-a|--auto-servernum) AUTO_SERVERNUM=yes ;;
|
|
--no-auto-servernum) AUTO_SERVERNUM=no ;;
|
|
-e|--error-file) ERRORFILE=$2; shift ;;
|
|
-f|--auth-file) AUTHFILE=$2; shift ;;
|
|
-h|--help) SHOWHELP=yes ;;
|
|
-n|--server-num) SERVERNUM=$2; shift ;;
|
|
--max-server-num) MAX_SERVERNUM=$2; shift ;;
|
|
-l|--listen-tcp) LISTENTCP= ;;
|
|
-p|--xauth-protocol) XAUTHPROTO=$2; shift ;;
|
|
-s|--server-args) XVFBARGS=$2; shift ;;
|
|
-w|--wait) STARTWAIT=$2; shift ;;
|
|
--) shift; break ;;
|
|
*) error "internal error; getopt permitted '$1' unexpectedly"; exit 6 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ "$SHOWHELP" ]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$#" -eq 0 ]; then
|
|
usage "need a command to run" >&2
|
|
exit 2
|
|
fi
|
|
|
|
case "$SERVERNUM:$MAX_SERVERNUM" in
|
|
*[!0-9:]*|:*|*:)
|
|
error "server numbers must be numeric"
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
if ! command -v xauth >/dev/null 2>&1; then
|
|
error "xauth command not found"
|
|
exit 3
|
|
fi
|
|
|
|
if ! command -v Xvfb >/dev/null 2>&1; then
|
|
error "Xvfb command not found"
|
|
exit 3
|
|
fi
|
|
|
|
if [ "$AUTO_SERVERNUM" = yes ]; then
|
|
START_SERVERNUM=$SERVERNUM
|
|
if ! find_and_claim_display; then
|
|
error "could not find a free X display between $START_SERVERNUM and $MAX_SERVERNUM"
|
|
exit 4
|
|
fi
|
|
else
|
|
if is_display_busy "$SERVERNUM"; then
|
|
error "display :$SERVERNUM is already busy"
|
|
exit 4
|
|
fi
|
|
if ! claim_display "$SERVERNUM"; then
|
|
error "display :$SERVERNUM is already claimed by another $PROGNAME process"
|
|
exit 4
|
|
fi
|
|
fi
|
|
|
|
trap cleanup EXIT INT TERM HUP
|
|
|
|
if [ -z "$AUTHFILE" ]; then
|
|
XVFB_RUN_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/$PROGNAME.XXXXXXXX") || {
|
|
error "could not create temporary directory"
|
|
exit 4
|
|
}
|
|
AUTHFILE=$(mktemp "$XVFB_RUN_TMPDIR/Xauthority.XXXXXXXX") || {
|
|
error "could not create Xauthority file"
|
|
exit 4
|
|
}
|
|
fi
|
|
|
|
MCOOKIE=$(mcookie)
|
|
XAUTHORITY=$AUTHFILE xauth add ":$SERVERNUM" "$XAUTHPROTO" "$MCOOKIE" >"$ERRORFILE" 2>&1
|
|
|
|
# shellcheck disable=SC2086 # XVFBARGS and LISTENTCP intentionally split like xvfb-run.
|
|
Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP -auth "$AUTHFILE" >"$ERRORFILE" 2>&1 &
|
|
XVFBPID=$!
|
|
|
|
if ! wait_for_xvfb; then
|
|
error "Xvfb failed to start on display :$SERVERNUM; see $ERRORFILE"
|
|
exit 1
|
|
fi
|
|
|
|
set +e
|
|
ASAN_OPTIONS="${ASAN_OPTIONS:+$ASAN_OPTIONS:}detect_leaks=0" \
|
|
DISPLAY=:$SERVERNUM \
|
|
XAUTHORITY=$AUTHFILE \
|
|
"$@"
|
|
RETVAL=$?
|
|
set -e
|
|
|
|
exit "$RETVAL"
|