#!/bin/sh # Start the host, and decide what to do when it will not start. # # novox/hq ADR 0061. The init is asked for two things — start this at boot, start it again if it # exits — and everything else is here, because this is the one piece that has to work on a # machine where the host does not. Unit-file syntax cannot be tested; this can. # # POSIX sh, no bashisms, nothing that has to be installed. set -eu STATE_DIR="${MESH_HOST_STATE_DIR:-/var/lib/mesh-host}" LIBEXEC="${MESH_HOST_LIBEXEC:-/usr/lib/nox-mesh-host}" HOST="${MESH_HOST_BIN:-/usr/bin/nox-mesh-host}" LIMIT="${MESH_HOST_START_LIMIT:-3}" ATTEMPTS="$STATE_DIR/start-attempts" HALTED="$STATE_DIR/halted" say() { echo "nox-mesh-host-launch: $*" >&2; } mkdir -p "$STATE_DIR" # Halted: rolled back once and the previous version failed too, so the binary is not the problem. # Nothing further is tried automatically. Exit zero — a supervisor restarting this forever is a # slow visible loop rather than a crash loop, and the node stays down until a person looks. if [ -e "$HALTED" ]; then say "halted: $(cat "$HALTED" 2>/dev/null || echo 'reason not recorded')" say "not starting the host. this node needs a person." exit 0 fi # Read the FIRST FIELD, then insist it is a plain integer. # # Stripping whitespace instead concatenates, and that is not a hypothetical: a counter file # holding "1 2" became "12", which is past the limit, so a healthy node rolled itself back. An # unreadable counter must fail towards "start normally", never towards "give up". count=0 if [ -s "$ATTEMPTS" ]; then read -r count _ < "$ATTEMPTS" 2>/dev/null || count=0 fi case "${count:-}" in '' | *[!0-9]*) count=0 ;; esac count=$((count + 1)) printf '%s\n' "$count" > "$ATTEMPTS" if [ "$count" -gt "$LIMIT" ]; then # The host has failed to get through a reconcile $LIMIT times running. The counter is # cleared by the host itself on success, so reaching here means none of those starts # worked — not that the machine has been up a long time. if [ -e "$STATE_DIR/rollback-attempted" ]; then say "the host failed $count times after a rollback. the previous version does not" say "start either, so this is the machine and not the binary." printf 'rolled back and still failing\n' > "$HALTED" exit 0 fi say "the host failed $count times. rolling back." if "$LIBEXEC/rollback"; then # Fresh count for the version we just installed: it deserves its own attempts, and # without this it inherits a count already over the limit and halts immediately. printf '0\n' > "$ATTEMPTS" else say "rollback failed. halting rather than restarting into the same failure." printf 'rollback failed\n' > "$HALTED" exit 0 fi fi # exec, so the host is what the supervisor watches and signals reach it directly. exec "$HOST" run