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