ali@38: #!/bin/sh ali@38: ali@38: # This script starts an instance of Xvfb, the "fake" X server, runs a command ali@38: # with that server available, and kills the X server when done. The return ali@38: # value of the command becomes the return value of this script. ali@38: # ali@38: # If anyone is using this to build a Debian package, make sure the package ali@38: # Build-Depends on xvfb and xauth. ali@38: ali@38: set -e ali@38: ali@38: PROGNAME=xvfb-run ali@38: SERVERNUM=99 ali@38: AUTHFILE= ali@38: ERRORFILE=/dev/null ali@38: XVFBARGS="-screen 0 640x480x8" ali@38: LISTENTCP="-nolisten tcp" ali@38: XAUTHPROTO=. ali@38: ali@38: # Query the terminal to establish a default number of columns to use for ali@38: # displaying messages to the user. This is used only as a fallback in the event ali@38: # the COLUMNS variable is not set. ($COLUMNS can react to SIGWINCH while the ali@38: # script is running, and this cannot, only being calculated once.) ali@38: DEFCOLUMNS=$(stty size 2>/dev/null | awk '{print $2}') || true ali@38: if ! expr "$DEFCOLUMNS" : "[[:digit:]]\+$" >/dev/null 2>&1; then ali@38: DEFCOLUMNS=80 ali@38: fi ali@38: ali@38: # Display a message, wrapping lines at the terminal width. ali@38: message () { ali@38: echo "$PROGNAME: $*" | fmt -t -w ${COLUMNS:-$DEFCOLUMNS} ali@38: } ali@38: ali@38: # Display an error message. ali@38: error () { ali@38: message "error: $*" >&2 ali@38: } ali@38: ali@38: # Display a usage message. ali@38: usage () { ali@38: if [ -n "$*" ]; then ali@38: message "usage error: $*" ali@38: fi ali@38: cat <>"$ERRORFILE" 2>&1 ali@38: fi ali@38: if [ -n "$XVFB_RUN_TMPDIR" ]; then ali@38: if ! rm -r "$XVFB_RUN_TMPDIR"; then ali@38: error "problem while cleaning up temporary directory" ali@38: exit 5 ali@38: fi ali@38: fi ali@38: if [ -n "$XVFBPID" ]; then ali@38: kill "$XVFBPID" >>"$ERRORFILE" 2>&1 ali@38: fi ali@38: } ali@38: ali@38: # Parse the command line. ali@38: ARGS=$(getopt --options +ae:f:hn:lp:s:w: \ ali@38: --long auto-servernum,error-file:,auth-file:,help,server-num:,listen-tcp,xauth-protocol:,server-args:,wait: \ ali@38: --name "$PROGNAME" -- "$@") ali@38: GETOPT_STATUS=$? ali@38: ali@38: if [ $GETOPT_STATUS -ne 0 ]; then ali@38: error "internal error; getopt exited with status $GETOPT_STATUS" ali@38: exit 6 ali@38: fi ali@38: ali@38: eval set -- "$ARGS" ali@38: ali@38: while :; do ali@38: case "$1" in ali@38: -a|--auto-servernum) SERVERNUM=$(find_free_servernum); AUTONUM="yes" ;; ali@38: -e|--error-file) ERRORFILE="$2"; shift ;; ali@38: -f|--auth-file) AUTHFILE="$2"; shift ;; ali@38: -h|--help) SHOWHELP="yes" ;; ali@38: -n|--server-num) SERVERNUM="$2"; shift ;; ali@38: -l|--listen-tcp) LISTENTCP="" ;; ali@38: -p|--xauth-protocol) XAUTHPROTO="$2"; shift ;; ali@38: -s|--server-args) XVFBARGS="$2"; shift ;; ali@38: -w|--wait) shift ;; ali@38: --) shift; break ;; ali@38: *) error "internal error; getopt permitted \"$1\" unexpectedly" ali@38: exit 6 ali@38: ;; ali@38: esac ali@38: shift ali@38: done ali@38: ali@38: if [ "$SHOWHELP" ]; then ali@38: usage ali@38: exit 0 ali@38: fi ali@38: ali@38: if [ -z "$*" ]; then ali@38: usage "need a command to run" >&2 ali@38: exit 2 ali@38: fi ali@38: ali@38: if ! which xauth >/dev/null; then ali@38: error "xauth command not found" ali@38: exit 3 ali@38: fi ali@38: ali@38: # tidy up after ourselves ali@38: trap clean_up EXIT ali@38: ali@38: # If the user did not specify an X authorization file to use, set up a temporary ali@38: # directory to house one. ali@38: if [ -z "$AUTHFILE" ]; then ali@38: XVFB_RUN_TMPDIR="$(mktemp -d -t $PROGNAME.XXXXXX)" ali@38: # Create empty file to avoid xauth warning ali@38: AUTHFILE=$(tempfile -n "$XVFB_RUN_TMPDIR/Xauthority") ali@38: fi ali@38: ali@38: # Start Xvfb. ali@38: MCOOKIE=$(mcookie) ali@38: tries=10 ali@38: while [ $tries -gt 0 ]; do ali@38: tries=$(( $tries - 1 )) ali@38: XAUTHORITY=$AUTHFILE xauth source - << EOF >>"$ERRORFILE" 2>&1 ali@38: add :$SERVERNUM $XAUTHPROTO $MCOOKIE ali@38: EOF ali@38: # handle SIGUSR1 so Xvfb knows to send a signal when it's ready to accept ali@38: # connections ali@38: trap : USR1 ali@38: (trap '' USR1; exec Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP -auth $AUTHFILE >>"$ERRORFILE" 2>&1) & ali@38: XVFBPID=$! ali@38: ali@38: wait || : ali@38: if kill -0 $XVFBPID 2>/dev/null; then ali@38: break ali@38: elif [ -n "$AUTONUM" ]; then ali@38: # The display is in use so try another one (if '-a' was specified). ali@38: SERVERNUM=$((SERVERNUM + 1)) ali@38: SERVERNUM=$(find_free_servernum) ali@38: continue ali@38: fi ali@38: error "Xvfb failed to start" >&2 ali@38: XVFBPID= ali@38: exit 1 ali@38: done ali@38: ali@38: # Start the command and save its exit status. ali@38: set +e ali@38: DISPLAY=:$SERVERNUM XAUTHORITY=$AUTHFILE "$@" 2>&1 ali@38: RETVAL=$? ali@38: set -e ali@38: ali@38: # Return the executed command's exit status. ali@38: exit $RETVAL ali@38: ali@38: # vim:set ai et sts=4 sw=4 tw=80: