#!/bin/bash # Watchdog photobooth — tourne toutes les 30s via systemd timer # Vérifie backend, camera, chromium. Escalade jusqu'au reboot si nécessaire. HEALTH_URL="http://localhost/api/health" STATE_FILE="/tmp/photobooth-watchdog-failures" MAX_FAILURES=10 # 10 échecs × 30s = 5 min avant reboot LOG_TAG="photobooth-watchdog" log() { logger -t "$LOG_TAG" "$1"; } # Compteur d'échecs persistant failures=$(cat "$STATE_FILE" 2>/dev/null || echo 0) # --- 1. Tuer les backends orphelins (ppid=1, pas rattachés au kiosk) --- while IFS= read -r line; do pid=$(echo "$line" | awk '{print $1}') ppid=$(echo "$line" | awk '{print $2}') if [ "$ppid" = "1" ]; then log "Backend orphelin détecté (pid=$pid), kill" kill -9 "$pid" 2>/dev/null fi done < <(ps -eo pid,ppid,args | grep 'python.*backend.main' | grep -v grep) # --- 2. Vérifier que le backend répond --- health=$(curl -sf --max-time 5 "$HEALTH_URL" 2>/dev/null) if [ -z "$health" ]; then log "Backend ne répond pas (échec $((failures+1))/$MAX_FAILURES)" # Vérifier si un process backend existe if ! pgrep -f 'python.*backend.main' > /dev/null 2>&1; then log "Aucun backend en cours — le kiosk devrait le relancer" fi failures=$((failures + 1)) echo "$failures" > "$STATE_FILE" if [ "$failures" -ge "$MAX_FAILURES" ]; then log "REBOOT: $MAX_FAILURES échecs consécutifs, redémarrage système" echo 0 > "$STATE_FILE" /usr/sbin/reboot fi exit 0 fi # Backend répond — extraire l'état caméra camera=$(echo "$health" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("camera",""))' 2>/dev/null) clients=$(echo "$health" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("clients",0))' 2>/dev/null) # --- 3. Vérifier la caméra --- if [ "$camera" = "disconnected" ] || [ "$camera" = "erreur" ]; then log "Caméra déconnectée, tentative USB reset Canon" # USB ioctl reset du Canon (vendor 04a9) python3 -c ' import fcntl, glob, os for f in glob.glob("/sys/bus/usb/devices/*/idVendor"): if open(f).read().strip() == "04a9": d = os.path.dirname(f) bus = open(d + "/busnum").read().strip().zfill(3) dev = open(d + "/devnum").read().strip().zfill(3) path = f"/dev/bus/usb/{bus}/{dev}" with open(path, "wb") as fh: fcntl.ioctl(fh, 0x5514, 0) print(f"USB reset: {path}") ' 2>/dev/null # Si le Canon n'est même pas sur le bus USB if ! lsusb 2>/dev/null | grep -q "04a9"; then log "Canon absent du bus USB" failures=$((failures + 1)) else failures=$((failures + 1)) fi echo "$failures" > "$STATE_FILE" if [ "$failures" -ge "$MAX_FAILURES" ]; then log "REBOOT: caméra injoignable depuis $MAX_FAILURES cycles" echo 0 > "$STATE_FILE" /usr/sbin/reboot fi exit 0 fi # --- 4. Vérifier Chromium --- if ! pgrep -f chromium > /dev/null 2>&1; then log "Chromium ne tourne pas — restart lightdm" systemctl restart lightdm failures=$((failures + 1)) echo "$failures" > "$STATE_FILE" exit 0 fi # --- 5. Tuer gvfsd-gphoto2 s'il revient --- pkill -f gvfsd-gphoto2 2>/dev/null pkill -f gvfs-gphoto2-volume-monitor 2>/dev/null # --- Tout va bien : reset compteur --- if [ "$failures" -gt 0 ]; then log "Système OK (camera=$camera, clients=$clients) — compteur remis à 0" fi echo 0 > "$STATE_FILE"