# row253-supervisor-begin IFS= read -r -d '' TESTER_SUPERVISOR <<'MOSAIC_TESTER_SUPERVISOR' || : set +m seconds="$1" setsid_bin="$2" sleep_bin="$3"; shift 3 child="" timer="" verified=0 stat_fields() { local pid="$1" raw="" rest # EOF is expected: stat has no NUL. Decide on content, not read's status. IFS= read -r -d '' raw < "/proc/$pid/stat" 2>/dev/null || : [ -n "$raw" ] || { [ -e "/proc/$pid/stat" ] && return 1; return 3; } rest="${raw##*) }" [ "$rest" != "$raw" ] || return 1 read -r ST_STATE ST_PARENT ST_GROUP ST_SESSION _ <<< "$rest" [[ "$ST_STATE" =~ ^[A-Za-z]$ && "$ST_SESSION" =~ ^[0-9]+$ && "$ST_GROUP" =~ ^[0-9]+$ ]] } clock_cs() { local boot spare read -r boot spare < /proc/uptime || return 1 [[ "$boot" =~ ^([0-9]+)\.([0-9]{2})$ ]] || return 1 CLOCK_CS=$((10#${BASH_REMATCH[1]} * 100 + 10#${BASH_REMATCH[2]})) } clean_session() { local entry pid rc live until="" while :; do live=0 for entry in /proc/[0-9]*/stat; do pid="${entry%/stat}"; pid="${pid##*/}" rc=0; stat_fields "$pid" || rc=$? [ "$rc" != 3 ] || continue [ "$rc" = 0 ] || return 1 [ "$ST_SESSION" = "$child" ] || continue case "$ST_STATE" in Z|X) continue ;; esac live=1 if ! kill -KILL "$pid" 2>/dev/null; then rc=0; stat_fields "$pid" || rc=$? [ "$rc" = 3 ] || { [ "$rc" = 0 ] && { [ "$ST_STATE" = Z ] || [ "$ST_STATE" = X ]; }; } || return 1 fi done [ "$live" != 0 ] || return 0 clock_cs || return 1 # Inventory pass 1 is outside the bounded confirmation/retry budget. [ -n "$until" ] || until=$((CLOCK_CS + 25)) [ "$CLOCK_CS" -lt "$until" ] || return 1 "$sleep_bin" 0.01 done } finish() { local rc="$?" trap - EXIT HUP INT TERM if [ "$verified" = 1 ]; then clean_session || rc=86 elif [ -n "$child" ]; then kill -KILL "$child" 2>/dev/null || : fi [ -z "$child" ] || wait "$child" 2>/dev/null || : if [ -n "$timer" ]; then kill -KILL "$timer" 2>/dev/null || :; wait "$timer" 2>/dev/null || :; fi exit "$rc" } trap finish EXIT trap 'exit 88' HUP INT TERM "$sleep_bin" "$seconds" & timer=$! "$setsid_bin" "$@" & child=$! # Allow session creation the whole inner budget, rather than a 200 ms margin. while :; do rc=0; stat_fields "$child" || rc=$? [ "$rc" = 0 ] || exit 85 if [ "$ST_SESSION" = "$child" ]; then verified=1; break; fi kill -0 "$timer" 2>/dev/null || exit 81 "$sleep_bin" 0.002 done [ "$verified" = 1 ] || exit 85 while kill -0 "$child" 2>/dev/null; do kill -0 "$timer" 2>/dev/null || exit 81 "$sleep_bin" 0.01 done kill -0 "$timer" 2>/dev/null || exit 81 rc=0; wait "$child" || rc=$? [ "$rc" = 0 ] || exit 87 exit 0 MOSAIC_TESTER_SUPERVISOR # row253-supervisor-end tester_tools() { local input target p st magic TS_BASH="" TS_SETSID="" TS_SLEEP="" TS_TIMEOUT="" TS_HEAD="" for input in /bin/bash /usr/bin/setsid /usr/bin/sleep /usr/bin/timeout /usr/bin/head; do target="$(realpath -e -- "$input" 2>/dev/null)" || return 1 case "$target" in /home/*|/root/*|/run/user/*) return 1 ;; esac [ -f "$target" ] && [ -x "$target" ] && [ ! -L "$target" ] || return 1 p="$target" while :; do st="$(stat -c '%u %a' -- "$p" 2>/dev/null)" || return 1 [ "${st% *}" = 0 ] && [[ "${st#* }" =~ ^[0-7]{3,4}$ ]] && (( (8#${st#* } & 8#022) == 0 )) || return 1 [ "$p" != / ] || break p="${p%/*}"; [ -n "$p" ] || p=/ done magic="$(/usr/bin/od -An -tx1 -N4 -- "$target" 2>/dev/null | tr -d ' \n')" [ "$magic" = 7f454c46 ] || return 1 case "$input" in /bin/bash) TS_BASH="$target" ;; /usr/bin/setsid) TS_SETSID="$target" ;; /usr/bin/sleep) TS_SLEEP="$target" ;; /usr/bin/timeout) TS_TIMEOUT="$target" ;; /usr/bin/head) TS_HEAD="$target" ;; esac done } tester_capture_reason() { case "$1" in 81) echo inner-timeout ;; 82) echo outer-timeout ;; 83) echo output-limit ;; 84) echo supervisor-tools ;; 85) echo inner-session ;; 86) echo inner-cleanup ;; *) echo inner-command ;; esac } tester_capture() ( local inner="$1" outer="$2" maximum="$3" user="$4" home="$5" path="$6" file notice child="" rc=0; shift 6 tester_tools || exit 84 file="$(mktemp)" || exit 88 notice="$(mktemp)" || { rm -f -- "$file"; exit 88; } trap 'if [ -n "$child" ]; then kill -KILL -- "-$child" 2>/dev/null || :; wait "$child" 2>/dev/null || :; fi; rm -f -- "$file" "$notice"' EXIT trap 'exit 88' HUP INT TERM LC_ALL=C "$TS_TIMEOUT" --verbose --signal=KILL "${outer}s" "$TS_BASH" -c ' set -o pipefail maximum="$1"; head_bin="$2"; shift 2 "$@" 2>/dev/null | "$head_bin" -c "$((maximum + 1))" ' -- "$maximum" "$TS_HEAD" sudo -u "$user" -H env -i "HOME=$home" "PATH=$path" "$TS_BASH" -c "$TESTER_SUPERVISOR" -- "$inner" "$TS_SETSID" "$TS_SLEEP" "$@" > "$file" 2> "$notice" & child=$! wait "$child" 2>/dev/null || rc=$? kill -KILL -- "-$child" 2>/dev/null || : # Only GNU timeout and the trusted head can write this root-side channel. if [ "$rc" = 137 ] && [[ "$(<"$notice")" == *': sending signal KILL to command '* ]]; then exit 82; fi [ "$(wc -c < "$file")" -le "$maximum" ] || exit 83 [ "$rc" = 0 ] || { case "$rc" in 81|85|86|87|88) exit "$rc" ;; *) exit 87 ;; esac; } cat -- "$file" )