#!/bin/sh
# 固定监督程序。安装为 /usr/local/sbin/nestssl-webhook-reload，root:root、0755。
# 不读取参数，也不把请求内容传给后面的动作。
# 超时、动作退出和异常退出都按整个进程组里仍能执行的进程清理。
# 组长已经退出时，仍向组内剩余进程发送 KILL。僵尸进程不算未结束。
# 确认这些进程退出后才返回；仍不能确认时返回 125。
set -eu
TIMEOUT=10
ACTION=/usr/local/libexec/nestssl-webhook/reload-nginx

if [ ! -x "$ACTION" ]; then
  exit 1
fi

pid_executable() {
  stat=$(ps -o stat= -p "$1" 2>/dev/null | tr -d ' ' || true)
  case "$stat" in
    ''|Z*) return 1 ;;
    *) return 0 ;;
  esac
}

group_busy() {
  pids=$(pgrep -g "$1" 2>/dev/null || true)
  for member in $pids; do
    if pid_executable "$member"; then
      return 0
    fi
  done
  return 1
}

stop_group() {
  kill -TERM -"$1" 2>/dev/null || true
  sleep 0.2
  if group_busy "$1"; then
    kill -KILL -"$1" 2>/dev/null || true
  fi
  j=0
  while [ "$j" -lt 25 ]; do
    if ! group_busy "$1"; then
      return 0
    fi
    kill -KILL -"$1" 2>/dev/null || true
    sleep 0.1
    j=$((j + 1))
  done
  return 1
}

finish_locked() {
  wait "$launcher" 2>/dev/null || true
  rm -f "$pidfile" "$statusfile"
  exit "$1"
}

pidfile=$(mktemp)
statusfile=$(mktemp)
env -i PATH="/usr/sbin:/usr/bin:/sbin:/bin" setsid /bin/sh -c 'echo $$ > "$1"; "$2"; echo $? > "$3"' sh "$pidfile" "$ACTION" "$statusfile" >/dev/null 2>&1 &
launcher=$!

i=0
while [ ! -s "$pidfile" ] && [ "$i" -lt 50 ]; do
  sleep 0.05
  i=$((i + 1))
done
if [ ! -s "$pidfile" ]; then
  wait "$launcher" 2>/dev/null || true
  rm -f "$pidfile" "$statusfile"
  exit 1
fi
pid=$(cat "$pidfile")

deadline=$(( $(date +%s) + TIMEOUT ))
while true; do
  if ! pid_executable "$pid"; then
    if group_busy "$pid"; then
      if stop_group "$pid"; then
        finish_locked 1
      fi
      finish_locked 125
    fi
    break
  fi
  if [ "$(date +%s)" -ge "$deadline" ]; then
    if stop_group "$pid"; then
      finish_locked 124
    fi
    finish_locked 125
  fi
  sleep 0.2
done

wait "$launcher" 2>/dev/null || true
status=1
if [ -s "$statusfile" ]; then
  status=$(cat "$statusfile")
fi
rm -f "$pidfile" "$statusfile"
case "$status" in
  ''|*[!0-9]*) status=1 ;;
esac
exit "$status"
