#!/usr/bin/env bash # Is the FIFA client currently connected to anything? # # client-state.sh [client-ip] # # WHY THIS EXISTS # # Host-side `ss` CANNOT see the Python backend's connections. The Python # responders run inside a container, so a client session terminates at # 172.20.0.2:42130 in the container's network namespace; the host only ever sees # the NAT'd flow, and a plain `ss | grep ` on the host reports nothing. # # That produced a wrong precondition check: "no .105 Blaze session — closed" was # reported while FIFA was mid-session on Python, and gate 9 was armed against a # client that had never exited. Python's own log gave it away — it logs closes # reliably (45 of them) and there was no close for that session. # # So this looks in BOTH namespaces, and reports the Rust sidecar and the Python # container separately. # # Exit 0 when the client has no live session anywhere (safe to start a gate), # 1 when it does. set -uo pipefail CLIENT="${1:-${OPENFUT_CLIENT_IP:-}}" if [[ -z "$CLIENT" ]]; then echo "usage: client-state.sh (or set OPENFUT_CLIENT_IP)" >&2 echo " no default: the lab's address is deployment config, not architecture," >&2 echo " and a default that matches the current lab hides the coupling." >&2 exit 2 fi CONTAINER="${OPENFUT_PY_CONTAINER:-openfut-fut-backend}" live=0 decode_tcp() { # /proc/net/tcp rows -> "local remote state", little-endian hex addresses. python3 -c " import sys def d(x): ip, port = x.split(':') return '.'.join(str(int(ip[i:i+2], 16)) for i in (6, 4, 2, 0)) + ':' + str(int(port, 16)) for line in sys.stdin: f = line.split() if len(f) < 4 or not f[0].endswith(':'): continue try: print(d(f[1]), d(f[2]), f[3]) except Exception: pass " } echo "client: $CLIENT" # ---- Rust sidecar (host namespace) rust="$(ss -tn state established 2>/dev/null | grep -F "$CLIENT" | grep -E ':42230' || true)" if [[ -n "$rust" ]]; then echo " RUST sidecar : LIVE session(s)" sed 's/^/ /' <<<"$rust" live=1 else echo " RUST sidecar : none" fi # ---- Python backend (container namespace) if docker exec "$CONTAINER" true 2>/dev/null; then py="$(docker exec "$CONTAINER" cat /proc/net/tcp 2>/dev/null | decode_tcp \ | awk '$3=="01"' | grep -F "$CLIENT" || true)" if [[ -n "$py" ]]; then echo " PYTHON backend: LIVE session(s)" awk '{printf " %-22s <- %-22s ESTABLISHED\n", $1, $2}' <<<"$py" live=1 else echo " PYTHON backend: none" fi else echo " PYTHON backend: container '$CONTAINER' not reachable — CANNOT confirm" live=1 # unknown is not the same as clear fi echo if [[ $live -eq 0 ]]; then echo "RESULT: no live client session — safe to begin a gate" else echo "RESULT: client still connected — close FIFA before starting a gate" fi exit $live