diff --git a/Watchdogs/stability_watchdog.sh b/Watchdogs/stability_watchdog.sh index 0c7f4ad..749b648 100755 --- a/Watchdogs/stability_watchdog.sh +++ b/Watchdogs/stability_watchdog.sh @@ -535,7 +535,28 @@ do_reboot() { if is_docker_enabled && command -v docker >/dev/null 2>&1; then mapfile -t _SYS_REBOOT_STOPPED < <(timeout "$DOCKER_TIMEOUT" docker ps --format '{{.Names}}' 2>/dev/null) trap _trap_sys_reboot_restart EXIT - timeout 60 docker ps -q 2>/dev/null | xargs -r docker stop >/dev/null 2>&1 + + # One container at a time, bounded individually, named when it fails. + # + # This was `timeout 60 docker ps -q | xargs -r docker stop`, where the timeout bounded + # only the left of the pipe — the stops themselves were unbounded, so a wedged daemon + # hung the reboot path indefinitely, on the half that actually stops the containers. + # + # Bounding the batch instead would mean picking a number against the container count, + # and being wrong either kills containers mid-write or never returns. Per container that + # question disappears: DOCKER_TIMEOUT is three times docker's own 10s stop grace, and + # holds whether this host runs five containers or fifty. + # + # It also reuses the list already read above rather than asking docker a second time, + # and matches stop_local_containers() in common.sh — loop, report by name, never xargs. + for _c in "${_SYS_REBOOT_STOPPED[@]}"; do + [[ -z "$_c" ]] && continue + if timeout "$DOCKER_TIMEOUT" docker stop "$_c" >/dev/null 2>&1; then + log " $_c stopped" + else + warn " $_c did not stop within ${DOCKER_TIMEOUT}s — continuing to reboot" + fi + done fi warn "Stopping User Scripts..."