From 5310c866bdcec9da78efb3b56650994f0136a024 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 19:27:19 +0000 Subject: [PATCH 1/3] refactor(maintenance): adopt audited v6 script, self-contained UI Replace the outdated maintenance script with the newer, security-audited variant (printf-based, unified log output) while keeping it a single, standalone file suitable for the wget/cron install model. - Inline the UI helpers (ui_init, log_ok/warn/err) instead of sourcing an external scripts/lib/ui.sh; restores TTY colors and avoids a hard dependency that would abort under 'set -euo pipefail'. - Switch per-step output to unified, timestamped log_* lines; end-of-run dashboard/summary format is unchanged. - Harden signal handling: on_exit is re-entry guarded and runs exactly once via EXIT; SIGINT/SIGTERM route through it so summary + tmp cleanup still happen on Ctrl-C. - Preserve the documented cron PATH string (no ${PATH} append). - All flags, JSON output, steps, backup rotation and Pi-hole v6 commands (pihole -v/-up/-g, pihole-FTL service, FTL query status codes) unchanged. - README/README.de: note the per-step output and signal-handling changes. Verified: git diff --check, bash -n, and CI-path dry-run all pass. shellcheck/shfmt are not installed locally and were not run; CI (make check) covers them. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MLBSE1qE3QvZYF8i9GjCz3 --- README.de.md | 6 ++ README.md | 5 ++ pihole_maintenance_pro.sh | 152 ++++++++++++++++++++++++-------------- 3 files changed, 109 insertions(+), 54 deletions(-) diff --git a/README.de.md b/README.de.md index b221d99..a12f673 100644 --- a/README.de.md +++ b/README.de.md @@ -107,6 +107,12 @@ Aufgenommen auf einem Raspberry Pi mit Pi-hole Core 6.1.4, Web 6.2.1, FTL 6.2.3 #13 πŸ‘₯ FTL Client 25 active clients βœ” OK ``` +> **Hinweis zur Ausgabe (v5.3.2-Refresh):** Die Live-Zeilen pro Schritt nutzen jetzt ein +> vereinheitlichtes, zeitgestempeltes Format (`[HH:MM:SS] OK|WARN|ERR …`) statt +> `βœ” Erfolg`/`⚠ Warnung`. Das oben gezeigte Dashboard und die Zusammenfassung bleiben +> unverΓ€ndert. `SIGINT`/`SIGTERM` (Strg-C) erzeugen die Zusammenfassung und rΓ€umen das +> Temp-Verzeichnis nun genau einmal auf. Alle Flags, JSON-Ausgabe und Steps sind identisch. + Der reale Lauf bestΓ€tigt außerdem: - Backups werden vor Pi-hole-Wartung erstellt (z.β€―B. `/etc/pihole/backup_20251025_100315`, `/etc/pihole/backup_20251025_100337`) diff --git a/README.md b/README.md index 61e0bcc..49305e8 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,11 @@ Captured on a Raspberry Pi with Pi-hole Core 6.1.4, Web 6.2.1, FTL 6.2.3 β€” thi #13 πŸ‘₯ FTL Client 25 active clients βœ” OK ``` +> **Output note (v5.3.2 refresh):** per-step live lines now use a unified, timestamped +> format (`[HH:MM:SS] OK|WARN|ERR …`) instead of `βœ” Erfolg`/`⚠ Warnung`. The end-of-run +> dashboard and summary shown above are unchanged. `SIGINT`/`SIGTERM` (Ctrl-C) now render the +> summary and clean up the temp dir exactly once. All flags, JSON output and steps are identical. + The same production run confirms: - Backups are created before Pi-hole maintenance kicks in (e.g. `/etc/pihole/backup_20251025_100315`, `/etc/pihole/backup_20251025_100337`) diff --git a/pihole_maintenance_pro.sh b/pihole_maintenance_pro.sh index 5034933..a3c3faa 100755 --- a/pihole_maintenance_pro.sh +++ b/pihole_maintenance_pro.sh @@ -20,26 +20,58 @@ IFS=$'\n\t' # Voller PATH fΓΌr cron/Nicht-Login-Shells (frΓΌh setzen) export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +# --------------------------- UI helpers (self-contained) -------------------- +# Inline-Portierung der zentralen UI-Helfer (im Schwester-Repo als +# scripts/lib/ui.sh ausgelagert). Bewusst self-contained: dieses Skript wird +# als eigenstΓ€ndiger Einzel-Download verteilt und via cron ausgefΓΌhrt – es darf +# keine externe Bibliothek voraussetzen. +ui_init() { + UI_COLOR=true + if [[ ! -t 1 ]]; then UI_COLOR=false; fi + if [[ -n "${NO_COLOR:-}" ]]; then UI_COLOR=false; fi + + if [[ "$UI_COLOR" == true ]]; then + UI_RED=$'\033[0;31m' + UI_GREEN=$'\033[0;32m' + UI_YELLOW=$'\033[1;33m' + UI_BLUE=$'\033[0;34m' + UI_MAGENTA=$'\033[0;35m' + UI_CYAN=$'\033[0;36m' + UI_BOLD=$'\033[1m' + UI_RESET=$'\033[0m' + else + UI_RED='' UI_GREEN='' UI_YELLOW='' UI_BLUE='' UI_MAGENTA='' UI_CYAN='' UI_BOLD='' UI_RESET='' + fi +} + +_ui_ts() { date +"%H:%M:%S"; } + +_ui_format_line() { + local label="$1" color="$2" msg="$3" + if [[ "${UI_COLOR:-false}" == true && -n "$color" ]]; then + printf '[%s] %s%s%s %s' "$(_ui_ts)" "$color" "$label" "$UI_RESET" "$msg" + else + printf '[%s] %s %s' "$(_ui_ts)" "$label" "$msg" + fi +} + +# Vereinheitlichte log_*-Ausgabe (aus ui.sh ΓΌbernommen). Kein zusΓ€tzliches +# Datei-Logging nΓΆtig: exec-tee unten schreibt bereits alles ins $LOGFILE. +log_ok() { printf '%s\n' "$(_ui_format_line "OK" "$UI_GREEN" "$*")"; } +log_warn() { printf '%s\n' "$(_ui_format_line "WARN" "$UI_YELLOW" "$*")" >&2; } +log_err() { printf '%s\n' "$(_ui_format_line "ERR" "$UI_RED" "$*")" >&2; } + +ui_init + # --------------------------- Colors & symbols ------------------------------- -if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - BLUE='\033[0;34m' - MAGENTA='\033[0;35m' - CYAN='\033[0;36m' - BOLD='\033[1m' - NC='\033[0m' -else - RED="" - GREEN="" - YELLOW="" - BLUE="" - MAGENTA="" - CYAN="" - BOLD="" - NC="" -fi +RED="${UI_RED:-}" +GREEN="${UI_GREEN:-}" +YELLOW="${UI_YELLOW:-}" +BLUE="${UI_BLUE:-}" +MAGENTA="${UI_MAGENTA:-}" +CYAN="${UI_CYAN:-}" +BOLD="${UI_BOLD:-}" +NC="${UI_RESET:-}" CHECK="${GREEN}βœ”${NC}" WARN="${YELLOW}⚠${NC}" FAIL="${RED}βœ–${NC}" @@ -48,9 +80,9 @@ FAIL="${RED}βœ–${NC}" # FΓΌr sicheren lokalen Selftest (RUN_SELFTEST=1) ohne Root erlauben if [[ ${EUID} -ne 0 ]]; then if [[ "${RUN_SELFTEST:-0}" == "1" ]]; then - echo -e "${YELLOW}Hinweis:${NC} RUN_SELFTEST=1 erkannt – Root-Check ΓΌbersprungen (APT/Upgrade/Gravity sollten via --no-* Flags deaktiviert sein)." + printf '%sHinweis:%s RUN_SELFTEST=1 erkannt – Root-Check ΓΌbersprungen (APT/Upgrade/Gravity sollten via --no-* Flags deaktiviert sein).\n' "$YELLOW" "$NC" else - echo -e "${RED}[ERROR]${NC} Bitte mit sudo oder als root ausfΓΌhren." >&2 + printf '%s[ERROR]%s Bitte mit sudo oder als root ausfΓΌhren.\n' "$RED" "$NC" >&2 exit 1 fi fi @@ -130,7 +162,7 @@ if [[ -z "$PIHOLE_BIN" ]]; then echo "Warnung: pihole CLI nicht im CI vorhanden. Test wird ΓΌbersprungen." exit 0 else - echo -e "${RED}[ERROR]${NC} 'pihole' CLI nicht gefunden. PATH=$PATH" >&2 + printf '%s[ERROR]%s '\''pihole'\'' CLI nicht gefunden. PATH=%s\n' "$RED" "$NC" "$PATH" >&2 echo "Auf Pi-hole-Host ausfΓΌhren oder CLI installieren." >&2 exit 127 fi @@ -147,10 +179,11 @@ if [[ -d "$LOGDIR" && -w "$LOGDIR" ]]; then LOGFILE="$LOGDIR/pihole_maintenance_pro_$(date +%Y-%m-%d_%H-%M-%S).log" else LOGFILE="$TMPDIR/pihole_maintenance_pro_$(date +%Y-%m-%d_%H-%M-%S).log" - echo -e "${YELLOW}Hinweis:${NC} /var/log nicht beschreibbar, Log nach $TMPDIR." + printf '%sHinweis:%s /var/log nicht beschreibbar, Log nach %s.\n' "$YELLOW" "$NC" "$TMPDIR" fi -trap 'rm -rf "$TMPDIR" 2>/dev/null || true' EXIT +cleanup_tmpdir() { rm -rf "$TMPDIR" 2>/dev/null || true; } +trap cleanup_tmpdir EXIT INT TERM exec > >(tee -a "$LOGFILE") 2>&1 # shellcheck disable=SC2034 @@ -161,16 +194,17 @@ strip_ansi() { sed -r $'s/\x1B\[[0-9;]*[a-zA-Z]//g' | tr -d '\r'; } echo_hdr() { if [[ -t 1 ]]; then clear; fi - echo -e "${MAGENTA}╔════════════════════════════════════════════════════════════════════════╗${NC}" - echo -e "${MAGENTA}β•‘${NC} πŸ›°οΈ ${BOLD}PI-HOLE MAINTENANCE PRO MAX${NC}${MAGENTA} - TimInTech (${CYAN}v5.3.2${MAGENTA}) β•‘${NC}" - echo -e "${MAGENTA}╠════════════════════════════════════════════════════════════════════════╣${NC}" + printf '%s╔════════════════════════════════════════════════════════════════════════╗%s\n' "$MAGENTA" "$NC" + printf '%sβ•‘%s πŸ›°οΈ %sPI-HOLE MAINTENANCE PRO MAX%s%s - TimInTech (%sv5.3.2%s) β•‘%s\n' \ + "$MAGENTA" "$NC" "$BOLD" "$NC" "$MAGENTA" "$CYAN" "$MAGENTA" "$NC" + printf '%s╠════════════════════════════════════════════════════════════════════════╣%s\n' "$MAGENTA" "$NC" if "$PIHOLE_BIN" -v > /dev/null 2>&1; then PH_VER="$("$PIHOLE_BIN" -v 2> /dev/null || true)" - echo -e "${MAGENTA}β•‘${NC} Version: ${CYAN}${PH_VER:-unbekannt}${NC}" + printf '%sβ•‘%s Version: %s%s%s\n' "$MAGENTA" "$NC" "$CYAN" "${PH_VER:-unbekannt}" "$NC" else - echo -e "${MAGENTA}β•‘${NC} ${YELLOW}Pi-hole CLI nicht gefunden${NC}" + printf '%sβ•‘%s %sPi-hole CLI nicht gefunden%s\n' "$MAGENTA" "$NC" "$YELLOW" "$NC" fi - echo -e "${MAGENTA}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${NC}" + printf '%sβ•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•%s\n' "$MAGENTA" "$NC" } run_step() { @@ -178,7 +212,7 @@ run_step() { local step_log="$TMPDIR/step_${n}.log" # shellcheck disable=SC2034 # consumed later when printing per-step log paths STEP_LOGFILE["$n"]="$step_log" - echo -e "\n${BLUE}╔═[Step ${n}]${NC}\n${BLUE}β•‘ ${icon} ${title}${NC}\n${BLUE}β•šβ•>${NC} " + printf '\n%s╔═[Step %s]%s\n%sβ•‘ %s %s%s\n%sβ•šβ•>%s ' "$BLUE" "$n" "$NC" "$BLUE" "$icon" "$title" "$NC" "$BLUE" "$NC" local out="/dev/null" if [[ -t 1 ]]; then @@ -187,14 +221,14 @@ run_step() { if [[ "$display_only" == "true" ]]; then if bash -lc "$cmd" 2>&1 | tee -a "$out" | strip_ansi > "$step_log"; then - echo -e "${CHECK} Erfolg" + log_ok "Step $n: $title" STATUS["$n"]="${GREEN}βœ” OK${NC}" [[ -f "$step_log" ]] && extract_step_data "$n" "$(cat "$step_log")" else - echo -e "${WARN} Warnung" + log_warn "Step $n: $title" STATUS["$n"]="${YELLOW}⚠ WARN${NC}" [[ -s "$step_log" ]] && tail -n 20 "$step_log" - [[ "$critical" == "true" ]] && echo -e "${RED}[ERROR] Kritischer Fehler – Abbruch${NC}" && exit 1 + [[ "$critical" == "true" ]] && log_err "Kritischer Fehler in Step $n – Abbruch" && exit 1 fi return 0 fi @@ -214,15 +248,17 @@ run_step() { printf '\r' > "$out" 2> /dev/null || true ) & if wait "$pid"; then - echo -e "\n${CHECK} Erfolg" + printf '\n' + log_ok "Step $n: $title" STATUS["$n"]="${GREEN}βœ” OK${NC}" [[ -f "$step_log" ]] && extract_step_data "$n" "$(cat "$step_log")" else local ec=$? - echo -e "\n${FAIL} Fehler (Code: $ec)" + printf '\n' + log_err "Step $n: $title (Code: $ec)" STATUS["$n"]="${RED}βœ– FAIL${NC}" [[ -f "$step_log" ]] && tail -n 50 "$step_log" - [[ "$critical" == "true" ]] && echo -e "${RED}[ERROR] Kritischer Fehler in Step ${n}${NC}" && exit $ec + [[ "$critical" == "true" ]] && log_err "Kritischer Fehler in Step $n: $title" && exit $ec fi } @@ -248,12 +284,12 @@ extract_step_data() { summary() { collect_system_info echo - echo -e "${CYAN}╔═══════════════ PERFORMANCE DASHBOARD ═══════════════╗${NC}" + printf '%s╔═══════════════ PERFORMANCE DASHBOARD ═══════════════╗%s\n' "$CYAN" "$NC" printf "${CYAN}β•‘${NC} πŸš€ Load: %-8s πŸ’Ύ RAM: %s%% 🌑️ Temp: %sΒ°C πŸ—„οΈ Disk: %s%% ${CYAN}β•‘${NC}\n" \ "${PERFORMANCE_DATA[load]:-N/A}" "${PERFORMANCE_DATA[memory]:-N/A}" "${PERFORMANCE_DATA[temp]:-N/A}" "${PERFORMANCE_DATA[disk]:-N/A}" - echo -e "${CYAN}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${NC}" + printf '%sβ•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•%s\n' "$CYAN" "$NC" echo - echo -e "${MAGENTA}════════ INTELLIGENTE ZUSAMMENFASSUNG ════════${NC}" + printf '%s════════ INTELLIGENTE ZUSAMMENFASSUNG ════════%s\n' "$MAGENTA" "$NC" for k in $(printf '%s\n' "${!STATUS[@]}" | sort -n); do local step_info="" case "$k" in @@ -277,8 +313,8 @@ summary() { done echo show_recommendations - echo -e "Log: ${CYAN}$LOGFILE${NC}" - echo -e "Step logs: ${CYAN}$TMPDIR${NC} (werden beim Exit gelΓΆscht)" + printf 'Log: %s%s%s\n' "$CYAN" "$LOGFILE" "$NC" + printf 'Step logs: %s%s%s (werden beim Exit gelΓΆscht)\n' "$CYAN" "$TMPDIR" "$NC" } get_step_description() { @@ -334,11 +370,11 @@ show_recommendations() { recommendations+=("🚨 Restart Pi-hole FTL: sudo systemctl restart pihole-FTL") } ((${#warnings[@]})) && { - echo -e "\n${YELLOW}════════ WARNINGS ════════${NC}" + printf '\n%s════════ WARNINGS ════════%s\n' "$YELLOW" "$NC" printf '%s\n' "${warnings[@]}" } ((${#recommendations[@]})) && { - echo -e "\n${BLUE}════════ RECOMMENDATIONS ════════${NC}" + printf '\n%s════════ RECOMMENDATIONS ════════%s\n' "$BLUE" "$NC" printf '%s\n' "${recommendations[@]}" } } @@ -428,20 +464,28 @@ for c in /etc/pihole/pihole-FTL.db /run/pihole-FTL.db /var/lib/pihole/pihole-FTL done # (gravity DB path is queried lazily when needed) +# Re-Entry-Guard: verhindert doppelte Summary/Cleanup, falls on_exit sowohl ΓΌber +# ein Signal als auch ΓΌber den finalen EXIT ausgelΓΆst wΓΌrde. +ON_EXIT_DONE=0 # shellcheck disable=SC2317 # trap callback is invoked by bash on_exit() { local rc="$1" + [[ "$ON_EXIT_DONE" == "1" ]] && return + ON_EXIT_DONE=1 echo "" if [[ "$JSON_OUTPUT" == "1" ]]; then output_json 2> /dev/null || true else summary 2> /dev/null || true fi - rm -rf "$TMPDIR" 2> /dev/null || true - [[ $rc -ne 0 ]] && echo -e "${RED}Script ended with exit code $rc${NC}" - exit "$rc" + cleanup_tmpdir + [[ $rc -ne 0 ]] && printf '%sScript ended with exit code %s%s\n' "$RED" "$rc" "$NC" } +# on_exit lΓ€uft genau einmal ΓΌber EXIT; INT/TERM leiten sauber dorthin um und +# sorgen so auch bei Ctrl-C fΓΌr Summary + tmp-Cleanup. trap 'on_exit $?' EXIT +trap 'exit 130' INT +trap 'exit 143' TERM # --------------------------- Run ------------------------------------------- echo_hdr @@ -460,13 +504,13 @@ if ((DO_APT == 1)); then export DEBIAN_FRONTEND=noninteractive run_step 01 "πŸ”„" "APT: update & upgrade" "apt update && apt -y upgrade" true run_step 02 "🧹" "APT: autoremove & autoclean" "apt -y autoremove && apt -y autoclean" - if dpkg --print-architecture | grep -q '^armhf$'; then + if dpkg --print-architecture | grep -q '^armhf$'; then if apt list --upgradable 2> /dev/null | grep -q '^linux-image-rpi-v8'; then - echo -e "${YELLOW}Hinweis:${NC} 'linux-image-rpi-v8' ist 64-bit (ARMv8). Auf Pi 3B (ARMv7) ignorierbar." + printf '%sHinweis:%s '\''linux-image-rpi-v8'\'' ist 64-bit (ARMv8). Auf Pi 3B (ARMv7) ignorierbar.\n' "$YELLOW" "$NC" fi fi else - echo -e "${YELLOW}APT-Schritte ΓΌbersprungen (--no-apt).${NC}" + printf '%sAPT-Schritte ΓΌbersprungen (--no-apt).%s\n' "$YELLOW" "$NC" fi # 02 – Security Checks (optional display) @@ -486,7 +530,7 @@ command -v clamscan > /dev/null 2>&1 && run_step 29 "🦠" "Security: clamav" "c PIHOLE_LOG="/var/log/pihole.log" if [[ -f "$PIHOLE_LOG" ]]; then LOGSIZE=$(stat -c %s "$PIHOLE_LOG" 2> /dev/null || echo 0) - ((LOGSIZE > 1073741824)) && echo -e "${YELLOW}WARNUNG: pihole.log > 1GB! Empfehlung: logrotate aktivieren.${NC}" + ((LOGSIZE > 1073741824)) && printf '%sWARNUNG: pihole.log > 1GB! Empfehlung: logrotate aktivieren.%s\n' "$YELLOW" "$NC" fi # Backup-Integration (optional) @@ -517,7 +561,7 @@ if ((DO_UPGRADE == 1)); then backup_pihole run_step 04 "πŸ†™" "Pi-hole self-update" "\"$PIHOLE_BIN\" -up" else - echo -e "${YELLOW}Pi-hole Upgrade ΓΌbersprungen (--no-upgrade).${NC}" + printf '%sPi-hole Upgrade ΓΌbersprungen (--no-upgrade).%s\n' "$YELLOW" "$NC" fi # 05 – Gravity @@ -525,7 +569,7 @@ if ((DO_GRAVITY == 1)); then backup_pihole run_step 05 "πŸ“‹" "Update Gravity / Blocklists" "\"$PIHOLE_BIN\" -g" else - echo -e "${YELLOW}Gravity-Update ΓΌbersprungen (--no-gravity).${NC}" + printf '%sGravity-Update ΓΌbersprungen (--no-gravity).%s\n' "$YELLOW" "$NC" fi # 06 – optionaler FTL-Restart (v6: nur bei Bedarf) @@ -557,7 +601,7 @@ if command -v sqlite3 > /dev/null 2>&1 && [[ -f "$FTL_DB" ]]; then run_step 12 "πŸ“ˆ" "Top 5 Domains (FTL)" $'sqlite3 -readonly "$FTL_DB" "SELECT domain, COUNT(1) c FROM queries GROUP BY domain ORDER BY c DESC LIMIT 5;" || true' false true run_step 13 "πŸ‘₯" "Top 5 Clients (FTL)" $'sqlite3 -readonly "$FTL_DB" "SELECT client, COUNT(1) c FROM queries GROUP BY client ORDER BY c DESC LIMIT 5;" || true' false true else - echo -e "${YELLOW}sqlite3 oder FTL DB nicht gefunden – Überspringe Top-Listen.${NC}" + printf '%ssqlite3 oder FTL DB nicht gefunden – Überspringe Top-Listen.%s\n' "$YELLOW" "$NC" fi # 14 – Abschluss (Summary/JSON kommt aus EXIT-Trap) From 008c4b3b3a6b8a0cdea3ceb7bc8ec0ad41c788eb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 19:32:12 +0000 Subject: [PATCH 2/3] style(maintenance): satisfy shfmt -sr/indent and drop unused vars CI (make check) flagged formatting inherited from the source variant: - space-after-redirect in cleanup_tmpdir ("2>/dev/null" -> "2> /dev/null") - fix 6-space indent on the armhf "if dpkg" guard to 2 spaces - remove now-unused CHECK/WARN/FAIL symbols (SC2034); run_step uses log_* No behavior change. bash -n and the CI-path dry-run pass. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MLBSE1qE3QvZYF8i9GjCz3 --- pihole_maintenance_pro.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pihole_maintenance_pro.sh b/pihole_maintenance_pro.sh index a3c3faa..8a608a4 100755 --- a/pihole_maintenance_pro.sh +++ b/pihole_maintenance_pro.sh @@ -72,9 +72,6 @@ MAGENTA="${UI_MAGENTA:-}" CYAN="${UI_CYAN:-}" BOLD="${UI_BOLD:-}" NC="${UI_RESET:-}" -CHECK="${GREEN}βœ”${NC}" -WARN="${YELLOW}⚠${NC}" -FAIL="${RED}βœ–${NC}" # --------------------------- Root check ------------------------------------ # FΓΌr sicheren lokalen Selftest (RUN_SELFTEST=1) ohne Root erlauben @@ -182,7 +179,7 @@ else printf '%sHinweis:%s /var/log nicht beschreibbar, Log nach %s.\n' "$YELLOW" "$NC" "$TMPDIR" fi -cleanup_tmpdir() { rm -rf "$TMPDIR" 2>/dev/null || true; } +cleanup_tmpdir() { rm -rf "$TMPDIR" 2> /dev/null || true; } trap cleanup_tmpdir EXIT INT TERM exec > >(tee -a "$LOGFILE") 2>&1 @@ -504,7 +501,7 @@ if ((DO_APT == 1)); then export DEBIAN_FRONTEND=noninteractive run_step 01 "πŸ”„" "APT: update & upgrade" "apt update && apt -y upgrade" true run_step 02 "🧹" "APT: autoremove & autoclean" "apt -y autoremove && apt -y autoclean" - if dpkg --print-architecture | grep -q '^armhf$'; then + if dpkg --print-architecture | grep -q '^armhf$'; then if apt list --upgradable 2> /dev/null | grep -q '^linux-image-rpi-v8'; then printf '%sHinweis:%s '\''linux-image-rpi-v8'\'' ist 64-bit (ARMv8). Auf Pi 3B (ARMv7) ignorierbar.\n' "$YELLOW" "$NC" fi From a923e0321ada49038217d2be556b4abae8a53502 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 19:37:36 +0000 Subject: [PATCH 3/3] fix(maintenance): preserve successful exit status in EXIT trap The refactor dropped the explicit `exit "$rc"` at the end of on_exit. On a clean run (rc=0) the trap's last evaluated command became `[[ 0 -ne 0 ]]`, which returns 1; under `set -e` that made the whole script exit 1 even when every step succeeded, so cron/systemd/CI wrappers would flag good runs as failures. Restore `exit "$rc"` as the final statement of on_exit. The re-entry guard already prevents double summary/cleanup, and calling exit inside the EXIT trap does not re-run it. INT/TERM still route through EXIT. Verified: buggy pattern exits 1 on a clean run, fixed pattern exits 0; full clean run of the script now returns 0. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MLBSE1qE3QvZYF8i9GjCz3 --- pihole_maintenance_pro.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pihole_maintenance_pro.sh b/pihole_maintenance_pro.sh index 8a608a4..e789420 100755 --- a/pihole_maintenance_pro.sh +++ b/pihole_maintenance_pro.sh @@ -477,6 +477,11 @@ on_exit() { fi cleanup_tmpdir [[ $rc -ne 0 ]] && printf '%sScript ended with exit code %s%s\n' "$RED" "$rc" "$NC" + # Exit-Status explizit erhalten: ohne dieses exit wΓΌrde bei rc=0 die vorherige + # (falsche) [[ ]]-Bedingung als RΓΌckgabewert der EXIT-Trap durchschlagen und + # erfolgreiche LΓ€ufe fΓ€lschlich als Fehler (Code 1) melden. Re-Entry-Guard + # oben verhindert dabei doppelte Summary/Cleanup. + exit "$rc" } # on_exit lΓ€uft genau einmal ΓΌber EXIT; INT/TERM leiten sauber dorthin um und # sorgen so auch bei Ctrl-C fΓΌr Summary + tmp-Cleanup.