🎉 Improved backend functionality with new watchdog services and manager script 📚
This commit is contained in:
parent
98f92c97f0
commit
57d19c7df3
@ -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
|
||||
|
53
backend/kiosk-watchdog-python.service
Normal file
53
backend/kiosk-watchdog-python.service
Normal 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
|
@ -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
|
590
backend/utils/watchdog_manager.py
Normal file
590
backend/utils/watchdog_manager.py
Normal 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()
|
Loading…
x
Reference in New Issue
Block a user