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.