# GNU Shepherd --- Ensure respawning is inhibited when shutting down.
# Copyright © 2025 Ludovic Courtès <ludo@gnu.org>
#
# This file is part of the GNU Shepherd.
#
# The GNU Shepherd is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# The GNU Shepherd is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the GNU Shepherd.  If not, see <http://www.gnu.org/licenses/>.

shepherd --version
herd --version

socket="t-socket-$$"
conf="t-conf-$$"
log="t-log-$$"
pid="t-pid-$$"

herd="herd -s $socket"

trap "cat $log || true; rm -f $log $socket $conf $pid;
      kill \$main_pid || true" EXIT

cat > "$conf"<<EOF
(define (terminate-service-processes)
  ;; Terminate each process associated with a running service.
  (define try-again? #f)
  (format #t "terminating service processes...~%")
  (for-each-service
    (lambda (service)
      (unless (eq? service root-service)
	(let ((value (service-running-value service)))
	  (when (process? value)
            (format #t "terminating process ~a of '~a'...~%"
                    (process-id value) (service-canonical-name service))
	    (kill (process-id value) SIGTERM)
	    (set! try-again? #t))))))
  (when try-again?
    (sleep 1)
    (terminate-service-processes)))

(define other-service-names
  '(a b c d e f g))

;; Disable respawn throttling.
(default-respawn-limit '(+inf.0 . 1))

(register-services
  (cons (service
          '(terminator)
          #:requirement other-service-names
          #:start (const #t)
          #:stop (lambda _
                   ;; Terminate processes associated with other
                   ;; services, just like 'user-processes' on
                   ;; Guix System.
                   (terminate-service-processes)))
        (map (lambda (name)
               (service
                (list name)
                #:start (make-forkexec-constructor
                         '("sleep" "180"))
                #:stop (make-kill-destructor)
                #:respawn? #t))
             other-service-names)))

(start-in-the-background (cons 'terminator other-service-names))
EOF

rm -f "$pid"

shepherd -I -s "$socket" -c "$conf" -l "$log" --pid="$pid" &
main_pid=$!

# Wait till it's ready.
until test -f "$pid" ; do sleep 0.3 ; done

until $herd status terminator | grep running; do sleep 0.3; done
$herd status

# Shut down.  This also stops 'terminator', which terminates all the processes
# of all the other services in its 'stop' method.
$herd stop root

# Wait until shepherd is really gone.
while kill -0 $main_pid; do sleep 0.2; done

# Processes were indeed terminated but respawning was inhibited since shepherd
# was shutting down.
grep "terminated with signal 15" "$log"
grep -v "Respawning" "$log"
