🎉 Improved backend functionality with new watchdog services and manager script 📚

This commit is contained in:
Till Tomczak 2025-06-01 13:11:59 +02:00
parent 98f92c97f0
commit 57d19c7df3
4 changed files with 1052 additions and 75 deletions

View File

@ -758,6 +758,84 @@ install_system_dependencies() {
log "✅ System-Abhängigkeiten vollständig installiert"
}
# =========================== WATCHDOG-SERVICES ===========================
create_watchdog_service() {
log "=== WATCHDOG-SERVICE KONFIGURATION ==="
progress "Erstelle intelligenten Watchdog-Service..."
# Prüfe Python-Dependencies für erweiterten Watchdog
local python_watchdog_available=false
if python3 -c "import psutil, requests" 2>/dev/null; then
python_watchdog_available=true
log "✅ Python-Dependencies für erweiterten Watchdog verfügbar"
else
warning "⚠️ Python-Dependencies fehlen - verwende Bash-Watchdog"
fi
# Kopiere Watchdog-Service-Dateien
if [ -f "$CURRENT_DIR/kiosk-watchdog.service" ]; then
cp "$CURRENT_DIR/kiosk-watchdog.service" "/etc/systemd/system/kiosk-watchdog.service"
log "✅ Bash-Watchdog-Service installiert"
fi
if [ "$python_watchdog_available" = true ] && [ -f "$CURRENT_DIR/kiosk-watchdog-python.service" ]; then
cp "$CURRENT_DIR/kiosk-watchdog-python.service" "/etc/systemd/system/kiosk-watchdog-python.service"
log "✅ Python-Watchdog-Service installiert"
# Installiere Python-Dependencies falls nicht vorhanden
local pip_opts="--break-system-packages --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org"
pip3 install $pip_opts psutil requests 2>/dev/null || warning "Python-Watchdog-Dependencies Installation fehlgeschlagen"
fi
# Aktiviere bevorzugten Watchdog-Service
if [ "$python_watchdog_available" = true ]; then
progress "Aktiviere Python-Watchdog-Service..."
systemctl enable kiosk-watchdog-python.service || warning "Python-Watchdog Enable fehlgeschlagen"
systemctl disable kiosk-watchdog.service 2>/dev/null || true
log "✅ Python-Watchdog als primärer Service konfiguriert"
else
progress "Aktiviere Bash-Watchdog-Service..."
systemctl enable kiosk-watchdog.service || warning "Bash-Watchdog Enable fehlgeschlagen"
systemctl disable kiosk-watchdog-python.service 2>/dev/null || true
log "✅ Bash-Watchdog als primärer Service konfiguriert"
fi
log "✅ Watchdog-Service erfolgreich konfiguriert"
}
start_watchdog_service() {
log "=== WATCHDOG-SERVICE START ==="
# Prüfe welcher Watchdog-Service aktiviert ist
if systemctl is-enabled --quiet kiosk-watchdog-python.service 2>/dev/null; then
progress "Starte Python-Watchdog-Service..."
systemctl start kiosk-watchdog-python.service || warning "Python-Watchdog Start fehlgeschlagen"
sleep 5
if systemctl is-active --quiet kiosk-watchdog-python.service; then
log "✅ Python-Watchdog-Service läuft erfolgreich"
else
warning "⚠️ Python-Watchdog-Service läuft nicht - prüfen Sie: journalctl -u kiosk-watchdog-python -f"
fi
elif systemctl is-enabled --quiet kiosk-watchdog.service 2>/dev/null; then
progress "Starte Bash-Watchdog-Service..."
systemctl start kiosk-watchdog.service || warning "Bash-Watchdog Start fehlgeschlagen"
sleep 5
if systemctl is-active --quiet kiosk-watchdog.service; then
log "✅ Bash-Watchdog-Service läuft erfolgreich"
else
warning "⚠️ Bash-Watchdog-Service läuft nicht - prüfen Sie: journalctl -u kiosk-watchdog -f"
fi
else
warning "⚠️ Kein Watchdog-Service aktiviert"
fi
log "✅ Watchdog-Service-Start abgeschlossen"
}
# =========================== AKTUALISIERTE HAUPTFUNKTIONEN ===========================
setup_production_kiosk() {
log "=== PRODUKTIONS-KIOSK SETUP ==="
@ -771,8 +849,10 @@ setup_production_kiosk() {
install_npm_dependencies
create_https_service
create_kiosk_service
create_watchdog_service
configure_kiosk_environment
enable_and_start_services
start_watchdog_service
run_system_tests
log "✅ PRODUKTIONS-KIOSK ERFOLGREICH EINGERICHTET"
@ -791,6 +871,17 @@ show_main_menu() {
echo -e "${YELLOW}HTTPS-URL:${NC} $HTTPS_URL"
echo -e "${YELLOW}Kiosk-Benutzer:${NC} $KIOSK_USER"
echo -e "${YELLOW}System:${NC} $(uname -m) - $(cat /etc/debian_version 2>/dev/null || echo 'Unbekannt')"
echo ""
# Watchdog-Status anzeigen
if systemctl is-active --quiet kiosk-watchdog-python.service 2>/dev/null; then
echo -e "${GREEN}🔍 Watchdog:${NC} Python-Watchdog aktiv"
elif systemctl is-active --quiet kiosk-watchdog.service 2>/dev/null; then
echo -e "${GREEN}🔍 Watchdog:${NC} Bash-Watchdog aktiv"
else
echo -e "${RED}🔍 Watchdog:${NC} Nicht aktiv"
fi
echo ""
echo -e "${PURPLE}Installationsoptionen:${NC}"
echo ""
@ -804,6 +895,7 @@ show_main_menu() {
echo -e " → Installiert minimale X11-Umgebung"
echo -e " → Erstellt SSL-Zertifikate automatisch"
echo -e " → Konfiguriert Autologin und Chromium-Kiosk"
echo -e "${CYAN}Intelligenter Watchdog-Service${NC}"
echo -e "${YELLOW}NEUSTART ERFORDERLICH!${NC}"
echo ""
echo -e "${GREEN}3)${NC} Nur SSL-Zertifikate generieren"
@ -811,17 +903,20 @@ show_main_menu() {
echo -e " → Fügt Zertifikate zum System CA-Store hinzu"
echo ""
echo -e "${GREEN}4)${NC} Services verwalten"
echo -e " → Start/Stop/Restart HTTPS und Kiosk Services"
echo -e " → Start/Stop/Restart HTTPS, Kiosk und Watchdog Services"
echo -e " → Service-Status anzeigen"
echo -e "${CYAN}Erweiterte Watchdog-Verwaltung${NC}"
echo ""
echo -e "${GREEN}5)${NC} System-Tests ausführen"
echo -e " → Prüft Installation und Konfiguration"
echo -e " → Testet HTTPS-Erreichbarkeit"
echo -e " → Watchdog-Funktionalität testen"
echo ""
echo -e "${RED}0)${NC} Beenden"
echo ""
echo -e "${RED}⚠️ WARNUNG: Option 2 macht das System zu einem reinen Kiosk!${NC}"
echo -e "${GREEN}🔐 HTTPS: Automatische SSL-Zertifikat-Generierung${NC}"
echo -e "${CYAN}🔍 WATCHDOG: Intelligente Python/Bash-basierte Überwachung${NC}"
echo -e "${BLUE}=================================================================${NC}"
echo -n "Ihre Wahl [0-5]: "
}
@ -836,11 +931,12 @@ manage_services_menu() {
echo -e "${GREEN}3)${NC} HTTPS-Service neustarten"
echo -e "${GREEN}4)${NC} Kiosk-Service starten"
echo -e "${GREEN}5)${NC} Kiosk-Service stoppen"
echo -e "${GREEN}6)${NC} Service-Status anzeigen"
echo -e "${GREEN}7)${NC} Service-Logs anzeigen"
echo -e "${GREEN}6)${NC} Watchdog-Service verwalten"
echo -e "${GREEN}7)${NC} Service-Status anzeigen"
echo -e "${GREEN}8)${NC} Service-Logs anzeigen"
echo -e "${RED}0)${NC} Zurück zum Hauptmenü"
echo ""
echo -n "Ihre Wahl [0-7]: "
echo -n "Ihre Wahl [0-8]: "
read -r choice
@ -866,18 +962,163 @@ manage_services_menu() {
echo -e "${YELLOW}Kiosk-Service gestoppt${NC}"
;;
6)
manage_watchdog_services
;;
7)
echo -e "${BLUE}=== SERVICE-STATUS ===${NC}"
systemctl status "${HTTPS_SERVICE_NAME}.service" --no-pager || true
echo ""
systemctl status "${KIOSK_SERVICE_NAME}.service" --no-pager || true
echo ""
# Watchdog-Status
if systemctl is-enabled --quiet kiosk-watchdog-python.service 2>/dev/null; then
systemctl status kiosk-watchdog-python.service --no-pager || true
elif systemctl is-enabled --quiet kiosk-watchdog.service 2>/dev/null; then
systemctl status kiosk-watchdog.service --no-pager || true
fi
;;
7)
8)
echo -e "${BLUE}=== SERVICE-LOGS (letzte 20 Zeilen) ===${NC}"
echo -e "${CYAN}HTTPS-Service:${NC}"
journalctl -u "${HTTPS_SERVICE_NAME}.service" -n 20 --no-pager || true
echo ""
echo -e "${CYAN}Kiosk-Service:${NC}"
journalctl -u "${KIOSK_SERVICE_NAME}.service" -n 20 --no-pager || true
echo ""
echo -e "${CYAN}Watchdog-Service:${NC}"
if systemctl is-active --quiet kiosk-watchdog-python.service 2>/dev/null; then
journalctl -u kiosk-watchdog-python.service -n 20 --no-pager || true
elif systemctl is-active --quiet kiosk-watchdog.service 2>/dev/null; then
journalctl -u kiosk-watchdog.service -n 20 --no-pager || true
fi
;;
0)
break
;;
*)
echo -e "${RED}Ungültige Eingabe${NC}"
;;
esac
echo ""
echo -e "${YELLOW}Drücken Sie Enter, um fortzufahren...${NC}"
read -r
done
}
manage_watchdog_services() {
while true; do
clear
echo -e "${BLUE}=== WATCHDOG-SERVICE MANAGEMENT ===${NC}"
echo ""
# Status anzeigen
if systemctl is-active --quiet kiosk-watchdog-python.service 2>/dev/null; then
echo -e "${GREEN}✅ Python-Watchdog aktiv${NC}"
elif systemctl is-enabled --quiet kiosk-watchdog-python.service 2>/dev/null; then
echo -e "${YELLOW}⚠️ Python-Watchdog aktiviert aber nicht laufend${NC}"
else
echo -e "${RED}❌ Python-Watchdog nicht aktiviert${NC}"
fi
if systemctl is-active --quiet kiosk-watchdog.service 2>/dev/null; then
echo -e "${GREEN}✅ Bash-Watchdog aktiv${NC}"
elif systemctl is-enabled --quiet kiosk-watchdog.service 2>/dev/null; then
echo -e "${YELLOW}⚠️ Bash-Watchdog aktiviert aber nicht laufend${NC}"
else
echo -e "${RED}❌ Bash-Watchdog nicht aktiviert${NC}"
fi
echo ""
echo -e "${GREEN}1)${NC} Python-Watchdog aktivieren und starten"
echo -e "${GREEN}2)${NC} Bash-Watchdog aktivieren und starten"
echo -e "${GREEN}3)${NC} Aktiven Watchdog stoppen"
echo -e "${GREEN}4)${NC} Aktiven Watchdog neustarten"
echo -e "${GREEN}5)${NC} Watchdog-Logs anzeigen"
echo -e "${GREEN}6)${NC} Watchdog-Konfiguration testen"
echo -e "${RED}0)${NC} Zurück"
echo ""
echo -n "Ihre Wahl [0-6]: "
read -r choice
case $choice in
1)
# Python-Watchdog aktivieren
systemctl stop kiosk-watchdog.service 2>/dev/null || true
systemctl disable kiosk-watchdog.service 2>/dev/null || true
systemctl enable kiosk-watchdog-python.service
systemctl start kiosk-watchdog-python.service
echo -e "${GREEN}Python-Watchdog aktiviert und gestartet${NC}"
;;
2)
# Bash-Watchdog aktivieren
systemctl stop kiosk-watchdog-python.service 2>/dev/null || true
systemctl disable kiosk-watchdog-python.service 2>/dev/null || true
systemctl enable kiosk-watchdog.service
systemctl start kiosk-watchdog.service
echo -e "${GREEN}Bash-Watchdog aktiviert und gestartet${NC}"
;;
3)
# Aktiven Watchdog stoppen
systemctl stop kiosk-watchdog-python.service 2>/dev/null || true
systemctl stop kiosk-watchdog.service 2>/dev/null || true
echo -e "${YELLOW}Watchdog-Services gestoppt${NC}"
;;
4)
# Aktiven Watchdog neustarten
if systemctl is-enabled --quiet kiosk-watchdog-python.service 2>/dev/null; then
systemctl restart kiosk-watchdog-python.service
echo -e "${GREEN}Python-Watchdog neugestartet${NC}"
elif systemctl is-enabled --quiet kiosk-watchdog.service 2>/dev/null; then
systemctl restart kiosk-watchdog.service
echo -e "${GREEN}Bash-Watchdog neugestartet${NC}"
else
echo -e "${RED}Kein Watchdog-Service aktiviert${NC}"
fi
;;
5)
# Watchdog-Logs anzeigen
echo -e "${BLUE}=== WATCHDOG-LOGS ===${NC}"
if systemctl is-active --quiet kiosk-watchdog-python.service 2>/dev/null; then
echo -e "${CYAN}Python-Watchdog (systemd):${NC}"
journalctl -u kiosk-watchdog-python.service -n 30 --no-pager || true
echo ""
echo -e "${CYAN}Python-Watchdog (Datei):${NC}"
tail -n 20 /var/log/kiosk-watchdog-python.log 2>/dev/null || echo "Log-Datei nicht gefunden"
elif systemctl is-active --quiet kiosk-watchdog.service 2>/dev/null; then
echo -e "${CYAN}Bash-Watchdog:${NC}"
journalctl -u kiosk-watchdog.service -n 30 --no-pager || true
echo ""
tail -n 20 /var/log/kiosk-watchdog.log 2>/dev/null || echo "Log-Datei nicht gefunden"
else
echo -e "${RED}Kein aktiver Watchdog-Service${NC}"
fi
;;
6)
# Watchdog-Konfiguration testen
echo -e "${BLUE}=== WATCHDOG-KONFIGURATION TEST ===${NC}"
# Teste Python-Watchdog
if python3 -c "import sys; sys.path.insert(0, '$APP_DIR'); from utils.watchdog_manager import WatchdogManager; print('✅ Python-Watchdog importierbar')" 2>/dev/null; then
echo -e "${GREEN}✅ Python-Watchdog-Konfiguration OK${NC}"
else
echo -e "${RED}❌ Python-Watchdog-Konfiguration fehlerhaft${NC}"
fi
# Teste SSL-Zertifikate
if [ -f "$APP_DIR/certs/localhost/localhost.crt" ]; then
echo -e "${GREEN}✅ SSL-Zertifikat vorhanden${NC}"
else
echo -e "${RED}❌ SSL-Zertifikat fehlt${NC}"
fi
# Teste HTTPS-Erreichbarkeit
if curl -k -s --connect-timeout 5 "$HTTPS_URL" >/dev/null 2>&1; then
echo -e "${GREEN}✅ HTTPS Backend erreichbar${NC}"
else
echo -e "${RED}❌ HTTPS Backend nicht erreichbar${NC}"
fi
;;
0)
break

View File

@ -0,0 +1,53 @@
[Unit]
Description=MYP Kiosk Watchdog Service - Python-basierte intelligente Überwachung
Documentation=https://github.com/MYP-Druckerverwaltung
After=multi-user.target myp-https.service
Wants=myp-https.service
[Service]
Type=simple
User=root
Restart=always
RestartSec=30
TimeoutStartSec=60
TimeoutStopSec=30
# Python-basierter Watchdog mit erweiterten Features
WorkingDirectory=/opt/myp
ExecStartPre=/usr/bin/python3 -c "import psutil, requests; print('Python-Dependencies verfügbar')"
ExecStart=/usr/bin/python3 /opt/myp/utils/watchdog_manager.py --app-dir /opt/myp --daemon
# Umgebungsvariablen für Python-Watchdog
Environment=PYTHONPATH=/opt/myp
Environment=PYTHONUNBUFFERED=1
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=DISPLAY=:0
Environment=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
Environment=LC_ALL=C.UTF-8
Environment=LANG=C.UTF-8
# Logging-Konfiguration
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myp-watchdog-python
# Sicherheitseinstellungen
NoNewPrivileges=false
PrivateTmp=false
ProtectSystem=strict
ReadWritePaths=/var/log
ReadWritePaths=/opt/myp
ReadWritePaths=/home/kiosk
ReadWritePaths=/proc/sys/vm
ReadWritePaths=/tmp
# Resource-Limits für Python-Prozess
MemoryMax=512M
CPUQuota=50%
# Restart-Verhalten bei Fehlern
StartLimitBurst=5
StartLimitInterval=300
[Install]
WantedBy=multi-user.target

View File

@ -1,5 +1,5 @@
[Unit]
Description=MYP Kiosk Watchdog Service - Überwacht HTTPS Backend und Kiosk-Browser
Description=MYP Kiosk Watchdog Service - Intelligente Überwachung für HTTPS Backend und Kiosk-Browser
Documentation=https://github.com/MYP-Druckerverwaltung
After=multi-user.target myp-https.service
Wants=myp-https.service
@ -9,104 +9,197 @@ Type=simple
User=root
Restart=always
RestartSec=30
TimeoutStartSec=60
TimeoutStopSec=30
# Intelligentes Watchdog-Skript mit modularer Struktur
ExecStart=/bin/bash -c '\
while true; do \
# Prüfe HTTPS Backend-Service (Port 443) \
if ! systemctl is-active --quiet myp-https; then \
echo "$(date): HTTPS Backend-Service nicht aktiv - starte neu" >> /var/log/kiosk-watchdog.log; \
systemctl start myp-https; \
sleep 10; \
fi; \
\
# Prüfe HTTPS Backend-Erreichbarkeit (Port 443) \
if ! curl -k -s --connect-timeout 5 https://localhost:443 >/dev/null 2>&1; then \
echo "$(date): HTTPS Backend nicht erreichbar - starte Service neu" >> /var/log/kiosk-watchdog.log; \
systemctl restart myp-https; \
sleep 15; \
fi; \
\
# Prüfe SSL-Zertifikat-Gültigkeit \
if [ -f /opt/myp/certs/localhost/localhost.crt ]; then \
if ! openssl x509 -in /opt/myp/certs/localhost/localhost.crt -noout -checkend 86400 >/dev/null 2>&1; then \
echo "$(date): SSL-Zertifikat läuft ab - regeneriere Zertifikat" >> /var/log/kiosk-watchdog.log; \
python3 -c "import sys; sys.path.insert(0, \"/opt/myp\"); from utils.ssl_config import ensure_ssl_certificates; ensure_ssl_certificates(\"/opt/myp\", True)" || true; \
systemctl restart myp-https; \
sleep 10; \
fi; \
# === KONFIGURATION === \
readonly HTTPS_SERVICE="myp-https" \
readonly KIOSK_SERVICE="myp-kiosk" \
readonly KIOSK_USER="kiosk" \
readonly HTTPS_URL="https://localhost:443" \
readonly APP_DIR="/opt/myp" \
readonly LOG_FILE="/var/log/kiosk-watchdog.log" \
readonly CHECK_INTERVAL=30 \
readonly HTTPS_TIMEOUT=10 \
readonly RESTART_DELAY=15 \
readonly MAX_MEMORY_PERCENT=85 \
readonly CERT_EXPIRE_DAYS=7 \
\
# === LOGGING-FUNKTIONEN === \
log_info() { echo "$(date "+%Y-%m-%d %H:%M:%S") [INFO] $1" >> "$LOG_FILE"; } \
log_warn() { echo "$(date "+%Y-%m-%d %H:%M:%S") [WARN] $1" >> "$LOG_FILE"; } \
log_error() { echo "$(date "+%Y-%m-%d %H:%M:%S") [ERROR] $1" >> "$LOG_FILE"; } \
\
# === HILFSFUNKTIONEN === \
is_service_active() { systemctl is-active --quiet "$1"; } \
is_service_enabled() { systemctl is-enabled --quiet "$1"; } \
restart_service() { \
log_info "Starte Service neu: $1"; \
systemctl restart "$1" && sleep "$RESTART_DELAY" || log_error "Service-Neustart fehlgeschlagen: $1"; \
} \
\
check_https_connectivity() { \
curl -k -s --connect-timeout "$HTTPS_TIMEOUT" --max-time "$HTTPS_TIMEOUT" "$HTTPS_URL" >/dev/null 2>&1; \
} \
\
check_ssl_certificate() { \
local cert_file="$APP_DIR/certs/localhost/localhost.crt" \
[ -f "$cert_file" ] && openssl x509 -in "$cert_file" -noout -checkend $((86400 * CERT_EXPIRE_DAYS)) >/dev/null 2>&1; \
} \
\
regenerate_ssl_certificate() { \
log_warn "Regeneriere SSL-Zertifikat..."; \
if python3 -c "import sys; sys.path.insert(0, \"$APP_DIR\"); from utils.ssl_config import ensure_ssl_certificates; ensure_ssl_certificates(\"$APP_DIR\", True)" 2>/dev/null; then \
log_info "SSL-Zertifikat erfolgreich regeneriert"; \
restart_service "$HTTPS_SERVICE"; \
else \
echo "$(date): SSL-Zertifikat fehlt - generiere neues Zertifikat" >> /var/log/kiosk-watchdog.log; \
python3 -c "import sys; sys.path.insert(0, \"/opt/myp\"); from utils.ssl_config import ensure_ssl_certificates; ensure_ssl_certificates(\"/opt/myp\")" || true; \
systemctl restart myp-https; \
sleep 10; \
log_error "SSL-Zertifikat-Regenerierung fehlgeschlagen"; \
fi \
} \
\
check_kiosk_user_session() { \
pgrep -u "$KIOSK_USER" >/dev/null 2>&1; \
} \
\
check_chromium_process() { \
pgrep -u "$KIOSK_USER" -f "chromium.*kiosk" >/dev/null 2>&1; \
} \
\
check_x_server() { \
pgrep -f "X.*:0" >/dev/null 2>&1; \
} \
\
check_display_availability() { \
[ -n "$(DISPLAY=:0 xdpyinfo 2>/dev/null)" ]; \
} \
\
get_memory_usage() { \
free | awk "/^Mem:/ {printf \"%.1f\", \$3/\$2 * 100.0}"; \
} \
\
cleanup_system_resources() { \
log_info "Bereinige Systemressourcen (Speichernutzung: $(get_memory_usage)%)"; \
\
# Browser-Cache bereinigen \
rm -rf "/home/$KIOSK_USER/.chromium-kiosk/Default/Cache"/* 2>/dev/null || true; \
rm -rf "/home/$KIOSK_USER/.cache"/* 2>/dev/null || true; \
\
# Temporäre Dateien bereinigen \
find "/tmp" -type f -atime +1 -delete 2>/dev/null || true; \
find "$APP_DIR/uploads/temp" -type f -mtime +1 -delete 2>/dev/null || true; \
\
# System-Cache leeren \
sync; \
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true; \
\
log_info "Systemressourcen bereinigt - neue Speichernutzung: $(get_memory_usage)%"; \
} \
\
restart_kiosk_session() { \
log_warn "Starte Kiosk-Session neu..."; \
\
# Beende alle Kiosk-Prozesse sanft \
pkill -u "$KIOSK_USER" -TERM 2>/dev/null || true; \
sleep 5; \
\
# Erzwinge Beendigung falls nötig \
pkill -u "$KIOSK_USER" -KILL 2>/dev/null || true; \
sleep 2; \
\
# Starte Getty-Service neu für Autologin \
systemctl restart getty@tty1.service; \
sleep "$RESTART_DELAY"; \
\
log_info "Kiosk-Session neugestartet"; \
} \
\
# === HAUPTÜBERWACHUNGSSCHLEIFE === \
log_info "Kiosk-Watchdog gestartet (PID: $$)"; \
\
while true; do \
# === HTTPS BACKEND ÜBERWACHUNG === \
if ! is_service_active "$HTTPS_SERVICE"; then \
log_error "HTTPS-Service nicht aktiv"; \
restart_service "$HTTPS_SERVICE"; \
elif ! check_https_connectivity; then \
log_error "HTTPS Backend nicht erreichbar"; \
restart_service "$HTTPS_SERVICE"; \
fi; \
\
# Prüfe Kiosk-Benutzer Session \
if ! pgrep -u kiosk > /dev/null; then \
echo "$(date): Kiosk-Benutzer nicht angemeldet - prüfe Autologin" >> /var/log/kiosk-watchdog.log; \
# Versuche getty@tty1 Service zu restarten für Autologin \
systemctl restart getty@tty1.service; \
sleep 15; \
# === SSL-ZERTIFIKAT ÜBERWACHUNG === \
if ! check_ssl_certificate; then \
if [ -f "$APP_DIR/certs/localhost/localhost.crt" ]; then \
log_warn "SSL-Zertifikat läuft in $CERT_EXPIRE_DAYS Tagen ab"; \
else \
log_error "SSL-Zertifikat fehlt"; \
fi; \
regenerate_ssl_certificate; \
fi; \
\
# Prüfe Chromium Kiosk-Prozess \
if pgrep -u kiosk > /dev/null && ! pgrep -u kiosk -f "chromium.*kiosk" > /dev/null; then \
echo "$(date): Chromium-Kiosk nicht gefunden aber Kiosk-User aktiv - starte Browser" >> /var/log/kiosk-watchdog.log; \
# Versuche Kiosk-Service zu starten \
systemctl --user start myp-kiosk 2>/dev/null || true; \
sleep 10; \
# === KIOSK-SESSION ÜBERWACHUNG === \
if ! check_kiosk_user_session; then \
log_error "Kiosk-Benutzer-Session nicht aktiv"; \
restart_kiosk_session; \
elif ! check_x_server; then \
log_error "X-Server nicht verfügbar"; \
restart_kiosk_session; \
elif ! check_display_availability; then \
log_error "Display :0 nicht verfügbar"; \
restart_kiosk_session; \
elif ! check_chromium_process; then \
log_warn "Chromium-Kiosk-Prozess nicht gefunden"; \
if is_service_enabled "$KIOSK_SERVICE"; then \
systemctl --user start "$KIOSK_SERVICE" 2>/dev/null || true; \
else \
sudo -u "$KIOSK_USER" DISPLAY=:0 chromium --kiosk --no-sandbox --ignore-certificate-errors "$HTTPS_URL" >/dev/null 2>&1 & \
fi; \
sleep "$RESTART_DELAY"; \
fi; \
\
# Prüfe X-Server für Kiosk-Display \
if pgrep -u kiosk > /dev/null && ! pgrep -f "X.*:0" > /dev/null; then \
echo "$(date): X-Server nicht gefunden aber Kiosk-User aktiv - starte X" >> /var/log/kiosk-watchdog.log; \
# Versuche X-Server über Kiosk-User zu starten \
sudo -u kiosk DISPLAY=:0 startx 2>/dev/null & \
sleep 15; \
# === SYSTEMRESSOURCEN ÜBERWACHUNG === \
memory_usage=$(get_memory_usage); \
if (( $(echo "$memory_usage > $MAX_MEMORY_PERCENT" | bc -l 2>/dev/null || echo 0) )); then \
log_warn "Hohe Speichernutzung: ${memory_usage}%"; \
cleanup_system_resources; \
fi; \
\
# Prüfe Display-Verfügbarkeit \
if pgrep -u kiosk > /dev/null && [ -z "$(DISPLAY=:0 xdpyinfo 2>/dev/null)" ]; then \
echo "$(date): Display :0 nicht verfügbar - starte X-Session neu" >> /var/log/kiosk-watchdog.log; \
# Beende alle X-Prozesse des Kiosk-Users und starte neu \
pkill -u kiosk -f "X" 2>/dev/null || true; \
sleep 5; \
sudo -u kiosk DISPLAY=:0 startx 2>/dev/null & \
sleep 15; \
# === LOG-ROTATION === \
if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) -gt 10485760 ]; then \
tail -n 1000 "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"; \
log_info "Log-Datei rotiert (>10MB)"; \
fi; \
\
# Prüfe Systemressourcen und bereinige bei Bedarf \
MEMORY_USAGE=$(free | grep Mem | awk "{print (\$3/\$2) * 100.0}"); \
if (( $(echo "$MEMORY_USAGE > 90" | bc -l) )); then \
echo "$(date): Hohe Speichernutzung ($MEMORY_USAGE%) - bereinige System" >> /var/log/kiosk-watchdog.log; \
# Bereinige Browser-Cache \
rm -rf /home/kiosk/.chromium-kiosk/Default/Cache/* 2>/dev/null || true; \
rm -rf /home/kiosk/.cache/* 2>/dev/null || true; \
# Garbage Collection \
sync; \
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true; \
fi; \
\
# Warte 30 Sekunden vor nächster Prüfung \
sleep 30; \
# Warte bis zur nächsten Prüfung \
sleep "$CHECK_INTERVAL"; \
done'
# Umgebungsvariablen für HTTPS-Überwachung
# Umgebungsvariablen für optimierte Überwachung
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=DISPLAY=:0
Environment=PYTHONPATH=/opt/myp
Environment=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
Environment=LC_ALL=C.UTF-8
Environment=LANG=C.UTF-8
# Logging
# Logging-Konfiguration
StandardOutput=append:/var/log/kiosk-watchdog.log
StandardError=append:/var/log/kiosk-watchdog.log
# Sicherheitseinstellungen
NoNewPrivileges=false
PrivateTmp=false
ProtectSystem=strict
ReadWritePaths=/var/log
ReadWritePaths=/opt/myp
ReadWritePaths=/home/kiosk
ReadWritePaths=/proc/sys/vm
ReadWritePaths=/tmp
# Resource-Limits
MemoryMax=256M
CPUQuota=25%
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,590 @@
#!/usr/bin/env python3
"""
Intelligenter Watchdog-Manager für MYP Druckerverwaltung
Erweiterte Überwachung mit Python für bessere Fehlerbehandlung und Logging
Optimiert für Debian/Linux-Systeme im Kiosk-Modus
"""
import os
import sys
import time
import json
import logging
import subprocess
import threading
import signal
from pathlib import Path
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Callable
import psutil
import requests
from urllib3.exceptions import InsecureRequestWarning
# SSL-Warnungen unterdrücken für localhost
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class WatchdogConfig:
"""Konfiguration für den Watchdog-Manager"""
def __init__(self, app_dir: str = "/opt/myp"):
self.app_dir = Path(app_dir)
self.config_file = self.app_dir / "config" / "watchdog.json"
# Standard-Konfiguration
self.defaults = {
"https_service": "myp-https",
"kiosk_service": "myp-kiosk",
"kiosk_user": "kiosk",
"https_url": "https://localhost:443",
"check_interval": 30,
"https_timeout": 10,
"restart_delay": 15,
"max_memory_percent": 85,
"cert_expire_days": 7,
"log_rotation_size_mb": 10,
"max_restart_attempts": 3,
"restart_cooldown": 300,
"enable_auto_cleanup": True,
"enable_performance_monitoring": True
}
self.config = self.load_config()
def load_config(self) -> Dict:
"""Lädt Konfiguration aus Datei oder verwendet Defaults"""
try:
if self.config_file.exists():
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# Merge mit Defaults
merged = self.defaults.copy()
merged.update(config)
return merged
else:
self.save_config(self.defaults)
return self.defaults.copy()
except Exception as e:
logging.error(f"Fehler beim Laden der Konfiguration: {e}")
return self.defaults.copy()
def save_config(self, config: Dict) -> None:
"""Speichert Konfiguration in Datei"""
try:
self.config_file.parent.mkdir(parents=True, exist_ok=True)
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
except Exception as e:
logging.error(f"Fehler beim Speichern der Konfiguration: {e}")
def get(self, key: str, default=None):
"""Holt Konfigurationswert"""
return self.config.get(key, default)
def set(self, key: str, value) -> None:
"""Setzt Konfigurationswert"""
self.config[key] = value
self.save_config(self.config)
class ServiceMonitor:
"""Überwacht systemd-Services"""
def __init__(self, config: WatchdogConfig):
self.config = config
self.restart_counts = {}
self.last_restart_times = {}
def is_service_active(self, service_name: str) -> bool:
"""Prüft ob Service aktiv ist"""
try:
result = subprocess.run(
["systemctl", "is-active", "--quiet", service_name],
capture_output=True
)
return result.returncode == 0
except Exception:
return False
def is_service_enabled(self, service_name: str) -> bool:
"""Prüft ob Service aktiviert ist"""
try:
result = subprocess.run(
["systemctl", "is-enabled", "--quiet", service_name],
capture_output=True
)
return result.returncode == 0
except Exception:
return False
def restart_service(self, service_name: str) -> bool:
"""Startet Service neu mit Cooldown-Logik"""
now = datetime.now()
# Prüfe Restart-Cooldown
if service_name in self.last_restart_times:
time_since_last = (now - self.last_restart_times[service_name]).total_seconds()
if time_since_last < self.config.get("restart_cooldown", 300):
logging.warning(f"Service {service_name} im Cooldown ({time_since_last:.0f}s)")
return False
# Prüfe maximale Restart-Versuche
restart_count = self.restart_counts.get(service_name, 0)
max_attempts = self.config.get("max_restart_attempts", 3)
if restart_count >= max_attempts:
logging.error(f"Service {service_name} erreichte maximale Restart-Versuche ({max_attempts})")
return False
try:
logging.info(f"Starte Service neu: {service_name} (Versuch {restart_count + 1}/{max_attempts})")
result = subprocess.run(
["systemctl", "restart", service_name],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
self.restart_counts[service_name] = restart_count + 1
self.last_restart_times[service_name] = now
time.sleep(self.config.get("restart_delay", 15))
logging.info(f"Service {service_name} erfolgreich neugestartet")
return True
else:
logging.error(f"Service-Neustart fehlgeschlagen: {result.stderr}")
return False
except subprocess.TimeoutExpired:
logging.error(f"Service-Neustart Timeout: {service_name}")
return False
except Exception as e:
logging.error(f"Service-Neustart Fehler: {e}")
return False
def reset_restart_counter(self, service_name: str) -> None:
"""Setzt Restart-Zähler zurück"""
if service_name in self.restart_counts:
del self.restart_counts[service_name]
if service_name in self.last_restart_times:
del self.last_restart_times[service_name]
class HTTPSMonitor:
"""Überwacht HTTPS-Backend"""
def __init__(self, config: WatchdogConfig):
self.config = config
self.session = requests.Session()
self.session.verify = False # Selbstsignierte Zertifikate
def check_connectivity(self) -> bool:
"""Prüft HTTPS-Erreichbarkeit"""
try:
url = self.config.get("https_url", "https://localhost:443")
timeout = self.config.get("https_timeout", 10)
response = self.session.get(
url,
timeout=timeout,
allow_redirects=True
)
return response.status_code < 500
except Exception as e:
logging.debug(f"HTTPS-Konnektivitätsprüfung fehlgeschlagen: {e}")
return False
def check_ssl_certificate(self) -> bool:
"""Prüft SSL-Zertifikat-Gültigkeit"""
try:
cert_file = self.config.app_dir / "certs" / "localhost" / "localhost.crt"
if not cert_file.exists():
return False
expire_days = self.config.get("cert_expire_days", 7)
expire_seconds = expire_days * 86400
result = subprocess.run([
"openssl", "x509",
"-in", str(cert_file),
"-noout", "-checkend", str(expire_seconds)
], capture_output=True)
return result.returncode == 0
except Exception as e:
logging.error(f"SSL-Zertifikat-Prüfung fehlgeschlagen: {e}")
return False
def regenerate_ssl_certificate(self) -> bool:
"""Regeneriert SSL-Zertifikat"""
try:
logging.info("Regeneriere SSL-Zertifikat...")
# Importiere SSL-Konfiguration
sys.path.insert(0, str(self.config.app_dir))
from utils.ssl_config import ensure_ssl_certificates
success = ensure_ssl_certificates(str(self.config.app_dir), force_regenerate=True)
if success:
logging.info("SSL-Zertifikat erfolgreich regeneriert")
else:
logging.error("SSL-Zertifikat-Regenerierung fehlgeschlagen")
return success
except Exception as e:
logging.error(f"SSL-Zertifikat-Regenerierung Fehler: {e}")
return False
class KioskMonitor:
"""Überwacht Kiosk-Session und Browser"""
def __init__(self, config: WatchdogConfig):
self.config = config
self.kiosk_user = config.get("kiosk_user", "kiosk")
def check_user_session(self) -> bool:
"""Prüft ob Kiosk-User-Session aktiv ist"""
try:
for proc in psutil.process_iter(['username']):
if proc.info['username'] == self.kiosk_user:
return True
return False
except Exception:
return False
def check_chromium_process(self) -> bool:
"""Prüft ob Chromium-Kiosk-Prozess läuft"""
try:
for proc in psutil.process_iter(['username', 'cmdline']):
if (proc.info['username'] == self.kiosk_user and
proc.info['cmdline'] and
any('chromium' in arg and 'kiosk' in arg for arg in proc.info['cmdline'])):
return True
return False
except Exception:
return False
def check_x_server(self) -> bool:
"""Prüft ob X-Server läuft"""
try:
for proc in psutil.process_iter(['cmdline']):
if (proc.info['cmdline'] and
any('X' in arg and ':0' in arg for arg in proc.info['cmdline'])):
return True
return False
except Exception:
return False
def check_display_availability(self) -> bool:
"""Prüft ob Display verfügbar ist"""
try:
result = subprocess.run(
["xdpyinfo"],
env={"DISPLAY": ":0"},
capture_output=True,
timeout=5
)
return result.returncode == 0
except Exception:
return False
def restart_kiosk_session(self) -> bool:
"""Startet Kiosk-Session neu"""
try:
logging.info("Starte Kiosk-Session neu...")
# Beende Kiosk-Prozesse sanft
subprocess.run(["pkill", "-u", self.kiosk_user, "-TERM"], timeout=10)
time.sleep(5)
# Erzwinge Beendigung falls nötig
subprocess.run(["pkill", "-u", self.kiosk_user, "-KILL"], timeout=5)
time.sleep(2)
# Starte Getty-Service neu für Autologin
subprocess.run(["systemctl", "restart", "getty@tty1.service"], timeout=15)
time.sleep(self.config.get("restart_delay", 15))
logging.info("Kiosk-Session neugestartet")
return True
except Exception as e:
logging.error(f"Kiosk-Session-Neustart fehlgeschlagen: {e}")
return False
class SystemMonitor:
"""Überwacht Systemressourcen"""
def __init__(self, config: WatchdogConfig):
self.config = config
def get_memory_usage(self) -> float:
"""Gibt Speichernutzung in Prozent zurück"""
try:
return psutil.virtual_memory().percent
except Exception:
return 0.0
def get_cpu_usage(self) -> float:
"""Gibt CPU-Nutzung in Prozent zurück"""
try:
return psutil.cpu_percent(interval=1)
except Exception:
return 0.0
def get_disk_usage(self) -> float:
"""Gibt Festplatten-Nutzung in Prozent zurück"""
try:
return psutil.disk_usage('/').percent
except Exception:
return 0.0
def cleanup_system_resources(self) -> None:
"""Bereinigt Systemressourcen"""
try:
memory_before = self.get_memory_usage()
logging.info(f"Bereinige Systemressourcen (Speicher: {memory_before:.1f}%)")
kiosk_user = self.config.get("kiosk_user", "kiosk")
app_dir = self.config.app_dir
# Browser-Cache bereinigen
cache_dirs = [
f"/home/{kiosk_user}/.chromium-kiosk/Default/Cache",
f"/home/{kiosk_user}/.cache"
]
for cache_dir in cache_dirs:
if os.path.exists(cache_dir):
subprocess.run(["rm", "-rf", f"{cache_dir}/*"], shell=True)
# Temporäre Dateien bereinigen
temp_dirs = [
"/tmp",
str(app_dir / "uploads" / "temp")
]
for temp_dir in temp_dirs:
if os.path.exists(temp_dir):
subprocess.run([
"find", temp_dir, "-type", "f", "-atime", "+1", "-delete"
], timeout=30)
# System-Cache leeren
subprocess.run(["sync"])
with open("/proc/sys/vm/drop_caches", "w") as f:
f.write("3")
memory_after = self.get_memory_usage()
logging.info(f"Systemressourcen bereinigt (Speicher: {memory_after:.1f}%)")
except Exception as e:
logging.error(f"Systemressourcen-Bereinigung fehlgeschlagen: {e}")
class WatchdogManager:
"""Hauptklasse für Watchdog-Management"""
def __init__(self, app_dir: str = "/opt/myp"):
self.config = WatchdogConfig(app_dir)
self.service_monitor = ServiceMonitor(self.config)
self.https_monitor = HTTPSMonitor(self.config)
self.kiosk_monitor = KioskMonitor(self.config)
self.system_monitor = SystemMonitor(self.config)
self.running = False
self.setup_logging()
self.setup_signal_handlers()
def setup_logging(self) -> None:
"""Konfiguriert Logging"""
log_file = Path("/var/log/kiosk-watchdog-python.log")
log_file.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
def setup_signal_handlers(self) -> None:
"""Konfiguriert Signal-Handler für sauberes Beenden"""
def signal_handler(signum, frame):
logging.info(f"Signal {signum} empfangen - beende Watchdog...")
self.running = False
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
def rotate_log_if_needed(self) -> None:
"""Rotiert Log-Datei bei Bedarf"""
try:
log_file = Path("/var/log/kiosk-watchdog-python.log")
max_size = self.config.get("log_rotation_size_mb", 10) * 1024 * 1024
if log_file.exists() and log_file.stat().st_size > max_size:
# Behalte nur die letzten 1000 Zeilen
subprocess.run([
"tail", "-n", "1000", str(log_file)
], stdout=open(f"{log_file}.tmp", "w"))
log_file.unlink()
Path(f"{log_file}.tmp").rename(log_file)
logging.info("Log-Datei rotiert (>10MB)")
except Exception as e:
logging.error(f"Log-Rotation fehlgeschlagen: {e}")
def check_https_backend(self) -> None:
"""Prüft HTTPS-Backend"""
service_name = self.config.get("https_service", "myp-https")
if not self.service_monitor.is_service_active(service_name):
logging.error("HTTPS-Service nicht aktiv")
self.service_monitor.restart_service(service_name)
elif not self.https_monitor.check_connectivity():
logging.error("HTTPS Backend nicht erreichbar")
self.service_monitor.restart_service(service_name)
else:
# Service läuft - Reset Restart-Counter
self.service_monitor.reset_restart_counter(service_name)
def check_ssl_certificate(self) -> None:
"""Prüft SSL-Zertifikat"""
if not self.https_monitor.check_ssl_certificate():
cert_file = self.config.app_dir / "certs" / "localhost" / "localhost.crt"
if cert_file.exists():
expire_days = self.config.get("cert_expire_days", 7)
logging.warning(f"SSL-Zertifikat läuft in {expire_days} Tagen ab")
else:
logging.error("SSL-Zertifikat fehlt")
if self.https_monitor.regenerate_ssl_certificate():
service_name = self.config.get("https_service", "myp-https")
self.service_monitor.restart_service(service_name)
def check_kiosk_session(self) -> None:
"""Prüft Kiosk-Session"""
if not self.kiosk_monitor.check_user_session():
logging.error("Kiosk-Benutzer-Session nicht aktiv")
self.kiosk_monitor.restart_kiosk_session()
elif not self.kiosk_monitor.check_x_server():
logging.error("X-Server nicht verfügbar")
self.kiosk_monitor.restart_kiosk_session()
elif not self.kiosk_monitor.check_display_availability():
logging.error("Display :0 nicht verfügbar")
self.kiosk_monitor.restart_kiosk_session()
elif not self.kiosk_monitor.check_chromium_process():
logging.warning("Chromium-Kiosk-Prozess nicht gefunden")
# Versuche Kiosk-Service zu starten
kiosk_service = self.config.get("kiosk_service", "myp-kiosk")
if self.service_monitor.is_service_enabled(kiosk_service):
subprocess.run(["systemctl", "--user", "start", kiosk_service])
else:
# Fallback: Browser direkt starten
https_url = self.config.get("https_url", "https://localhost:443")
kiosk_user = self.config.get("kiosk_user", "kiosk")
subprocess.Popen([
"sudo", "-u", kiosk_user,
"DISPLAY=:0", "chromium",
"--kiosk", "--no-sandbox", "--ignore-certificate-errors",
https_url
], env={"DISPLAY": ":0"})
time.sleep(self.config.get("restart_delay", 15))
def check_system_resources(self) -> None:
"""Prüft Systemressourcen"""
if not self.config.get("enable_performance_monitoring", True):
return
memory_usage = self.system_monitor.get_memory_usage()
max_memory = self.config.get("max_memory_percent", 85)
if memory_usage > max_memory:
logging.warning(f"Hohe Speichernutzung: {memory_usage:.1f}%")
if self.config.get("enable_auto_cleanup", True):
self.system_monitor.cleanup_system_resources()
def run_monitoring_cycle(self) -> None:
"""Führt einen Überwachungszyklus durch"""
try:
# HTTPS Backend prüfen
self.check_https_backend()
# SSL-Zertifikat prüfen
self.check_ssl_certificate()
# Kiosk-Session prüfen
self.check_kiosk_session()
# Systemressourcen prüfen
self.check_system_resources()
# Log-Rotation
self.rotate_log_if_needed()
except Exception as e:
logging.error(f"Fehler im Überwachungszyklus: {e}")
def run(self) -> None:
"""Startet Hauptüberwachungsschleife"""
self.running = True
check_interval = self.config.get("check_interval", 30)
logging.info(f"Kiosk-Watchdog gestartet (PID: {os.getpid()})")
logging.info(f"Überwachungsintervall: {check_interval}s")
while self.running:
try:
self.run_monitoring_cycle()
time.sleep(check_interval)
except KeyboardInterrupt:
logging.info("Watchdog durch Benutzer beendet")
break
except Exception as e:
logging.error(f"Unerwarteter Fehler: {e}")
time.sleep(check_interval)
logging.info("Kiosk-Watchdog beendet")
def main():
"""Hauptfunktion"""
import argparse
parser = argparse.ArgumentParser(description="MYP Kiosk Watchdog Manager")
parser.add_argument("--app-dir", default="/opt/myp", help="Anwendungsverzeichnis")
parser.add_argument("--config", help="Konfigurationsdatei")
parser.add_argument("--daemon", action="store_true", help="Als Daemon ausführen")
args = parser.parse_args()
try:
watchdog = WatchdogManager(args.app_dir)
if args.daemon:
# Daemon-Modus (für systemd)
watchdog.run()
else:
# Interaktiver Modus
print("Starte Watchdog... (Strg+C zum Beenden)")
watchdog.run()
except Exception as e:
logging.error(f"Watchdog-Start fehlgeschlagen: {e}")
sys.exit(1)
if __name__ == "__main__":
main()