#!/bin/sh
# Install kogut on Linux and run it as a systemd service.
#
#   curl -fsSL https://kogut.dev/install | sh
#   curl -fsSL https://kogut.dev/install | sh -s -- agent --server https://kogut.example.com --token <token>
#
# The agent gets the smaller build that has only the agent, unless the installed binary
# also runs the server.
#
# Environment:
#   KOGUT_VERSION      version to install, e.g. 0.1.0 (default: latest release)
#   KOGUT_BIN_DIR      where to put the binary (default: /usr/local/bin)
#   KOGUT_NO_SERVICE   set to 1 to install only the binary
set -eu

repo="piotrkowski/kogut"
bin_dir="${KOGUT_BIN_DIR:-/usr/local/bin}"
mode="serve"
server=""
token=""

say() { printf 'kogut: %s\n' "$*"; }
die() { printf 'kogut: error: %s\n' "$*" >&2; exit 1; }

while [ $# -gt 0 ]; do
  case $1 in
    serve | server) mode="serve" ;;
    agent) mode="agent" ;;
    --server) server="${2:?--server needs a URL}"; shift ;;
    --token) token="${2:?--token needs a value}"; shift ;;
    -h | --help)
      echo "usage: install.sh [serve | agent --server <url> --token <token>]"
      exit 0 ;;
    *) die "unknown argument: $1" ;;
  esac
  shift
done

if [ "$mode" = agent ]; then
  [ -n "$server" ] || die "the agent needs --server <url>"
  [ -n "$token" ] || die "the agent needs --token <token>"
fi

[ "$(uname -s)" = Linux ] || die "only Linux is supported; on other systems use Docker"
case $(uname -m) in
  x86_64 | amd64) target="x86_64-unknown-linux-musl" ;;
  aarch64 | arm64) target="aarch64-unknown-linux-musl" ;;
  *) die "unsupported architecture: $(uname -m)" ;;
esac

if [ "$(id -u)" -eq 0 ]; then
  sudo=""
elif command -v sudo >/dev/null 2>&1; then
  sudo="sudo"
else
  die "run as root or install sudo"
fi

if command -v curl >/dev/null 2>&1; then
  fetch() { curl -fsSL -o "$2" "$1"; }
elif command -v wget >/dev/null 2>&1; then
  fetch() { wget -qO "$2" "$1"; }
else
  die "curl or wget is required"
fi

if [ -n "${KOGUT_VERSION:-}" ]; then
  base="https://github.com/$repo/releases/download/v${KOGUT_VERSION#v}"
else
  base="https://github.com/$repo/releases/latest/download"
fi
archive="kogut-$target.tar.gz"

# The agent alone gets the smaller build without the server. A host whose binary has the
# server keeps the full one, and a release without the agent archive gives the full one.
archives="$archive"
if [ "$mode" = agent ] && ! "$bin_dir/kogut" help serve >/dev/null 2>&1; then
  archives="kogut-agent-$target.tar.gz $archive"
fi

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT INT TERM

for name in $archives; do
  say "downloading $name"
  if fetch "$base/$name" "$tmp/$name" && fetch "$base/$name.sha256" "$tmp/$name.sha256"; then
    archive=$name
    break
  fi
  [ "$name" != "$archive" ] || die "cannot download $name"
  say "cannot download $name; downloading the full binary instead"
done

say "verifying the checksum"
if command -v sha256sum >/dev/null 2>&1; then
  (cd "$tmp" && sha256sum -c "$archive.sha256" >/dev/null) || die "checksum mismatch"
else
  (cd "$tmp" && shasum -a 256 -c "$archive.sha256" >/dev/null) || die "checksum mismatch"
fi

tar -xzf "$tmp/$archive" -C "$tmp" kogut
$sudo install -m 0755 "$tmp/kogut" "$bin_dir/kogut"
say "installed $("$bin_dir/kogut" --version) to $bin_dir/kogut"

if [ "${KOGUT_NO_SERVICE:-0}" = 1 ] || ! command -v systemctl >/dev/null 2>&1; then
  say "no systemd service installed; run '$bin_dir/kogut $mode' yourself"
  exit 0
fi

if [ "$mode" = serve ]; then
  $sudo tee /etc/systemd/system/kogut.service >/dev/null <<EOF
[Unit]
Description=kogut monitoring server
Documentation=https://github.com/$repo
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=$bin_dir/kogut serve
Environment=KOGUT_DATA=/var/lib/kogut
Environment=KOGUT_LISTEN=0.0.0.0:8484
DynamicUser=yes
StateDirectory=kogut
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
AmbientCapabilities=CAP_NET_RAW

[Install]
WantedBy=multi-user.target
EOF
  unit="kogut"
else
  $sudo mkdir -p /etc/kogut
  printf 'KOGUT_SERVER=%s\nKOGUT_TOKEN=%s\n' "$server" "$token" | $sudo tee /etc/kogut/agent.env >/dev/null
  $sudo chmod 0600 /etc/kogut/agent.env
  groups=""
  for g in docker systemd-journal; do
    getent group "$g" >/dev/null 2>&1 && groups="$groups $g"
  done
  $sudo tee /etc/systemd/system/kogut-agent.service >/dev/null <<EOF
[Unit]
Description=kogut agent
Documentation=https://github.com/$repo
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=$bin_dir/kogut agent
EnvironmentFile=/etc/kogut/agent.env
Environment=KOGUT_STATE_DIR=/var/lib/kogut-agent
DynamicUser=yes
StateDirectory=kogut-agent
SupplementaryGroups=$groups
Restart=always
RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=yes
AmbientCapabilities=CAP_NET_RAW

[Install]
WantedBy=multi-user.target
EOF
  unit="kogut-agent"
fi

$sudo systemctl daemon-reload
$sudo systemctl enable --now "$unit" >/dev/null
say "started $unit.service"

if [ "$mode" = serve ]; then
  say "open http://$(hostname -f 2>/dev/null || hostname):8484 to finish the setup"
else
  say "the agent is connecting to $server"
fi
