⚙️ wartung-eos.sh
#!/bin/bash
# ------------------------------------------------------------------------
# Menü für Server Wartung & Pflege-Skript (EndeavourOS ARM, Raspberry Pi 4)
#
# Zweck:
# Zentrales, interaktives Wartungs- und Kontrollskript für einen
# headless betriebenen Raspberry Pi 4 im Heimnetz.
# Das Skript bündelt typische Administrations- und Wartungsaufgaben
# in einem übersichtlichen Menü.
#
# Aufruf:
# bash wartung-eos.sh (NICHT mit sudo ausführen)
#
# Eigenschaften:
# - läuft bewusst als normaler Benutzer
# - nutzt sudo nur gezielt für benötigte Aktionen
# - keine automatischen Änderungen ohne Bestätigung
# - geeignet für Dauerbetrieb und Remote-Administration
#
# Voraussetzungen:
# - Arch Linux / EndeavourOS ARM
# - pacman (Pflicht), yay (optional für AUR)
# - ioBroker inkl. iob CLI
# - Pi-hole 6 (pihole-FTL Service)
# - Unbound DNS Resolver
# * /etc/unbound/trusted-key.key
# * /etc/unbound/root.hints
# - optional: htop, fastfetch
#
# Dienste:
# - ioBroker
# - Pi-hole
# - Unbound
# - Syncthing (Status & Logs)
#
# Einsatzbereich:
# - Heimserver
# - Headless-Systeme
# - Kein Portforwarding erforderlich
#
# ------------------------------------------------------------------------
# Version
SCRIPT_VERSION="2025-12-15"
# Farben (fallback ohne Farben, wenn tput fehlt oder TERM dumm ist)
if command -v tput >/dev/null 2>&1 && [ -n "$TERM" ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
gruen=$(tput setaf 2); rot=$(tput setaf 1); gelb=$(tput setaf 3); reset=$(tput sgr0)
else
gruen=""; rot=""; gelb=""; reset=""
fi
# --- Sicherheitsprüfung: Script darf NICHT mit sudo oder als root laufen ---
if [ "$EUID" -eq 0 ] || [ -n "${SUDO_USER:-}" ]; then
echo
echo "${rot}Fehler:${reset} Dieses Skript darf nicht mit sudo oder als root ausgeführt werden!"
echo "Bitte starte es normal als Benutzer mit sudo-Rechten, z.B.:"
echo " bash wartung-eos.sh"
echo
exit 1
fi
# Versionsbanner (vor sudo)
clear
echo
echo "─────────────────────────────────────────────────────────────────"
echo " EndeavourOS ARM Wartung – Pi 4 (ioBroker + Pi-hole 6 + Unbound)"
echo " Version: ${gruen}${SCRIPT_VERSION}${reset}"
echo "─────────────────────────────────────────────────────────────────"
echo
# Systemd-Pager deaktivieren (verhindert "q"-Abfragen bei journalctl)
export SYSTEMD_PAGER=cat
echo "Pager für systemd-Befehle deaktiviert (SYSTEMD_PAGER=cat)"
echo
# Unbound-Status
unbound_msgs=()
systemctl is-active --quiet unbound || unbound_msgs+=("INAKTIV")
[ -f /etc/unbound/trusted-key.key ] || unbound_msgs+=("FEHLT trusted-key.key")
[ -f /etc/unbound/root.hints ] || unbound_msgs+=("FEHLT root.hints")
if [ ${#unbound_msgs[@]} -eq 0 ]; then
echo "Unbound-Status: OK"
else
echo "Unbound-Status: ${unbound_msgs[*]}"
fi
echo
# --- sudo-Credential-Cache aufbauen ---
if sudo -n true 2>/dev/null; then
echo "➤ sudo erfordert kein Passwort – überspringe Abfrage."
else
echo "➤ Dieses Skript benötigt teilweise sudo-Rechte. Bitte Passwort eingeben, falls abgefragt:"
sudo -K 2>/dev/null
sudo -v || { echo "${rot}sudo konnte nicht ausgeführt werden.${reset}"; exit 1; }
fi
# Verzeichnis dieses Skripts
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# REAL_USER ermitteln (robust, auch bei sudo)
REAL_USER=${SUDO_USER:-$(logname 2>/dev/null || whoami)}
# Log-Datei im echten Home-Verzeichnis
HOME_DIR="$(getent passwd "$REAL_USER" | cut -d: -f6)"
: "${HOME_DIR:="${HOME:-/home/$REAL_USER}"}"
logfile="$HOME_DIR/wartung.log"
# Logging
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$logfile"
}
# Log-Rotation ab ~100KB
rotate_log() {
if [ -f "$logfile" ]; then
filesize=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
if [ "$filesize" -ge 102400 ]; then
timestamp=$(date '+%Y%m%d-%H%M%S')
mv "$logfile" "$(dirname "$logfile")/wartung-$timestamp.log"
: > "$logfile"
log "Logfile >100KB, rotiert nach wartung-$timestamp.log"
fi
fi
}
rotate_log
press_enter() { echo; read -p "Drücke Enter zum Fortfahren..." _; }
have() { command -v "$1" >/dev/null 2>&1; }
while true; do
clear
echo "${gelb}====================================${reset}"
echo " EndeavourOS ARM Wartung – Pi 4"
echo " Version: ${gruen}${SCRIPT_VERSION}${reset}"
echo "${gelb}====================================${reset}"
echo "1) Systeminformationen"
echo "2) Speicher- & Datenträgerstatus & SSD Trim"
echo "3) Systemüberwachung in Echtzeit (htop)"
echo "4) Journal & Logdateien"
echo "5) Pacman & Paketverwaltung"
echo "6) ioBroker – Wartung & Kontrolle"
echo "7) Pi-hole mit Unbound – Wartung & Kontrolle"
echo "8) Syncthing – Status & Kontrolle"
echo "9) System-Neustart"
echo "0) Beenden"
echo "${gelb}====================================${reset}"
read -p "Bitte wähle eine Option: " auswahl
case "$auswahl" in
1)
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}1) Systeminformationen${reset}"
echo "${gelb}=========================================${reset}"
log "Systeminformationen anzeigen"
echo
echo ">> uname -a"
uname -a
echo
echo ">> hostnamectl"
hostnamectl
echo
echo ">> uptime -p"
uptime -p
echo
if have fastfetch; then
echo ">> fastfetch (Systemübersicht)"
fastfetch
else
echo "${gelb}Hinweis:${reset} fastfetch ist nicht installiert."
fi
press_enter
;;
2)
clear
echo "${gelb}=============================================${reset}"
echo "${gruen}2) Speicher- & Datenträgerstatus & SSD Trim${reset}"
echo "${gelb}=============================================${reset}"
log "Speicher- & Datenträgerstatus & SSD Trim"
echo
echo ">> df -h"
df -h
echo
echo ">> sudo fstrim -av"
sudo fstrim -av
press_enter
;;
3)
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}3) Systemüberwachung in Echtzeit – htop${reset}"
echo "${gelb}=========================================${reset}"
log "Systemüberwachung in Echtzeit – htop"
echo
if have htop; then
echo ">> Starte htop – beenden mit q"
sleep 2
htop
else
echo "${gelb}Hinweis:${reset} htop ist nicht installiert."
press_enter
fi
;;
4)
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}4) Journal & Logdateien${reset}"
echo "${gelb}=========================================${reset}"
log "Journal & Logdateien"
echo
read -p "Wie viele Zeilen sollen angezeigt werden? [Default: 20] " lines
lines=${lines:-20}
# nur Zahlen erlauben, sonst wieder 20
if ! [[ "$lines" =~ ^[0-9]+$ ]]; then lines=20; fi
echo
echo ">> journalctl -p 3 -xb | tail -${lines} # Fehler des letzten Systemstarts"
journalctl -p 3 -xb | tail -${lines}
echo
echo ">> journalctl -u iobroker -n ${lines} # Letzte ioBroker-Logzeilen"
journalctl -u iobroker -n ${lines}
echo
echo ">> dmesg | tail -${lines} # Letzte Kernelmeldungen"
if dmesg >/dev/null 2>&1; then
dmesg | tail -${lines}
else
sudo dmesg | tail -${lines}
fi
echo
if [ -f /var/log/pacman.log ]; then
echo ">> tail -${lines} /var/log/pacman.log # Letzte Paketinstallationen & Updates"
tail -${lines} /var/log/pacman.log
else
echo "${gelb}Hinweis:${reset} /var/log/pacman.log nicht gefunden."
fi
press_enter
;;
5)
while true; do
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}5) Pacman & Paketverwaltung${reset}"
echo "${gelb}=========================================${reset}"
log "Pacman & Paketverwaltung"
echo
echo "a) sudo pacman -Syu # System aktualisieren"
echo "b) sudo pacman -Syyuu # Komplett-Refresh inkl. Downgrades"
echo "c) sudo pacman -Qdtq # Verwaiste Pakete auflisten"
echo "d) sudo pacman -Rsn \$(pacman -Qdtq) # Verwaiste Pakete entfernen"
echo "e) sudo pacman -Scc # Cache leeren"
echo "f) yay -Syu # AUR aktualisieren"
echo "g) yay -Sc # AUR Cache leeren"
echo "h) yay -v -Scc # AUR Cache vollständig leeren"
echo "z) Zurück zum Hauptmenü"
echo
read -p "Unterauswahl: " sub
case "$sub" in
a) log "pacman -Syu"; sudo pacman -Syu; press_enter;;
b) log "pacman -Syyuu"; sudo pacman -Syyuu; press_enter;;
c) log "pacman -Qdtq"; sudo pacman -Qdtq || true; press_enter;;
d)
log "verwaiste Pakete entfernen"
orphans=$(pacman -Qdtq 2>/dev/null || true)
if [ -n "${orphans:-}" ]; then
echo "Folgende Pakete werden entfernt:"
echo "$orphans"
read -p "Bestätigen? [j/N] " ans
if [[ "$ans" =~ ^[Jj]$ ]]; then
sudo pacman -Rsn $orphans
else
echo "Abgebrochen."
fi
else
echo "Keine verwaisten Pakete gefunden."
fi
press_enter;;
e) log "pacman -Scc"; sudo pacman -Scc; press_enter;;
f) if have yay; then log "yay -Syu"; yay -Syu; else echo "yay nicht gefunden."; fi; press_enter;;
g) if have yay; then log "yay -Sc"; yay -Sc; else echo "yay nicht gefunden."; fi; press_enter;;
h) if have yay; then log "yay -v -Scc"; yay -v -Scc; else echo "yay nicht gefunden."; fi; press_enter;;
z|Z) break;;
*) echo "${rot}Ungültige Auswahl.${reset}"; press_enter;;
esac
done
;;
6)
while true; do
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}6) ioBroker – Wartung & Kontrolle${reset}"
echo "${gelb}=========================================${reset}"
log "ioBroker – Wartung & Kontrolle"
echo
if ! have iob; then
echo "${rot}iob (ioBroker CLI) nicht gefunden.${reset}"
press_enter
break
fi
echo "a) systemctl status iobroker.service # Dienststatus anzeigen"
echo "b) iob diag # Diagnosebericht erstellen"
echo "c) iob stop && iob fix # Rechte & Dienste reparieren"
echo "d) iob stop && iob update && iob upgrade self && iob upgrade # ioBroker aktualisieren"
echo "e) iob start # ioBroker starten"
echo "z) Zurück zum Hauptmenü"
echo
read -p 'Unterauswahl: ' sub
case "$sub" in
a)
log "systemctl status iobroker.service"
echo ">> systemctl status iobroker.service --no-pager"
systemctl status iobroker.service --no-pager
press_enter;;
b)
log "iob diag"
echo ">> iob diag"
iob diag
press_enter;;
c)
log "iob stop && iob fix"
iob stop && iob fix
press_enter;;
d)
log "ioBroker Update"
iob stop && iob update && iob upgrade self && iob upgrade
press_enter;;
e)
log "iob start"
iob start
press_enter;;
z|Z)
break;;
*)
echo "${rot}Ungültige Auswahl.${reset}"
press_enter;;
esac
done
;;
7)
while true; do
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}7) Pi-hole mit Unbound – Wartung & Kontrolle${reset}"
echo "${gelb}=========================================${reset}"
log "Pi-hole mit Unbound – Wartung & Kontrolle"
echo
echo "a) systemctl status unbound # Status des DNS-Resolvers anzeigen"
echo "b) sudo pihole -g # Blockierlisten aktualisieren"
echo "c) sudo pihole -f # Statistikdaten löschen"
echo "d) sudo systemctl restart pihole-FTL # DNS-Dienst neu starten"
echo "e) cat /etc/unbound/trusted-key.key # DNSSEC Trusted Keys anzeigen"
echo "f) cat /etc/unbound/root.hints # Root Hints (13 Root-Server) anzeigen"
echo "z) Zurück zum Hauptmenü"
echo
read -p "Unterauswahl: " sub
case "$sub" in
a)
log "systemctl status unbound"
echo ">> systemctl status unbound --no-pager"
systemctl status unbound --no-pager
press_enter;;
b)
if have pihole; then
log "pihole -g"
sudo pihole -g
else
echo "pihole nicht gefunden."
fi
press_enter;;
c)
if have pihole; then
log "pihole -f"
sudo pihole -f
else
echo "pihole nicht gefunden."
fi
press_enter;;
d)
log "systemctl restart pihole-FTL"
sudo systemctl restart pihole-FTL && echo "pihole-FTL neu gestartet."
press_enter;;
e)
log "trusted-key.key anzeigen"
sudo cat /etc/unbound/trusted-key.key || echo "Datei nicht gefunden."
press_enter;;
f)
log "root.hints anzeigen"
sudo cat /etc/unbound/root.hints || echo "Datei nicht gefunden."
press_enter;;
z|Z)
break;;
*)
echo "${rot}Ungültige Auswahl.${reset}"
press_enter;;
esac
done
;;
8)
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}8) Syncthing – Status & Kontrolle${reset}"
echo "${gelb}=========================================${reset}"
log "Syncthing – Status & Kontrolle"
echo
echo ">> systemctl status syncthing@alarm.service --no-pager"
systemctl status syncthing@alarm.service --no-pager
echo
echo ">> Letzte Logmeldungen (journalctl)"
journalctl -u syncthing@alarm.service -n 10 --no-pager
echo
echo "GUI (LAN): http://$(ip -4 route get 1.1.1.1 | awk '{print $7; exit}'):8384"
press_enter
;;
9)
clear
echo "${gelb}=========================================${reset}"
echo "${gruen}9) System-Neustart${reset}"
echo "${gelb}=========================================${reset}"
log "System-Neustart"
echo
read -p "Jetzt neu starten? [j/N] " ans
if [[ "$ans" =~ ^[Jj]$ ]]; then
log "sudo reboot"
sudo reboot
else
echo "Abgebrochen."
press_enter
fi
;;
0)
log "Menü beendet."
echo "${gruen}Beende das Menü. Auf Wiedersehen!${reset}"
exit 0
;;
*)
echo "${rot}Ungültige Auswahl! Bitte wähle eine gültige Option.${reset}"
press_enter
;;
esac
done