#!/bin/sh -e # xvfb-run - run the specified command in a virtual X server # This script starts an instance of Xvfb, the "fake" X server, runs a # command with that server available, and kills the X server when # done. The return value of the command becomes the return value of # this script. # # If anyone is using this to build a Debian package, make sure the # package Build-Depends on xvfb and xfonts-base. # # If anyone is using this to build an ALT Linux package, make sure the # package BuildRequires XFree86-Xvfb and XFree86-utils. DISPLAYNUM=90 STARTWAIT=5 LISTENTCP="-nolisten tcp" AUTODISPLAYNUM=n usage() { echo "Usage: $0 [OPTION]... [command]" echo echo "run specified X client or command in a virtual X server environment" echo echo " -a --auto-displaynum Try to get a free display number, starting at --display-num" echo " -n --display-num=NUM Display number to use (default:$DISPLAYNUM)" echo " -l --listen-tcp Enable TCP port listening in the X server" echo " -t --timeout=DELAY Timeout in seconds to wait for Xvfb to start (default:$STARTWAIT)" echo " -h --help Display this help and exit" } force_kill_xvfb() { kill $XVFBPID 2> /dev/null ||: } # Start an Xvfb server process in the background # using the display number passed as a parameter. # Perform checks to guard against a premature server exit or a display hijack. # Set the XVFBPID variable to the PID of the server started. start_xvfb() { local dispnum=$1 # start Xvfb in the background and wait a little Xvfb :$dispnum -screen 0 640x480x8 $LISTENTCP > /dev/null 2>&1 & XVFBPID=$! # poll for the server lock file to appear local countdown=$STARTWAIT while [ ! -r /tmp/.X$dispnum-lock ]; do if [ $countdown = 0 ]; then echo "$0: can't access lockfile /tmp/.X$dispnum-lock" >&2 force_kill_xvfb return 2 fi sleep 1 countdown=$((countdown-1)) done # check if it really is our process behind the display if [ ! -O /tmp/.X$dispnum-lock ]; then echo "$0: can't obtain lockfile /tmp/.X$dispnum-lock" >&2 force_kill_xvfb return 1 fi local lockpid read lockpid < /tmp/.X$dispnum-lock if [ "$XVFBPID" != "$lockpid" ]; then echo "$0: display :$dispnum has been taken by another process" >&2 force_kill_xvfb return 1 fi # Poll for the X server socket to be established while [ ! -O /tmp/.X11-unix/X$dispnum ]; do if [ $countdown = 0 ]; then echo "$0: can't access socket for display :$dispnum" >&2 force_kill_xvfb return 2 fi sleep 1 countdown=$((countdown-1)) done # finally, check that the server has not exited if ! kill -0 $XVFBPID 2>/dev/null; then echo "$0: Xvfb server has died" >&2 return 2 fi return 0 } # Shut down the Xvfb server whose PID is in the XVFBPID variable shutdown_xvfb() { kill $XVFBPID ||: } # Parse command line ARGS=`getopt --options +an:lt:w:h \ --long auto-displaynum,display-num:,listen-tcp,timeout:,wait:,help \ --name "$0" -- "$@"` eval set -- "$ARGS" while true ; do case "$1" in '-a'|'--auto-displaynum') AUTODISPLAYNUM=y ;; '-n'|'--display-num') DISPLAYNUM="$2" shift ;; '-l'|'--listen-tcp') LISTENTCP= ;; '-t'|'--timeout'|'-w'|'--wait') STARTWAIT="$2" shift ;; '-h'|'--help') usage exit 1 ;; '--') # end of options shift break ;; esac shift done # create the transient authority file AUTHFILE=$(mktemp -t xvfb-run.XXXXXXXX) XAUTHORITY="$AUTHFILE" export XAUTHORITY MCOOKIE=$(mcookie) # The main loop increments display numbers while true ; do # check for the X server lock file if [ -f /tmp/.X$DISPLAYNUM-lock ]; then if [ $AUTODISPLAYNUM = y ]; then DISPLAYNUM=$((DISPLAYNUM+1)) continue else echo "$0: lock file exists for display $DISPLAYNUM" >&2 RETVAL=1 break fi fi # set up an X authority cookie xauth add :$DISPLAYNUM . $MCOOKIE >/dev/null 2>&1 # start Xvfb start_xvfb $DISPLAYNUM XVFBSTATUS=$? case $XVFBSTATUS in 0) # run the command and save its exit status set +e DISPLAY=:$DISPLAYNUM "$@" RETVAL=$? set -e # kill Xvfb shutdown_xvfb break ;; 1) # a non-fatal startup failure # (can fall back to other display number) if [ $AUTODISPLAYNUM != y ]; then RETVAL=$XVFBSTATUS break fi ;; 2) # a fatal startup failure RETVAL=$XVFBSTATUS break ;; esac # clean up xauth remove :$DISPLAYNUM >/dev/null 2>&1 # continue with the next display number DISPLAYNUM=$((DISPLAYNUM+1)) done # remove the authority file rm -f "$AUTHFILE" # return the executed command's exit status exit $RETVAL