#!/bin/sh
# Installs or updates the MiningMonitor agent on Linux.
#
#   curl -fsSL https://cylvaro.cloud/install.sh | sudo MININGMONITOR_ENROLL_TOKEN=... sh
#
# Options, after "sh -s --":
#   --accept-dev-fee   accept the developer fee and data notice without a prompt
#   --force            replace an existing enrollment
#   --uninstall        remove the agent, its service and its config

set -eu

DOWNLOAD_URL="${MININGMONITOR_DOWNLOAD_URL:-https://github.com/Paul198925/miningmonitor-releases/releases/latest/download}"
SERVER="${MININGMONITOR_SERVER:-https://api.cylvaro.cloud}"
BIN=/usr/local/bin/miningmonitor-agent
CONFIG_DIR=/etc/miningmonitor
CONFIG="$CONFIG_DIR/agent.json"
UNIT=/etc/systemd/system/miningmonitor-agent.service
UPDATE_UNIT=/etc/systemd/system/miningmonitor-update.service
UPDATE_TIMER=/etc/systemd/system/miningmonitor-update.timer
USER_NAME=miningmonitor
PID_FILE=/run/miningmonitor-agent.pid
LOG_FILE=/var/log/miningmonitor-agent.log

accept_dev_fee=false
force=false
uninstall=false

fail() {
	echo "Fehler: $*" >&2
	exit 1
}

has_systemd() {
	[ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1
}

# Stops an agent started without systemd, also one that was started by hand.
stop_background_agent() {
	if [ -f "$PID_FILE" ]; then
		kill "$(cat "$PID_FILE")" 2>/dev/null || true
		rm -f "$PID_FILE"
	fi
	if command -v pkill >/dev/null 2>&1; then
		pkill -f "$BIN (supervise|run)" 2>/dev/null || true
		sleep 1
	fi
}

install_requirements() {
	if command -v apt-get >/dev/null 2>&1; then
		echo "Aktualisiere die Paketliste ..."
		DEBIAN_FRONTEND=noninteractive apt-get update -qq >/dev/null 2>&1 ||
			echo "Hinweis: apt-get update ist fehlgeschlagen, es geht trotzdem weiter." >&2
		DEBIAN_FRONTEND=noninteractive apt-get install -y -qq curl ca-certificates coreutils pciutils >/dev/null 2>&1 ||
			echo "Hinweis: Pakete konnten nicht installiert werden." >&2
	elif command -v dnf >/dev/null 2>&1; then
		dnf install -y -q curl ca-certificates coreutils pciutils >/dev/null 2>&1 ||
			echo "Hinweis: Pakete konnten nicht installiert werden." >&2
	fi
}

for arg in "$@"; do
	case "$arg" in
	--accept-dev-fee) accept_dev_fee=true ;;
	--force) force=true ;;
	--uninstall) uninstall=true ;;
	*) fail "unbekannte Option: $arg" ;;
	esac
done

[ "$(id -u)" -eq 0 ] || fail "bitte mit sudo ausführen"
[ "$(uname -s)" = Linux ] || fail "nur Linux wird unterstützt"

if [ "$uninstall" = true ]; then
	stop_background_agent
	if has_systemd; then
		systemctl disable --now miningmonitor-update.timer 2>/dev/null || true
		systemctl disable --now miningmonitor-agent 2>/dev/null || true
	fi
	rm -f "$UNIT" "$UPDATE_UNIT" "$UPDATE_TIMER" "$BIN" "$BIN.prev" "$BIN.new" "$BIN.failed-version" "$LOG_FILE"
	rm -rf "$CONFIG_DIR"
	if has_systemd; then
		systemctl daemon-reload
	fi
	if id "$USER_NAME" >/dev/null 2>&1; then
		userdel "$USER_NAME" 2>/dev/null || true
	fi
	echo "MiningMonitor wurde entfernt. Das Rig bleibt im Dashboard, bis du es dort löschst."
	exit 0
fi

case "$(uname -m)" in
x86_64 | amd64) arch=amd64 ;;
aarch64 | arm64) arch=arm64 ;;
*) fail "nicht unterstützte Architektur: $(uname -m)" ;;
esac

token="${MININGMONITOR_ENROLL_TOKEN:-}"
enroll=true
if [ -f "$CONFIG" ] && [ "$force" = false ]; then
	enroll=false
elif [ -z "$token" ]; then
	fail "kein Token gefunden: erstelle im Dashboard einen neuen Befehl"
fi

if [ "$enroll" = true ] && [ "$accept_dev_fee" = false ]; then
	cat <<'EOF'

MiningMonitor ist kostenlos und finanziert sich über eine Dev-Fee:
  - 1 % der Mining-Zeit mint dein Rig für die Entwickler.
  - Das sind 4:48 Minuten nach je 8 Stunden Mining.
  - Der Strom dafür geht auf deine Kosten.
  - Die Fee ist im Dashboard und im Log sichtbar.

Außerdem sendet der Agent Hardware-Infos, Messwerte und Fehlermeldungen
von Agent und Minern an MiningMonitor, um Fehler beheben zu können.
Tokens, Passwörter und Wallet-Adressen werden vorher entfernt.

EOF
	if [ ! -r /dev/tty ] || [ ! -w /dev/tty ]; then
		fail "keine Eingabe möglich: mit --accept-dev-fee zustimmen, wenn du einverstanden bist"
	fi
	printf 'Stimmst du dem zu? [j/N] ' >/dev/tty
	read -r answer </dev/tty || answer=
	case "$answer" in
	j | J | ja | Ja | JA | y | Y | yes) ;;
	*) fail "ohne Zustimmung wird nichts installiert" ;;
	esac
fi

install_requirements
command -v curl >/dev/null 2>&1 || fail "curl fehlt"
command -v sha256sum >/dev/null 2>&1 || fail "sha256sum fehlt"

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

asset="miningmonitor-agent-linux-$arch"
echo "Lade $asset ..."
curl -fsSL "$DOWNLOAD_URL/$asset" -o "$tmp/$asset" || fail "Download des Agents fehlgeschlagen"
curl -fsSL "$DOWNLOAD_URL/SHA256SUMS" -o "$tmp/SHA256SUMS" || fail "Download der Prüfsummen fehlgeschlagen"
grep -q " $asset\$" "$tmp/SHA256SUMS" || fail "keine Prüfsumme für $asset gefunden"
(cd "$tmp" && grep " $asset\$" SHA256SUMS | sha256sum -c - >/dev/null) || fail "Prüfsumme stimmt nicht, Installation abgebrochen"

if ! id "$USER_NAME" >/dev/null 2>&1; then
	useradd --system --home-dir /nonexistent --shell /usr/sbin/nologin "$USER_NAME" 2>/dev/null ||
		useradd --system --home-dir /nonexistent --shell /sbin/nologin "$USER_NAME" ||
		fail "Benutzer $USER_NAME konnte nicht angelegt werden"
fi

# A running binary cannot be overwritten, and a restart below picks up the new one.
stop_background_agent
install -m 0755 "$tmp/$asset" "$BIN"
install -d -m 0750 -o root -g "$USER_NAME" "$CONFIG_DIR"

if [ "$enroll" = true ]; then
	force_flag=
	[ "$force" = true ] && force_flag=--force
	# shellcheck disable=SC2086 # force_flag is empty or a single word.
	MININGMONITOR_ENROLL_TOKEN="$token" "$BIN" enroll --server "$SERVER" --accept-dev-fee --config "$CONFIG" $force_flag ||
		fail "Anmeldung fehlgeschlagen: erstelle im Dashboard einen neuen Befehl"
fi
chown root:"$USER_NAME" "$CONFIG"
chmod 0640 "$CONFIG"

if ! has_systemd; then
	touch "$LOG_FILE"
	chown "$USER_NAME" "$LOG_FILE"
	chmod 0640 "$LOG_FILE"
	unset MININGMONITOR_ENROLL_TOKEN
	"$BIN" supervise --config "$CONFIG" >>"$LOG_FILE" 2>&1 </dev/null &
	echo "$!" >"$PID_FILE"
	sleep 2
	if ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
		fail "der Agent läuft nicht: prüfe $LOG_FILE"
	fi
	echo "Fertig. Der Agent läuft im Hintergrund und aktualisiert sich selbst, Log: $LOG_FILE"
	echo "Hinweis: Dieses System hat kein systemd, zum Beispiel weil es ein Container ist."
	echo "Nach einem Neustart des Systems führe den Installer ohne Token erneut aus:"
	echo "  curl -fsSL https://cylvaro.cloud/install.sh | sh"
	exit 0
fi

cat >"$UNIT" <<EOF
[Unit]
Description=MiningMonitor agent
After=network-online.target
Wants=network-online.target

[Service]
User=$USER_NAME
ExecStart=$BIN run --config $CONFIG
Restart=on-failure
RestartSec=10
RestartPreventExitStatus=78
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectControlGroups=true

[Install]
WantedBy=multi-user.target
EOF

cat >"$UPDATE_UNIT" <<EOF
[Unit]
Description=MiningMonitor agent update
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=$BIN self-update --config $CONFIG
ProtectHome=true
PrivateTmp=true
EOF

cat >"$UPDATE_TIMER" <<EOF
[Unit]
Description=Check for MiningMonitor agent updates

[Timer]
OnBootSec=2min
OnUnitActiveSec=15min
RandomizedDelaySec=3min
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable miningmonitor-agent >/dev/null 2>&1
systemctl enable --now miningmonitor-update.timer >/dev/null 2>&1
systemctl restart miningmonitor-agent
sleep 2
systemctl is-active --quiet miningmonitor-agent ||
	fail "der Dienst läuft nicht: prüfe 'journalctl -u miningmonitor-agent'"

echo "Fertig. Dein Rig erscheint jetzt im Dashboard."
echo "Entfernen: curl -fsSL https://cylvaro.cloud/install.sh | sudo sh -s -- --uninstall"
