nix-helpers: 7d81b805df9253f57c6ea716e12f73598aea7eb1
1: # xvfb-run has a few annoyances. Most important are that, prior to the end of
2: # 2017, it redirects stderr to stdout; it also clobbers itself if multiple
3: # instances are run. We fix this, as well as providing niceties like VNC access.
4: { bash, fail, mkBin, replace, runCommand, utillinux, x11vnc, xvfb_run }:
5:
6: with rec {
7: # Hack to avoid unwanted quasiquotes
8: braced = s: "$" + "{" + s + "}";
9:
10: # Patch xvfb_run to stop it merging stderr into stdout
11: patched = runCommand "patch-xvfb-run" {
12: buildInputs = [ fail replace ];
13: old = xvfb_run;
14: broken = ''DISPLAY=:$SERVERNUM XAUTHORITY=$AUTHFILE "$@" 2>&1'';
15: fixed = ''
16: [[ -z "$DEBUG" ]] || set -x
17: VNCPID=""
18: if [[ "x$XVFB_VNC" = "x1" ]]
19: then
20: echo "Starting VNC server, as requested" 1>&2
21: DISPLAY=":$SERVERNUM" XAUTHORITY="$AUTHFILE" x11vnc -localhost \
22: -quiet 1>&2 &
23: VNCPID="$!"
24: fi
25: DISPLAY=":$SERVERNUM" XAUTHORITY="$AUTHFILE" "$@"
26: [[ -z "$VNCPID" ]] || kill "$VNCPID"
27: '';
28: } ''
29: set -e
30:
31: cp -rv "$old" "$out"
32: chmod +w -R "$out"
33:
34: # Update references, e.g. in makeWrapper scripts
35: find "$out" -type f | while read -r FILE
36: do
37: replace "$old" "$out" -- "$FILE"
38: done
39:
40: # Look for the script. If it's been through makeWrapper, use the original.
41: NAME="xvfb-run"
42: WRAPPED="$out/bin/.${braced "NAME"}-wrapped"
43: SCRIPT="$out/bin/$NAME"
44:
45: if [[ -f "$WRAPPED" ]]
46: then
47: SCRIPT="$WRAPPED"
48: fi
49:
50: [[ -f "$SCRIPT" ]] || fail "xvfb-run script '$SCRIPT' not found"
51:
52: if grep -F "$broken" < "$SCRIPT"
53: then
54: echo "Patching broken xvfb-run script" 1>&2
55: replace "$broken" "$fixed" -- "$SCRIPT"
56: else
57: echo "Not patching '$SCRIPT' since it doesn't appear broken" 1>&2
58: fi
59: '';
60:
61: # Wrap xvfb_run, so we can find a free DISPLAY number, etc.
62: go = mkBin {
63: name = "xvfb-run-safe";
64: paths = [ bash fail utillinux patched x11vnc ];
65: script = ''
66: #!${bash}/bin/bash
67: set -e
68: [[ -z "$DEBUG" ]] || set -x
69:
70: # allow settings to be updated via environment
71: # shellcheck disable=SC2154
72: : "${braced "xvfb_lockdir:=/tmp/xvfb-locks"}"
73:
74: # shellcheck disable=SC2154
75: : "${braced "xvfb_display_min:=99"}"
76:
77: # shellcheck disable=SC2154
78: : "${braced "xvfb_display_max:=599"}"
79:
80: mkdir -p -- "$xvfb_lockdir" ||
81: fail "Couldn't make xvfb_lockdir '$xvfb_lockdir'"
82:
83: chmod a+w "$xvfb_lockdir" 2> /dev/null || true
84:
85: PERMISSIONS=$(stat -L -c "%a" "$xvfb_lockdir")
86: OCTAL="0$PERMISSIONS"
87: WRITABLE=$(( OCTAL & 0002 ))
88:
89: function debugMsg {
90: [[ -z "$DEBUG" ]] || echo -e "$*" 1>&2
91: }
92:
93: if [[ "$WRITABLE" -ne 2 ]]
94: then
95: echo "ERROR: xvfb_lockdir '$xvfb_lockdir' isn't world writable" 1>&2
96: fail "This may cause users to clobber each others' DISPLAY" 1>&2
97: fi
98:
99: function cleanUp {
100: # Gracefully stop 'tail' command
101: [[ -z "$ERRPID" ]] || {
102: # Fire off a bg job which waits, then kills tail (if still running)
103: (sleep 1; kill "$ERRPID" 2> /dev/null || true;) &
104:
105: # Wait for tail to die by making it an fg job (if still running)
106: fg 2> /dev/null || true
107: }
108: for F in "$xvfb_lockdir/$i" "/tmp/.X$i-lock" "/tmp/.X11-unix/X$i" \
109: "$xvfb_lockdir/$i.err"
110: do
111: rm -f "$F" || debugMsg "Failed to delete '$F'. Oh well."
112: done
113: }
114: trap cleanUp EXIT
115:
116: # Look for a free DISPLAY number, starting from min and going to max
117: ERRPID=""
118: for i in $(seq "$xvfb_display_min" "$xvfb_display_max" | shuf)
119: do
120: if [[ -e "/tmp/.X$i-lock" ]]
121: then
122: debugMsg "Skipping X display on :$i"
123: (( ++i ))
124: continue
125: fi
126:
127: if [[ -e "/tmp/.X11-unix/X$i" ]]
128: then
129: debugMsg "Skipping existing socket '/tmp/.X11-unix/X$i'"
130: (( ++i ))
131: continue
132: fi
133:
134: if [[ -e "$xvfb_lockdir/$i" ]]
135: then
136: debugMsg "Skipping existing lock file '$xvfb_lockdir/$i'"
137: (( ++i ))
138: continue
139: fi
140:
141: exec 5> "$xvfb_lockdir/$i" || {
142: debugMsg "Couldn't lock '$xvfb_lockdir/$i', skipping"
143: (( ++i ))
144: continue
145: }
146:
147: # Wait for the lock
148: if flock -x -n 5
149: then
150: debugMsg "Aquired lock '$xvfb_lockdir/$i', running command"
151:
152: # Stream stderr (process substitution doesn't seem to work)
153: touch "$xvfb_lockdir/$i.err"
154: if [[ -n "$DEBUG" ]]
155: then
156: tail -f "$xvfb_lockdir/$i.err" >&2 &
157: ERRPID="$!"
158: fi
159:
160: xvfb-run --server-num="$i" -e "$xvfb_lockdir/$i.err" "$@"
161: RET="$?"
162:
163: # Break the loop now that we've finished
164: exit "$RET"
165: fi
166:
167: # If we couldn't get the lock (e.g. due to a timeout), try the next
168: (( ++i ))
169: done
170: '';
171: };
172: };
173:
174: go
Generated by git2html.