redirector: commit stamp + shared build-identity verifier that REFUSES
The binary records only the commit it was built from -- no dirty-tree flag. Cargo will not re-run a build script because another crate's source changed, so a compiled-in 'clean' claim can be stale and is not a safeguard; that was verified on the Blaze host. scripts/verify-build-identity.sh establishes both facts at LAUNCH, where they cannot go stale: the stamped commit equals HEAD, and the migration crates are clean. It REFUSES rather than warns, because for a migration gate a warning on stderr is something to scroll past. --identity prints the stamp without valid configuration. The launcher must be able to establish which commit a binary came from BEFORE deciding whether to run it; requiring a correct environment first would invert the check. redirector.sh mirrors sidecar.sh: refuses to start with an orphan present or the port busy, matches the resolved executable rather than the command line (pgrep -f matches any shell mentioning the name), and stop PROVES the process is gone and the port free. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lifecycle for the Rust redirector host.
|
||||
#
|
||||
# redirector.sh start | stop | status | verify
|
||||
#
|
||||
# Mirrors sidecar.sh: refuses to start with an orphan present or the port busy,
|
||||
# and stop PROVES the process is gone and the port free rather than assuming a
|
||||
# signal worked.
|
||||
#
|
||||
# Additionally REFUSES TO START unless the binary's stamped commit equals HEAD
|
||||
# and the migration crates are clean — evidence from an unidentifiable binary is
|
||||
# not evidence.
|
||||
set -uo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
||||
ROOT="$(cd "$HERE/.." && pwd)"
|
||||
RUNDIR="${OPENFUT_REDIRECTOR_RUNDIR:-${TMPDIR:-/tmp}/openfut-redirector}"
|
||||
PIDFILE="$RUNDIR/redirector.pid"
|
||||
PORTFILE="$RUNDIR/redirector.port"
|
||||
LOGFILE="${OPENFUT_REDIRECTOR_LOG:-$RUNDIR/redirector.log}"
|
||||
BIN="$ROOT/target/debug/openfut-redirector-host"
|
||||
[[ -x "$BIN" ]] || BIN="$ROOT/target/release/openfut-redirector-host"
|
||||
|
||||
die() { echo "redirector: $*" >&2; exit 1; }
|
||||
pid_alive() { kill -0 "$1" 2>/dev/null; }
|
||||
port_listening() { ss -ltn 2>/dev/null | grep -qE "[:.]${1}[[:space:]]"; }
|
||||
|
||||
# Match the resolved executable, not the command line: `pgrep -f` matches any
|
||||
# shell whose arguments merely mention the name.
|
||||
list_procs() {
|
||||
local self=$$ pid exe
|
||||
for d in /proc/[0-9]*; do
|
||||
pid="${d#/proc/}"; [[ "$pid" == "$self" ]] && continue
|
||||
exe="$(readlink -f "$d/exe" 2>/dev/null)" || continue
|
||||
[[ "${exe##*/}" == "openfut-redirector-host" ]] && echo "$pid"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# The binary prints `commit=<sha>` in its banner; ask it rather than guessing.
|
||||
stamped_commit() { "$BIN" --identity 2>&1 | sed -nE 's/.*commit=([0-9a-f]+).*/\1/p' | head -1; }
|
||||
|
||||
cmd_verify() {
|
||||
local c; c="$(stamped_commit)"
|
||||
[[ -n "$c" ]] || die "could not read the binary's commit stamp"
|
||||
"$ROOT/scripts/verify-build-identity.sh" "$c"
|
||||
}
|
||||
|
||||
cmd_start() {
|
||||
[[ -x "$BIN" ]] || die "not built: cargo build -p openfut-redirector-host"
|
||||
: "${OPENFUT_REDIRECTOR_HOST_PORT:?set OPENFUT_REDIRECTOR_HOST_PORT (no default: runs beside Python)}"
|
||||
local strays; strays="$(list_procs)"
|
||||
[[ -z "$strays" ]] || die "orphan redirector process(es): $strays"
|
||||
port_listening "$OPENFUT_REDIRECTOR_HOST_PORT" && die "port $OPENFUT_REDIRECTOR_HOST_PORT in use"
|
||||
|
||||
cmd_verify || die "build identity check failed — refusing to start"
|
||||
|
||||
mkdir -p "$RUNDIR"; echo "$OPENFUT_REDIRECTOR_HOST_PORT" > "$PORTFILE"
|
||||
"$BIN" >"$LOGFILE" 2>&1 &
|
||||
local pid=$!; echo "$pid" > "$PIDFILE"
|
||||
local w=0
|
||||
while (( w < 100 )); do
|
||||
pid_alive "$pid" || { echo "died during startup:" >&2; tail -20 "$LOGFILE" >&2; rm -f "$PIDFILE"; return 1; }
|
||||
if port_listening "$OPENFUT_REDIRECTOR_HOST_PORT"; then
|
||||
echo "redirector started: pid $pid, port $OPENFUT_REDIRECTOR_HOST_PORT"
|
||||
grep -E 'SELF-TEST|openfut-redirector-host v' "$LOGFILE" | sed 's/^/ /'
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1; w=$((w+1))
|
||||
done
|
||||
echo "did not listen within 10s:" >&2; tail -20 "$LOGFILE" >&2
|
||||
kill "$pid" 2>/dev/null; rm -f "$PIDFILE"; return 1
|
||||
}
|
||||
|
||||
cmd_stop() {
|
||||
local rc=0 pid="" port=""
|
||||
[[ -f "$PIDFILE" ]] && pid="$(cat "$PIDFILE")"
|
||||
[[ -f "$PORTFILE" ]] && port="$(cat "$PORTFILE")"
|
||||
if [[ -n "$pid" ]] && pid_alive "$pid"; then
|
||||
kill "$pid" 2>/dev/null
|
||||
local w=0; while pid_alive "$pid" && (( w < 50 )); do sleep 0.1; w=$((w+1)); done
|
||||
pid_alive "$pid" && kill -9 "$pid" 2>/dev/null
|
||||
sleep 0.2
|
||||
fi
|
||||
[[ -n "$pid" ]] && pid_alive "$pid" && { echo "FAILED to stop $pid" >&2; rc=1; }
|
||||
[[ -n "$port" ]] && port_listening "$port" && { echo "FAILED: port $port still listening" >&2; rc=1; }
|
||||
local strays; strays="$(list_procs)"
|
||||
[[ -n "$strays" ]] && { echo "FAILED: still running: $strays" >&2; rc=1; }
|
||||
rm -f "$PIDFILE" "$PORTFILE"
|
||||
[[ $rc -eq 0 ]] && echo "redirector stopped and verified gone${pid:+ (pid $pid)}"
|
||||
return $rc
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
if [[ -f "$PIDFILE" ]] && pid_alive "$(cat "$PIDFILE")"; then
|
||||
echo "running: pid $(cat "$PIDFILE"), port $(cat "$PORTFILE" 2>/dev/null || echo '?')"
|
||||
else
|
||||
echo "not running"
|
||||
fi
|
||||
local strays; strays="$(list_procs)"
|
||||
[[ -n "$strays" ]] && echo "redirector processes: $strays"
|
||||
return 0
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
start) cmd_start ;;
|
||||
stop) cmd_stop ;;
|
||||
status) cmd_status ;;
|
||||
verify) cmd_verify ;;
|
||||
*) sed -n '2,6p' "$0" | sed 's/^# \?//'; exit 2 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user