#!/usr/bin/env bash
# Install a staged companion binary and restart its unit. Runs as ROOT, invoked
# by Sando's deploy step over the node's executor as:
#
#   sudo /usr/local/lib/mnw/install-companion.sh <src> <dst> <service>
#
# where <src> is the companion binary inside the just-rsynced release bundle
# (e.g. /opt/mnw/releases/<ver>/companions/mnw-cli), <dst> is the unit's
# ExecStart path (e.g. /opt/mnw-cli/mnw-cli), and <service> is the systemd unit
# to restart (e.g. mnw-cli.service). This wrapper exists so the deploy user's
# sudo grant is ONE auditable script rather than a broad install/systemctl grant.
#
# Install (one-time per node, as root):
#   sudo install -d /usr/local/lib/mnw
#   sudo install -m 0755 install-companion.sh /usr/local/lib/mnw/install-companion.sh
#   # then add the scoped sudoers line — see mnw-companion.sudoers
#
# The install is atomic (install(1) writes a temp then renames), so a running
# companion never execs a half-written file. The service is restarted only after
# a successful install; a failed install leaves the unit on its current binary.
set -euo pipefail

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

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

# Guard rails: refuse a src outside a release bundle or a dst outside /opt, and a
# service name that isn't a bare unit. These bound what a caller (already behind
# the single sudoers grant) can install/restart — defense in depth, not the
# primary control. Being the ONLY control on a NOPASSWD grant, though, they have
# to actually hold.
#
# Normalise BEFORE testing the prefix. These were once glob-only tests against
# the raw arguments, which `..` walks straight out of: `/opt/../etc/systemd/
# system/x.service` matches `/opt/*`, and `.../releases/v/companions/../../../
# ../etc/shadow` matches the src pattern — 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 usually does not on a first install).
#
# /opt itself is resolved too, so the comparison still holds if it is a symlink.
SRC="$(realpath -m -- "$SRC")"
DST="$(realpath -m -- "$DST")"
OPT_ROOT="$(realpath -m -- /opt)"

case "$SRC" in
    */releases/*/companions/*) : ;;
    *) echo "install-companion: refusing src outside a release bundle: $SRC" >&2; exit 3 ;;
esac
case "$DST" in
    "$OPT_ROOT"/*) : ;;
    *) echo "install-companion: refusing dst outside $OPT_ROOT: $DST" >&2; exit 3 ;;
esac
case "$SERVICE" in
    *[/[:space:]]*|"") echo "install-companion: bad service name: $SERVICE" >&2; exit 3 ;;
    *.service) : ;;
    *) echo "install-companion: service must end in .service: $SERVICE" >&2; exit 3 ;;
esac

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

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

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