tests/xvfb-run
author J. Ali Harlow <ali@juiblex.co.uk>
Mon Aug 31 07:12:39 2020 +0100 (2020-08-31)
changeset 105 bbddb595e366
permissions -rwxr-xr-x
Added tag 0.6 for changeset 5cb36c12ac49
     1 #!/bin/sh
     2 
     3 # This script starts an instance of Xvfb, the "fake" X server, runs a command
     4 # with that server available, and kills the X server when done.  The return
     5 # value of the command becomes the return value of this script.
     6 #
     7 # If anyone is using this to build a Debian package, make sure the package
     8 # Build-Depends on xvfb and xauth.
     9 
    10 set -e
    11 
    12 PROGNAME=xvfb-run
    13 SERVERNUM=99
    14 AUTHFILE=
    15 ERRORFILE=/dev/null
    16 XVFBARGS="-screen 0 640x480x8"
    17 LISTENTCP="-nolisten tcp"
    18 XAUTHPROTO=.
    19 
    20 # Query the terminal to establish a default number of columns to use for
    21 # displaying messages to the user.  This is used only as a fallback in the event
    22 # the COLUMNS variable is not set.  ($COLUMNS can react to SIGWINCH while the
    23 # script is running, and this cannot, only being calculated once.)
    24 DEFCOLUMNS=$(stty size 2>/dev/null | awk '{print $2}') || true
    25 if ! expr "$DEFCOLUMNS" : "[[:digit:]]\+$" >/dev/null 2>&1; then
    26     DEFCOLUMNS=80
    27 fi
    28 
    29 # Display a message, wrapping lines at the terminal width.
    30 message () {
    31     echo "$PROGNAME: $*" | fmt -t -w ${COLUMNS:-$DEFCOLUMNS}
    32 }
    33 
    34 # Display an error message.
    35 error () {
    36     message "error: $*" >&2
    37 }
    38 
    39 # Display a usage message.
    40 usage () {
    41     if [ -n "$*" ]; then
    42         message "usage error: $*"
    43     fi
    44     cat <<EOF
    45 Usage: $PROGNAME [OPTION ...] COMMAND
    46 Run COMMAND (usually an X client) in a virtual X server environment.
    47 Options:
    48 -a        --auto-servernum          try to get a free server number, starting at
    49                                     --server-num
    50 -e FILE   --error-file=FILE         file used to store xauth errors and Xvfb
    51                                     output (default: $ERRORFILE)
    52 -f FILE   --auth-file=FILE          file used to store auth cookie
    53                                     (default: ./.Xauthority)
    54 -h        --help                    display this usage message and exit
    55 -n NUM    --server-num=NUM          server number to use (default: $SERVERNUM)
    56 -l        --listen-tcp              enable TCP port listening in the X server
    57 -p PROTO  --xauth-protocol=PROTO    X authority protocol name to use
    58                                     (default: xauth command's default)
    59 -s ARGS   --server-args=ARGS        arguments (other than server number and
    60                                     "-nolisten tcp") to pass to the Xvfb server
    61                                     (default: "$XVFBARGS")
    62 EOF
    63 }
    64 
    65 # Find a free server number by looking at .X*-lock files in /tmp.
    66 find_free_servernum() {
    67     # Sadly, the "local" keyword is not POSIX.  Leave the next line commented in
    68     # the hope Debian Policy eventually changes to allow it in /bin/sh scripts
    69     # anyway.
    70     #local i
    71 
    72     i=$SERVERNUM
    73     while [ -f /tmp/.X$i-lock ]; do
    74         i=$(($i + 1))
    75     done
    76     echo $i
    77 }
    78 
    79 # Clean up files
    80 clean_up() {
    81     if [ -e "$AUTHFILE" ]; then
    82         XAUTHORITY=$AUTHFILE xauth remove ":$SERVERNUM" >>"$ERRORFILE" 2>&1
    83     fi
    84     if [ -n "$XVFB_RUN_TMPDIR" ]; then
    85         if ! rm -r "$XVFB_RUN_TMPDIR"; then
    86             error "problem while cleaning up temporary directory"
    87             exit 5
    88         fi
    89     fi
    90     if [ -n "$XVFBPID" ]; then
    91         kill "$XVFBPID" >>"$ERRORFILE" 2>&1
    92     fi
    93 }
    94 
    95 # Parse the command line.
    96 ARGS=$(getopt --options +ae:f:hn:lp:s:w: \
    97        --long auto-servernum,error-file:,auth-file:,help,server-num:,listen-tcp,xauth-protocol:,server-args:,wait: \
    98        --name "$PROGNAME" -- "$@")
    99 GETOPT_STATUS=$?
   100 
   101 if [ $GETOPT_STATUS -ne 0 ]; then
   102     error "internal error; getopt exited with status $GETOPT_STATUS"
   103     exit 6
   104 fi
   105 
   106 eval set -- "$ARGS"
   107 
   108 while :; do
   109     case "$1" in
   110         -a|--auto-servernum) SERVERNUM=$(find_free_servernum); AUTONUM="yes" ;;
   111         -e|--error-file) ERRORFILE="$2"; shift ;;
   112         -f|--auth-file) AUTHFILE="$2"; shift ;;
   113         -h|--help) SHOWHELP="yes" ;;
   114         -n|--server-num) SERVERNUM="$2"; shift ;;
   115         -l|--listen-tcp) LISTENTCP="" ;;
   116         -p|--xauth-protocol) XAUTHPROTO="$2"; shift ;;
   117         -s|--server-args) XVFBARGS="$2"; shift ;;
   118         -w|--wait) shift ;;
   119         --) shift; break ;;
   120         *) error "internal error; getopt permitted \"$1\" unexpectedly"
   121            exit 6
   122            ;;
   123     esac
   124     shift
   125 done
   126 
   127 if [ "$SHOWHELP" ]; then
   128     usage
   129     exit 0
   130 fi
   131 
   132 if [ -z "$*" ]; then
   133     usage "need a command to run" >&2
   134     exit 2
   135 fi
   136 
   137 if ! which xauth >/dev/null; then
   138     error "xauth command not found"
   139     exit 3
   140 fi
   141 
   142 # tidy up after ourselves
   143 trap clean_up EXIT
   144 
   145 # If the user did not specify an X authorization file to use, set up a temporary
   146 # directory to house one.
   147 if [ -z "$AUTHFILE" ]; then
   148     XVFB_RUN_TMPDIR="$(mktemp -d -t $PROGNAME.XXXXXX)"
   149     # Create empty file to avoid xauth warning
   150     AUTHFILE=$(tempfile -n "$XVFB_RUN_TMPDIR/Xauthority")
   151 fi
   152 
   153 # Start Xvfb.
   154 MCOOKIE=$(mcookie)
   155 tries=10
   156 while [ $tries -gt 0 ]; do
   157     tries=$(( $tries - 1 ))
   158     XAUTHORITY=$AUTHFILE xauth source - << EOF >>"$ERRORFILE" 2>&1
   159 add :$SERVERNUM $XAUTHPROTO $MCOOKIE
   160 EOF
   161     # handle SIGUSR1 so Xvfb knows to send a signal when it's ready to accept
   162     # connections
   163     trap : USR1
   164     (trap '' USR1; exec Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP -auth $AUTHFILE >>"$ERRORFILE" 2>&1) &
   165     XVFBPID=$!
   166 
   167     wait || :
   168     if kill -0 $XVFBPID 2>/dev/null; then
   169         break
   170     elif [ -n "$AUTONUM" ]; then
   171         # The display is in use so try another one (if '-a' was specified).
   172         SERVERNUM=$((SERVERNUM + 1))
   173         SERVERNUM=$(find_free_servernum)
   174         continue
   175     fi
   176     error "Xvfb failed to start" >&2
   177     XVFBPID=
   178     exit 1
   179 done
   180 
   181 # Start the command and save its exit status.
   182 set +e
   183 DISPLAY=:$SERVERNUM XAUTHORITY=$AUTHFILE "$@" 2>&1
   184 RETVAL=$?
   185 set -e
   186 
   187 # Return the executed command's exit status.
   188 exit $RETVAL
   189 
   190 # vim:set ai et sts=4 sw=4 tw=80: