#!/bin/sh -e

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

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

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

list=$(s6-rc-db atomics "$@")

# Oneshots have to be taken down before they can run again: "s6-rc -u change" on
# an already-active oneshot is a no-op, so until 2026-09-22 "svctl restart" on a
# oneshot did nothing at all and still exited 0 -- every "notify => Service[...]"
# on a oneshot was silently lost (S6-SYSTEM.md, and the Puppet provider reported
# "Triggered 'refresh'" for it).
#
# Note what this implies: taking a oneshot down takes its dependants down too,
# and "s6-rc -u change" below brings the named service back up, not them. That is
# s6-rc's dependency semantics, and it is the reason this only happens for
# oneshots that are actually up -- restarting a stopped one would have no
# meaning either way.
downagain=""
for i in $list ; do
  t=$(s6-rc-db type "$i")
  if test x"$t" = x"longrun" ; then
    # E4: WARTEN, wie "svctl start" es tut.
    #
    # "s6-svc -r" schickt das Signal und kehrt sofort zurueck; der anschliessende
    # "s6-rc -u change" ist fuer einen Dienst, den s6-rc schon als aktiv fuehrt,
    # ein No-op. Bis 1.1.3 war "svctl restart" damit fertig, bevor der Dienst
    # wieder oben war -- "start" erbt das Warten von s6-rc, "restart" erbte
    # nichts. Der Provider hat das ausgeglichen, Handgriffe und fremde postinsts
    # nicht.
    #
    # -wU wartet auf BEREIT, -wu nur auf oben. Was richtig ist, entscheidet die
    # Unit: ohne notification-fd meldet s6 ready == up, und -wU wartete dort ewig.
    #
    # -T nimmt dasselbe Limit wie jede andere Transition, im dpkg-Lauf also
    # SVCTL_TIMEOUT_DPKG (svctl_timeout entscheidet das an DPKG_RUNNING_VERSION).
    # Ohne -T waere das hier die naechste Sperre, die einen dpkg-Lauf haengen
    # laesst -- derselbe Fehler, den 1.1.3 in svctl-apply behoben hat.
    _rdir="/run/s6-rc/servicedirs/$i"
    if [ -e "$_rdir/notification-fd" ]; then
      _rwait="-wU"
      _rword="bereit"
    else
      _rwait="-wu"
      _rword="oben"
    fi
    _rrc=0
    s6-svc -r "$_rwait" -T "$(svctl_timeout)" "/run/s6-rc/scandir/$i" || _rrc=$?
    if [ "$_rrc" -ne 0 ]; then
      echo "svctl-restart: fatal: $i ist nach dem Neustart nicht wieder ${_rword} geworden" 1>&2
      echo "svctl-restart:        (Limit $(svctl_timeout) ms, s6-svc rc=${_rrc}). Zustand:" 1>&2
      s6-svstat "$_rdir" 2>&1 | sed 's/^/svctl-restart:        /' 1>&2
      exit "$_rrc"
    fi
  elif test x"$t" = x"oneshot" ; then
    if s6-rc -a list | grep -qFx "$i" ; then
      downagain="$downagain $i"
    fi
  fi
done

timeout="$(svctl_timeout)"

if test -n "$downagain" ; then
  # shellcheck disable=SC2086
  rc=0
  s6-rc -t "$timeout" -d change $downagain || rc=$?
  if [ "$rc" -ne 0 ]; then
    echo "svctl-restart: fatal: das Herunterfahren der Oneshots ist fehlgeschlagen oder hat" 1>&2
    echo "svctl-restart:        das Limit von ${timeout} ms gerissen (s6-rc rc=${rc})." 1>&2
    # shellcheck disable=SC2086
    svctl_report_pending down $downagain
    exit "$rc"
  fi
fi

# Mit Zeitlimit: ein Dienst, der beim Neustart haengt, darf kein postinst und
# keinen Puppet-Lauf blockieren (siehe svctl-update.unlocked).
rc=0
s6-rc -t "$timeout" -u change "$@" || rc=$?
if [ "$rc" -ne 0 ]; then
  echo "svctl-restart: fatal: der Zustandswechsel ist fehlgeschlagen oder hat das Limit von" 1>&2
  echo "svctl-restart:        ${timeout} ms gerissen (s6-rc rc=${rc})." 1>&2
  svctl_report_pending up "$@"
  exit "$rc"
fi
