File: inetd.sh

package info (click to toggle)
shepherd 1.0.9-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,828 kB
  • sloc: lisp: 8,779; sh: 3,586; makefile: 289; ansic: 50
file content (258 lines) | stat: -rw-r--r-- 7,546 bytes parent folder | download
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# 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="t-service-socket-$$"

herd="herd -s $socket"

trap "cat $log || true; rm -f $service_socket $socket $conf $log;
      test -f $pid && kill \`cat $pid\` || true; rm -f $pid" EXIT


PORT=4444			# port of the echo server

cat > "$conf" <<EOF
(define %command
  ;; Simple echo server.
  '("$SHELL" "-c" "echo hello; read line; echo \$line; echo done"))

(register-services
 (list
  (service
    '(test-inetd)
    #:start (make-inetd-constructor %command
				    (list
				     (endpoint (make-socket-address
						AF_INET
						INADDR_LOOPBACK
						$PORT)))
                                    #:service-name-stem "test-inetd")
    #:stop  (make-inetd-destructor))
  (service
    '(test-inetd6)
    #:start (make-inetd-constructor %command
				    (list
				     (endpoint (make-socket-address
						AF_INET
						INADDR_LOOPBACK
						$PORT))
				     (endpoint (make-socket-address
						AF_INET6
						IN6ADDR_LOOPBACK
						$PORT))))
    #:stop  (make-inetd-destructor))
  (service
    '(test-inetd-v6-only)
    #:start (make-inetd-constructor %command
				    (list
				     (endpoint (make-socket-address
						AF_INET6
						IN6ADDR_LOOPBACK
						$PORT))))
    #:stop  (make-inetd-destructor))
  (service
    '(test-inetd-unix)
    #:start (make-inetd-constructor %command
				    (list
				     (endpoint (make-socket-address
						AF_UNIX "$service_socket")))
				    #:max-connections 5)
    #:stop  (make-inetd-destructor))))

(start-service (lookup-service 'test-inetd))
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_descriptor_count ()
{
    ls -l /proc/$shepherd_pid/fd/[0-9]* | wc -l
}

# Trigger startup of the finalizer thread, which creates a couple of pipes.
# That way, those extra file descriptors won't influence the comparison with
# INITIAL_FD_COUNT done at the end.
$herd eval root '(gc)'

initial_fd_count=$(file_descriptor_count)

$herd status test-inetd | grep running
test $($herd status | grep '\+' | wc -l) -eq 2

converse_with_echo_server ()
{
    "$GUILE" -c "(use-modules (ice-9 match) (ice-9 rdelim))
      (define IN6ADDR_LOOPBACK 1)
      (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 status test-inetd | grep "Inetd-style service"
$herd status test-inetd | grep "127\.0\.0\.1:$PORT"
$herd status test-inetd | grep "Command:.*$SHELL.*echo hello"

# Initiate a conversation and make sure it creates a transient service with
# correct process information.
"$GUILE" -c "
  (define address (make-socket-address AF_INET INADDR_LOOPBACK $PORT))
  (define sock (socket (sockaddr:fam address) SOCK_STREAM 0))
  (connect sock address)
  (sleep 300)" &
client_pid=$!

# Wait until the inetd service shows up.
until $herd status test-inetd-1; do sleep 0.3; done

$herd status test-inetd-1 | grep transient
$herd status test-inetd-1 | grep "Command:.*$SHELL.*echo hello"
$herd stop test-inetd-1
kill "$client_pid" || true
$herd status test-inetd-1 && false

for i in $(seq 1 3)
do
    test $($herd status | grep '\+' | wc -l) -eq 2
    converse_with_echo_server \
	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
done

# Unavailable on IPv6.
converse_with_echo_server \
    "(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)" \
     && false

$herd stop test-inetd
converse_with_echo_server \
  "(make-socket-address AF_INET INADDR_LOOPBACK $PORT)" \
   && false

if "$GUILE" -c '(socket AF_INET6 SOCK_STREAM 0)'; then
    # Test IPv6 support.
    $herd start test-inetd6

    converse_with_echo_server \
	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
    converse_with_echo_server \
	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"

    $herd stop test-inetd6

    ! converse_with_echo_server \
	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
    ! converse_with_echo_server \
	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"

    $herd start test-inetd-v6-only

    converse_with_echo_server \
	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
    ! converse_with_echo_server \
	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"

    $herd stop test-inetd-v6-only

    ! converse_with_echo_server \
	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
    ! converse_with_echo_server \
	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"

    # Note: The following test below would hang with Fibers 1.1.0, due to
    # <https://codeberg.org/guile/fibers/pulls/57>.
fi

# Now test inetd on a Unix-domain socket.

$herd start test-inetd-unix
for i in $(seq 1 3)
do
    test $($herd status | grep '\+' | wc -l) -eq 2
    converse_with_echo_server \
	"(make-socket-address AF_UNIX \"$service_socket\")"
done

$herd stop test-inetd-unix
converse_with_echo_server \
  "(make-socket-address AF_UNIX \"$service_socket\")" \
   && false

# Check the maximum connection limit.
$herd start test-inetd-unix
"$GUILE" -c "
  (use-modules (ice-9 rdelim) (ice-9 match))
  (define address (make-socket-address AF_UNIX \"$service_socket\"))
  (let loop ((i 10)
             (sockets '()))
    (if (zero? i)
        ;; shepherd should close the extra sockets immediately.
        (unless (equal? (append (make-list 5 the-eof-object)
                                (make-list 5 \"hello\"))
                        (pk 'read (map read-line sockets)))
          (exit 1))
        (let ((sock (socket AF_UNIX SOCK_STREAM 0)))
          (connect sock address)
          (loop (- i 1) (cons sock sockets)))))"

converse_with_echo_server \
  "(make-socket-address AF_UNIX \"$service_socket\")"

$herd stop test-inetd-unix
$herd status

# Simulate EADDRINUSE.
$herd eval root "
  (let ((real-bind bind)
        (failures 0))
    (set! bind (lambda (sock address)
                 (when (< failures 2)
                   (set! failures (+ failures 1))
                   (throw 'system-error \"bind\" \"Oh!\" '()
                          (list EADDRINUSE)))
                 (set! bind real-bind)
                 (real-bind sock address))))"

$herd start test-inetd-unix
$herd status test-inetd-unix | grep running
$herd stop test-inetd-unix

grep "is in use" "$log"
$herd status

if test -d "/proc/$$/fd"	# GNU/Hurd lacks /proc/*/fd.
then
    # At this point, shepherd should have INITIAL_FD_COUNT - 1 file
    # descriptors opened.
    test $(file_descriptor_count) -lt $initial_fd_count
fi