#!/usr/bin/env bash ## stop-services.sh — sourced by the bash PID-1 entrypoints (entrypoint.sh, ## entrypoint-fpm.sh, entrypoint-shared-httpd.sh). Not executed directly. ## ## Why this exists: those entrypoints end with bash as PID 1 tailing logs. ## The kernel never delivers a signal to PID 1 unless PID 1 installed a handler ## for it, so `docker stop`'s SIGTERM was silently dropped. Docker waited the ## full stop timeout (10s) and then SIGKILLed the whole container, killing ## apache / php-fpm mid-request (exit 137). The entrypoints now trap TERM/INT ## and call stop_service for each daemon they started, then exit 0. ## ## Budget: docker's default stop timeout is 10s. Callers pass per-service ## timeouts that sum to < 10s so the graceful path always completes before ## docker gives up and SIGKILLs. # A pid counts as alive if it exists and is not a zombie. bash (PID 1) reaps # the daemons it adopted, but between exit and reap a zombie still answers # `kill -0`, so check /proc state too. _svc_alive() { [ -n "$1" ] || return 1 kill -0 "$1" 2>/dev/null || return 1 ! grep -qs '^State:[[:space:]]*Z' "/proc/$1/status" } # stop_service NAME PIDFILE GRACEFUL_SIGNAL GRACE_SECONDS [PGREP_PATTERN] # Sends GRACEFUL_SIGNAL, waits up to GRACE_SECONDS for the process to exit, # then sends TERM and waits 1s more, then KILL. Pid comes from PIDFILE, or # from `pgrep -o -f PGREP_PATTERN` when the pidfile is missing/stale. stop_service() { local name=$1 pidfile=$2 sig=$3 grace=$4 pat=${5:-} pid="" i [ -r "$pidfile" ] && pid=$(tr -dc '0-9' < "$pidfile") if ! _svc_alive "$pid" && [ -n "$pat" ]; then pid=$(pgrep -o -f "$pat" 2>/dev/null) fi if ! _svc_alive "$pid"; then echo "entrypoint: $name not running" return 0 fi echo "entrypoint: stopping $name (pid $pid) with SIG$sig" kill -s "$sig" "$pid" 2>/dev/null for ((i = 0; i < grace * 10; i++)); do _svc_alive "$pid" || { echo "entrypoint: $name stopped"; return 0; } sleep 0.1 done echo "entrypoint: $name still running after ${grace}s, sending SIGTERM" kill -TERM "$pid" 2>/dev/null for ((i = 0; i < 10; i++)); do _svc_alive "$pid" || { echo "entrypoint: $name stopped"; return 0; } sleep 0.1 done echo "entrypoint: $name still running, sending SIGKILL" kill -KILL "$pid" 2>/dev/null return 0 }