#!/usr/bin/env bash
# Install a staged service binary and restart its unit. Runs as ROOT, invoked by
# Bento's deploy step over the host's executor as:
#
#   sudo /usr/local/lib/bento/install-service.sh <src> <dst> <service>
#
# where <src> is the binary Bento staged under /var/tmp/bento-deploy/<app>/,
# <dst> is the unit's ExecStart path (/usr/local/bin/pom), and <service> is the
# systemd unit to restart (pom.service).
#
# This wrapper exists so the deploy user's sudo grant is ONE auditable script
# rather than a broad install/systemctl grant on a box running production. Same
# shape, and the same reasoning, as sando/deploy/install-companion.sh.
#
# Install (one-time per host, as root):
#   sudo install -d /usr/local/lib/bento
#   sudo install -m 0755 install-service.sh /usr/local/lib/bento/install-service.sh
#   # then add the scoped sudoers line -- see bento-deploy.sudoers
#
# What this deliberately does NOT touch:
#
#   Config. pom-astra.toml and pom-hetzner.toml differ per instance, and the
#   live config on prod once carried a [targets.mnw.tests] block the repo did
#   not have. The old deploy.sh copied config over the top with no diff and no
#   backup, which is how that block would have been silently deleted. A deploy
#   ships the binary; config is a separate, deliberate act.
#
#   The unit file. Same reasoning: a hardened unit that drifted from the repo is
#   a question for a human, not something a binary deploy overwrites.
#
# The install is atomic (install(1) writes a temp then renames), so a running
# service never execs a half-written file. The previous binary is kept next to
# the new one as <dst>.prev, which is the whole rollback: put it back and
# restart. The unit is restarted only after a successful install, so a failed
# install leaves the service on the binary it already had.
set -euo pipefail

if [[ $# -ne 3 ]]; then
    echo "usage: install-service.sh <src-binary> <dst-path> <service>" >&2
    exit 2
fi

STAGING_ROOT=/var/tmp/bento-deploy

SRC="$1"
DST="$2"
SERVICE="$3"

# Guard rails. These bound what a caller -- already behind the single sudoers
# grant -- can install and restart. Being the ONLY control on a NOPASSWD grant,
# they have to actually hold, so normalise BEFORE testing any prefix: a
# glob-only test against the raw argument is something `..` walks straight out
# of. `/var/tmp/bento-deploy/../../../etc/shadow` matches a naive src pattern,
# and `/usr/local/bin/../../../etc/systemd/system/x` a naive dst one -- i.e.
# `install -m 0755` as root to anywhere on the filesystem. `realpath -m`
# resolves `..` and symlinks without requiring the path to exist (the dst does
# not on a first install). The roots are resolved too, so the comparison still
# holds if either is a symlink.
SRC="$(realpath -m -- "$SRC")"
DST="$(realpath -m -- "$DST")"
STAGING_ROOT="$(realpath -m -- "$STAGING_ROOT")"
BIN_ROOT="$(realpath -m -- /usr/local/bin)"

case "$SRC" in
    "$STAGING_ROOT"/*) : ;;
    *) echo "install-service: refusing src outside $STAGING_ROOT: $SRC" >&2; exit 3 ;;
esac
case "$DST" in
    "$BIN_ROOT"/*) : ;;
    *) echo "install-service: refusing dst outside $BIN_ROOT: $DST" >&2; exit 3 ;;
esac
case "$SERVICE" in
    *[/[:space:]]*|"") echo "install-service: bad service name: $SERVICE" >&2; exit 3 ;;
    *.service) : ;;
    *) echo "install-service: service must end in .service: $SERVICE" >&2; exit 3 ;;
esac

if [[ ! -f "$SRC" ]]; then
    echo "install-service: no binary at $SRC" >&2
    exit 4
fi

# Refuse a binary that cannot run here. Bento checks this too, before it stages
# anything, but that check reads the build host's objdump output; this one is
# the target actually trying to load it. A unit restarted onto a binary it
# cannot exec is down until someone notices.
if ! "$SRC" --version >/dev/null 2>&1; then
    echo "install-service: staged binary at $SRC will not run on this host" >&2
    "$SRC" --version || true
    exit 5
fi

if [[ -f "$DST" ]]; then
    echo "install-service: keeping the current binary as $DST.prev"
    cp -p -- "$DST" "$DST.prev"
fi

# Installs the normalised paths, not the raw arguments, so there is no gap
# between what was checked and what is written.
echo "install-service: installing $SRC -> $DST"
install -m 0755 -- "$SRC" "$DST"

echo "install-service: restarting $SERVICE"
systemctl restart "$SERVICE"
echo "install-service: done ($SERVICE live on $(basename -- "$DST"))"
