# GNU Shepherd --- Test shepherd behavior when syslogd is slow.
# 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-$$"
pid="t-pid-$$"
logger="$PWD/t-syslog-logger-$$.scm"
syslogd="$PWD/t-syslog-syslogd-$$.scm"
syslog_file="$PWD/t-syslog-file-$$.log"
syslog_socket="$PWD/t-syslog-socket-$$"

herd="herd -s $socket"

trap "cat $syslog_file || true;
      rm -f $socket $conf $logger $syslogd $syslog_socket $syslog_file;
      kill \"\$syslogd_pid\" || true;
      test -f $pid && kill \`cat $pid\` || true; rm -f $pid" EXIT

cat > "$conf" <<EOF
(register-services
  (list (service
          '(logger)
          #:start (make-forkexec-constructor '("$logger"))
          #:stop (make-kill-destructor)
          #:respawn? #f)))
EOF

cat > "$syslogd" <<EOF
#!$GUILE --no-auto-compile
!#
(use-modules (rnrs bytevectors)
             (rnrs bytevectors gnu)
             (ice-9 match))

(setvbuf (current-output-port) 'line)
(setvbuf (current-error-port) 'line)

(format #t "starting syslogd~%")
(let ((socket (socket AF_UNIX SOCK_DGRAM 0))
      (buffer (make-bytevector 1024)))
  (bind socket AF_UNIX "$syslog_socket" 10)
  (format #t "listening to syslogd incoming messages~%")

  (call-with-output-file "$syslog_file"
    (lambda (port)
      (setvbuf port 'line)

      ;; Mimic a syslogd that processes incoming messages more slowly
      ;; than the incoming throughput.  In the shepherd process, this
      ;; leads write(2) calls to syslog to return EAGAIN.
      ;; See <https://issues.guix.gnu.org/76315>.
      (let loop ()
	(match (recvfrom! socket buffer)
	  ((bytes . _)
           (let ((str (utf8->string (bytevector-slice buffer 0 bytes))))
  	     (format #t "syslog: ~a" str)
	     (display str port))))
	(usleep 500000)
	(loop)))))
EOF

chmod +x "$syslogd"

cat > "$logger" <<EOF
#!$GUILE --no-auto-compile
!#
(use-modules (ice-9 format))

(display "starting logger\n")

;; Log messages as fast as possible to saturate the kernel's
;; buffers for the syslog socket.
(let loop ((i 500))
  (format #t "~3a ~a~%" i (make-string 100 #\a))
  (unless (zero? i)
    (loop (- i 1))))
EOF

chmod +x "$logger"

"$syslogd" &
syslogd_pid=$!

rm -f "$pid"
shepherd -I -s "$socket" -c "$conf" --syslog="$syslog_socket" --pid="$pid" &

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

$herd start logger
$herd status
$herd status logger
$herd eval root '(display "hello!\n")'
$herd status root | grep aaaaaaaaaaa
$herd status
until grep -q "aaaaaaaaaaaaaaa" "$syslog_file"; do sleep 0.5; done
$herd stop root
