diff --git a/mc-ntfy-notify.v1.sh b/mc-ntfy-notify.v1.sh index fcd1c5a..1bc4e88 100644 --- a/mc-ntfy-notify.v1.sh +++ b/mc-ntfy-notify.v1.sh @@ -1,15 +1,18 @@ #!/usr/bin/env bash #!/bin/bash -# Beschreibung: Minecraft → ntfy Notifier (Join/Leave + optional Up/Down) +# Beschreibung: Minecraft → ntfy Notifier (Join/Leave + optional Up/Down) mit Cron-safe Locking # Autor: Patrick Asmus # Web: https://www.cleveradmin.de # Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify -# Version: 1.2 +# Version: 1.3 # Datum: 18.09.2025 -# Modifikation: Spielername in Meldungen in Anführungszeichen gesetzt; Sanitizing beibehalten +# Modifikation: Cron-sicheres Locking (Lockdir+PID), Running-Mark, Cleanup via trap ##################################################### set -euo pipefail +# Robustes PATH für Cron +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" if [[ -f "${SCRIPT_DIR}/.env" ]]; then set -a @@ -42,6 +45,14 @@ STATE_DIR="${STATE_DIR:-${SCRIPT_DIR}/state}" STATE_PLAYERS="${STATE_DIR}/players.prev" STATE_UP="${STATE_DIR}/server_up.prev" +# Lock & Running-Mark (pro MC_HOST:RCON_PORT:NTFY_TOPIC eindeutig) +# -> erlaubt mehrere parallele Instanzen für verschiedene Server/Topics +LOCK_KEY="$(printf '%s' "${MC_HOST}_${RCON_PORT}_${NTFY_TOPIC}" | tr -c 'A-Za-z0-9._-' '_')" +RUN_DIR="${RUN_DIR:-${STATE_DIR}}" +LOCK_DIR="${RUN_DIR}/lock.${LOCK_KEY}" +PID_FILE="${LOCK_DIR}/pid" +RUN_MARK="${RUN_DIR}/running.${LOCK_KEY}" + # Optional Debug DEBUG="${DEBUG:-false}" @@ -50,6 +61,44 @@ die() { echo "ERROR: $*" >&2; exit 1; } need_bin() { command -v "$1" >/dev/null 2>&1 || die "Benötigtes Tool fehlt: $1"; } dbg() { [[ "$DEBUG" == "true" ]] && echo "DBG: $*" >&2 || true; } +cleanup() { + # Lauf-Mark und Lock sauber entfernen + rm -f "$RUN_MARK" 2>/dev/null || true + rm -f "$PID_FILE" 2>/dev/null || true + rmdir "$LOCK_DIR" 2>/dev/null || true +} + +acquire_lock() { + mkdir -p "$RUN_DIR" + # Atomarer Lock-Versuch + if mkdir "$LOCK_DIR" 2>/dev/null; then + echo "$$" > "$PID_FILE" + trap cleanup EXIT INT TERM + return 0 + fi + + # Lock existiert -> prüfen, ob Prozess noch lebt + if [[ -f "$PID_FILE" ]]; then + oldpid="$(cat "$PID_FILE" 2>/dev/null || true)" + if [[ -n "${oldpid:-}" ]] && kill -0 "$oldpid" 2>/dev/null; then + # Schon aktiv → leise beenden (Cron-safe) + dbg "Bereits laufend (PID $oldpid), beende." + exit 0 + fi + fi + + # Stale Lock entfernen und erneut versuchen + rm -rf "$LOCK_DIR" 2>/dev/null || true + if mkdir "$LOCK_DIR" 2>/dev/null; then + echo "$$" > "$PID_FILE" + trap cleanup EXIT INT TERM + return 0 + fi + + # Falls wir hier landen, ist wirklich etwas schief + die "Konnte Lock nicht übernehmen: ${LOCK_DIR}" +} + ntfy_notify() { local title="$1" body="$2" tags="$3" priority="$4" [[ -z "$NTFY_SERVER" || -z "$NTFY_TOPIC" ]] && { echo "ntfy Server/Topic fehlt – skip"; return 1; } @@ -81,11 +130,8 @@ get_players() { return 1 fi dbg "RAW: $out" - out="$(printf '%s' "$out" | sanitize)" - # "There are 0 of a max of 20 players online" - # "There are 2 of a max of 20 players online: Alice, Bob" if ! grep -q "players online:" <<<"$out"; then return 0 fi @@ -105,16 +151,22 @@ get_players() { return 0 } -# ===== Checks ===== +# ===== Checks & Lock ===== need_bin curl need_bin mcrcon [[ -n "${NTFY_SERVER}" && -n "${NTFY_TOPIC}" ]] || die "NTFY_SERVER/NTFY_TOPIC nicht gesetzt" ensure_state +acquire_lock -echo "Starte Polling ${MC_HOST}:${RCON_PORT} → ntfy ${NTFY_SERVER}/${NTFY_TOPIC}" +echo "Starte Polling ${MC_HOST}:${RCON_PORT} → ntfy ${NTFY_SERVER}/${NTFY_TOPIC} (PID $$)" prev_up="$(cat "$STATE_UP")" +# ===== Main Loop ===== while :; do + # Running-Mark aktualisieren (für Monitoring/„läuft“-Check) + date -Iseconds > "$RUN_MARK" + echo "$$" >> "$RUN_MARK" + tmp_players="$(mktemp)" if get_players >"$tmp_players"; then server_up="true"