1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
# GNU Shepherd --- Test transient services.
# Copyright © 2022-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 <https://www.gnu.org/licenses/>.
shepherd --version
herd --version
socket="t-socket-$$"
conf="t-conf-$$"
log="t-log-$$"
pid="t-pid-$$"
service_socket_dir="t-service-socket-$$"
service_socket="$service_socket_dir/socket"
herd="herd -s $socket"
# Create the socket directory with permissions other than those specified in
# the endpoint.
mkdir -p "$service_socket_dir"
chmod 755 "$service_socket_dir"
trap "cat $log || true; rm -r $service_socket_dir $socket $conf $log;
test -f $pid && kill \`cat $pid\` || true; rm -f $pid" EXIT
cat > "$conf" <<EOF
(define %command
;; Simple echo server.
(quasiquote ("$GUILE" "-c"
,(object->string
'(begin
(use-modules (ice-9 match) (ice-9 rdelim))
(setvbuf (current-output-port) 'line)
(setvbuf (current-error-port) 'line)
(display "starting\n")
(unless (= (string->number (getenv "LISTEN_PID")) (getpid))
(error "wrong pid!" (getenv "LISTEN_PID")))
(unless (= (string->number (getenv "LISTEN_FDS")) 1)
(error "wrong LISTEN_FDS!" (getenv "LISTEN_FDS")))
(let ((sock (fdopen 3 "r+0")))
(unless (zero? (logand O_NONBLOCK (fcntl sock F_GETFL)))
(error "socket is non-blocking!" sock))
(match (accept sock)
((connection . peer)
(format #t "accepting connection from ~s~%" peer)
(display "hello\n" connection)
(display (read-line connection) connection)
(newline connection)
(display "done\n" connection)
(display "exiting!\n")
(close-port connection)
(close-port sock)))))))))
(define %endpoints
(list (endpoint (make-socket-address AF_UNIX "$service_socket")
#:socket-directory-permissions #o700)))
(register-services
(list (service
'(test-systemd-unix)
#:start (make-systemd-constructor %command %endpoints)
#:stop (make-systemd-destructor)
#:respawn-delay 0 ;make the test slightly faster
#:respawn? #t)
(service
'(test-systemd-unix-eager)
#:start (make-systemd-constructor %command %endpoints
#:lazy-start? #f)
#:stop (make-systemd-destructor))))
EOF
rm -f "$pid"
shepherd -I -s "$socket" -c "$conf" -l "$log" --pid="$pid" &
# Wait till it's ready.
while ! test -f "$pid" ; do sleep 0.3 ; done
shepherd_pid="`cat $pid`"
file_permissions ()
{
"$GUILE" -c "(display (number->string (stat:perms (stat \"$1\")) 8))"
}
converse_with_echo_server ()
{
"$GUILE" -c "(use-modules (ice-9 match) (ice-9 rdelim))
(define address $1)
(define sock (socket (sockaddr:fam address) SOCK_STREAM 0))
(connect sock address)
(match (read-line sock) (\"hello\" #t))
(display \"bye\n\" sock)
(match (read-line sock) (\"bye\" #t))
(match (read-line sock) (\"done\" #t))"
}
$herd start test-systemd-unix
$herd status test-systemd-unix | grep running
test $($herd status | grep '\+' | wc -l) -eq 2
$herd status test-systemd-unix | grep "Systemd-style"
$herd status test-systemd-unix | grep "$service_socket"
$herd status test-systemd-unix | grep "Command: $GUILE.*begin.*LISTEN_PID"
test "$(file_permissions "$service_socket_dir")" = "700"
test "$(file_permissions "$service_socket")" = "666"
for i in $(seq 1 3)
do
converse_with_echo_server \
"(make-socket-address AF_UNIX \"$service_socket\")"
# Wait until the service has been respawned.
until $herd status test-systemd-unix | grep running
do
sleep 0.1
done
done
$herd stop test-systemd-unix
if converse_with_echo_server "(make-socket-address AF_UNIX \"$service_socket\")"
then false; else true; fi
# Now test the eager systemd-style service.
$herd start test-systemd-unix-eager
$herd status test-systemd-unix-eager | grep running
# The process should soon be running, before we've tried to connect to it.
while ! $herd status test-systemd-unix-eager | grep -E "PID: [0-9]+"
do $herd status test-systemd-unix-eager; sleep 0.3; done
child_pid="$($herd status test-systemd-unix-eager | grep PID: \
| sed '-es/.*PID: \([0-9]\+\)$/\1/g')"
kill -0 "$child_pid"
converse_with_echo_server "(make-socket-address AF_UNIX \"$service_socket\")"
while ! $herd status test-systemd-unix-eager | grep stopped
do sleep 0.3; done
|