#!/bin/sh
#
# Pair this machine with a PingMon account.
#
# Run by the package's postinst when the install is interactive, and available afterwards to
# re-pair a host or to pair one that was installed unattended.
#
# POSIX sh, no external commands beyond coreutils and systemctl. The whole point of the agent is
# that it installs on a remote box without dragging in a dependency tree, and a setup script
# that needed bash, curl or python would undo that on exactly the minimal images where it
# matters most.
#
# Reads from /dev/tty rather than stdin: dpkg gives a maintainer script a stdin of its own, so a
# plain `read` would return EOF instantly and the wizard would appear to skip itself.

set -eu

BINARY=/usr/bin/pingmon-agent
CONFIG=/etc/pingmon/agent.json
SERVICE=pingmon-agent

SERVER="${PINGMON_SERVER:-}"
TOKEN="${PINGMON_TOKEN:-}"
ASSUME_YES=0

usage() {
    cat <<EOF
Pair this machine with a PingMon account.

Usage:
  pingmon-agent-setup                       interactive
  pingmon-agent-setup --server URL --token KEY --yes

Options:
  --server URL   PingMon address, e.g. https://acme.pingmon.tech
  --token KEY    Enrolment key, created under Agents in the PingMon UI
  --yes          Do not prompt; fail rather than ask
  --help         This text

Environment:
  PINGMON_SERVER, PINGMON_TOKEN are used when the matching option is absent,
  which is how an unattended install or a machine image supplies them.
EOF
}

while [ $# -gt 0 ]; do
    case "$1" in
        --server) SERVER="${2:-}"; shift 2 ;;
        --token)  TOKEN="${2:-}";  shift 2 ;;
        --yes|-y) ASSUME_YES=1;    shift ;;
        --help|-h) usage; exit 0 ;;
        *) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
    esac
done

if [ "$(id -u)" != "0" ]; then
    echo "pingmon-agent-setup must run as root (it writes $CONFIG)." >&2
    exit 1
fi

# A terminal to talk to. Absent under an unattended install, which is not an error — it just
# means the answers have to have come from options or the environment.
#
# Opened rather than stat'd: /dev/tty is present and readable-looking on every system, but
# opening it fails with ENXIO when the process has no controlling terminal, and that is the
# distinction being made here.
if (exec 3< /dev/tty) 2>/dev/null; then
    HAVE_TTY=1
else
    HAVE_TTY=0
fi

say() { if [ "$HAVE_TTY" = "1" ]; then printf '%s\n' "$1" > /dev/tty; else printf '%s\n' "$1"; fi }

ask() {
    # ask <prompt> <default>; answer on stdout, prompt on the tty so it is not captured.
    _prompt="$1"
    _default="${2:-}"
    if [ -n "$_default" ]; then
        printf '%s [%s]: ' "$_prompt" "$_default" > /dev/tty
    else
        printf '%s: ' "$_prompt" > /dev/tty
    fi
    IFS= read -r _answer < /dev/tty || _answer=""
    [ -n "$_answer" ] || _answer="$_default"
    printf '%s' "$_answer"
}

trim() { printf '%s' "$1" | tr -d ' \t\r\n'; }

already_enrolled() {
    [ -f "$CONFIG" ] && grep -q '"agent_token"' "$CONFIG" 2>/dev/null
}

say ""
say "  PingMon agent setup"
say "  ==================="
say ""

if already_enrolled; then
    say "This machine is already paired:"
    if [ "$HAVE_TTY" = "1" ]; then
        "$BINARY" status > /dev/tty 2>&1 || true
    fi
    say ""

    if [ -n "$SERVER" ] && [ -n "$TOKEN" ]; then
        say "Re-pairing with the details given on the command line."
    elif [ "$ASSUME_YES" = "1" ] || [ "$HAVE_TTY" = "0" ]; then
        say "Nothing to do. Run pingmon-agent-setup interactively to re-pair."
        exit 0
    else
        answer=$(ask "Pair again with a different account? [y/N]" "n")
        case "$answer" in
            y|Y|yes|YES) : ;;
            *) say "Left as it is."; exit 0 ;;
        esac
    fi
fi

if [ -z "$SERVER" ]; then
    if [ "$HAVE_TTY" = "0" ] || [ "$ASSUME_YES" = "1" ]; then
        echo "error: --server is required for an unattended setup." >&2
        exit 1
    fi
    say "Your PingMon address — the URL you sign in at."
    SERVER=$(ask "  Server URL" "https://")
fi

SERVER=$(trim "$SERVER")
case "$SERVER" in
    https://*|http://*) : ;;
    "" ) echo "error: no server URL given." >&2; exit 1 ;;
    # Typing the bare hostname is the overwhelmingly common slip, and guessing https is both
    # right and safe — the agent refuses to send its token over a plain-text connection anyway.
    *) SERVER="https://$SERVER"; say "  Using $SERVER" ;;
esac

if [ -z "$TOKEN" ]; then
    if [ "$HAVE_TTY" = "0" ] || [ "$ASSUME_YES" = "1" ]; then
        echo "error: --token is required for an unattended setup." >&2
        exit 1
    fi
    say ""
    say "Your enrolment key — create one in PingMon under Agents > New enrolment key."
    say "One key can pair as many machines as you like."
    TOKEN=$(ask "  Enrolment key" "")
fi

TOKEN=$(trim "$TOKEN")
if [ -z "$TOKEN" ]; then
    echo "error: no enrolment key given." >&2
    exit 1
fi

say ""
say "Pairing …"

# The binary does the actual enrolment: it is what holds the token afterwards, and it knows how
# to identify this machine so a re-install re-attaches instead of creating a duplicate device.
if ! "$BINARY" enroll --server "$SERVER" --token "$TOKEN"; then
    say ""
    say "Pairing failed. The usual causes:"
    say "  - the enrolment key was mistyped, has expired, or has been revoked"
    say "  - this machine cannot reach $SERVER (try: $BINARY probe <server host>)"
    exit 1
fi

if command -v systemctl > /dev/null 2>&1 && [ -d /run/systemd/system ]; then
    systemctl enable "$SERVICE" > /dev/null 2>&1 || true
    systemctl restart "$SERVICE"

    say ""
    say "Done. The agent is running and will start on boot."
    say ""
    say "  Status:  systemctl status $SERVICE"
    say "  Logs:    journalctl -u $SERVICE -f"
else
    say ""
    say "Done. There is no systemd here, so start the agent yourself:"
    say "  $BINARY run"
fi

say ""
say "It reports this host's metrics, and probes the PingMon devices it can"
say "reach from this network — those are claimed automatically as they answer."
say ""
