#!/bin/sh -e

. /usr/lib/s6-policy-common/lib/svctl-common.sh

if test -z "$1" ; then
  echo "svctl-isup: usage: svctl-isup service" 1>&2
  exit 100
fi

if ! test -L /run/s6-rc/compiled ; then
  echo "svctl-isup: fatal: s6-rc is not running" 1>&2
  exit 2
fi

# Why this asks twice:
#
# Until 1.0.9 "svctl isup" answered from "s6-rc -a list" alone. That is the
# list of services s6-rc CONSIDERS active -- the result of the last transition,
# not the state of the process. A longrun that dies after its start stays in
# that list: on dns-1 "svctl isup pdns" reported "service pdns is running"
# while "s6-svstat" said "down (exitcode 1)" and no pdns_server process existed
# at all (Deploy, 23.09.2026).
#
# For a checking tool that is worse than an error: it does not say something
# incomplete, it says the opposite. So for a LONGRUN what the supervisor says
# counts as well. For a ONESHOT "s6-rc -a list" stays the only truth -- it has
# no process one could ask.
#
# The return codes stay so that callers do not break: 0 running, 3 not
# running, 4 unknown. New is 5 for NOT MEASURABLE -- fail-open: a state we
# could not determine must not pass as "running".
# The human svstat line of a longrun: "up (pid 8879) 200 seconds, ready 199
# seconds" or "down (exitcode 1) 0 seconds, normally up, want up". It carries
# the seconds, and those decide whether a service is running or has just been
# started for the seventh time.
_isup_line() {
  _d="/run/s6-rc/servicedirs/$1"
  if [ -d "$_d" ]; then
    s6-svstat "$_d" 2>/dev/null || echo 'not measurable'
  else
    echo 'oneshot (no supervisor, no process)'
  fi
}

_isup_one() {
  _s="$1"
  if ! svctl_is_active "$_s"; then
    return 3
  fi
  _t=$(s6-rc-db type "$_s" 2>/dev/null || echo unknown)
  if [ "$_t" != longrun ]; then
    return 0
  fi
  _dir="/run/s6-rc/servicedirs/$_s"
  if ! [ -d "$_dir" ]; then
    return 0
  fi
  _up=$(s6-svstat -o up "$_dir" 2>/dev/null) || return 5
  case "$_up" in
    true) return 0 ;;
    false) return 3 ;;
    *) return 5 ;;
  esac
}

if ! svctl_load_active; then
  svctl_report_unmeasurable
  exit 5
fi

if s6-rc-db list services | grep -qFx "$1" ; then
  rc=0
  _isup_one "$1" || rc=$?
  case "$rc" in
    0)
      # With the state line, so that "running" can be checked instead of
      # believed: seconds and ready are part of it.
      echo "service $1 is running: $(_isup_line "$1")"
      exit 0
      ;;
    3)
      if svctl_is_active "$1"; then
        # The case this is about: s6-rc lists it as active, the process is
        # not running. The svstat line is included because it is the diagnosis.
        echo "service $1 is NOT running, although s6-rc considers it active: $(_isup_line "$1")"
      else
        echo "service $1 is stopped"
      fi
      exit 3
      ;;
    *)
      echo "service $1 is NOT MEASURABLE: s6-svstat gave no answer for /run/s6-rc/servicedirs/$1" 1>&2
      exit 5
      ;;
  esac
elif s6-rc-db list bundles | grep -qFx "$1" ; then
  # A bundle is up when every directly contained unit is up. Until 1.0.9 the
  # loop ran in a subshell -- its "exit 3" left only the pipeline, not the
  # script, which then reported "bundle ... is running".
  rc=0
  for service in $(s6-rc-db contents "$1"); do
    _rc=0
    _isup_one "$service" || _rc=$?
    case "$_rc" in
      0) ;;
      3) echo "service $service in bundle $1 is not running: $(_isup_line "$service")"; rc=3 ;;
      *) echo "service $service in bundle $1 is NOT MEASURABLE" 1>&2; [ "$rc" = 3 ] || rc=5 ;;
    esac
  done
  if [ "$rc" = 0 ]; then
    echo "bundle $1 is running"
  fi
  exit "$rc"
else
  echo "svctl-isup: unknown atomic service: $1" 1>&2
  exit 4
fi
