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