xref: /freebsd/usr.sbin/syslogd/tests/syslogd_test.sh (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
1#-
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2021, 2023 The FreeBSD Foundation
5#
6# This software was developed by Mark Johnston under sponsorship from
7# the FreeBSD Foundation.
8#
9# This software was developed by Jake Freeland under sponsorship from
10# the FreeBSD Foundation.
11#
12
13# Tests to-do:
14# actions: users
15
16readonly SYSLOGD_UDP_PORT="5140"
17readonly SYSLOGD_CONFIG="${PWD}/syslog.conf"
18readonly SYSLOGD_LOCAL_SOCKET="${PWD}/log.sock"
19readonly SYSLOGD_PIDFILE="${PWD}/syslogd.pid"
20readonly SYSLOGD_LOCAL_PRIVSOCKET="${PWD}/logpriv.sock"
21
22# Start a private syslogd instance.
23syslogd_start()
24{
25    local jail bind_addr conf_file pid_file socket privsocket
26    local opt next other_args
27
28    # Setup loopback so we can deliver messages to ourself.
29    atf_check ifconfig lo0 inet 127.0.0.1/16
30
31    OPTIND=1
32    while getopts ":b:f:j:P:p:S:" opt; do
33        case "${opt}" in
34        b)
35            bind_addr="${OPTARG}"
36            ;;
37        f)
38            conf_file="${OPTARG}"
39            ;;
40        j)
41            jail="jexec ${OPTARG}"
42            ;;
43        P)
44            pid_file="${OPTARG}"
45            ;;
46        p)
47            socket="${OPTARG}"
48            ;;
49        S)
50            privsocket="${OPTARG}"
51            ;;
52        ?)
53            opt="${OPTARG}"
54            next="$(eval echo \${${OPTIND}})"
55
56            case "${next}" in
57            -* | "")
58                other_args="${other_args} -${opt}"
59                shift $((OPTIND - 1))
60                ;;
61            *)
62                other_args="${other_args} -${opt} ${next}"
63                shift ${OPTIND}
64                ;;
65            esac
66
67            # Tell getopts to continue parsing.
68            OPTIND=1
69            ;;
70        :)
71            atf_fail "The -${OPTARG} flag requires an argument"
72            ;;
73        esac
74    done
75
76    $jail syslogd \
77        -b "${bind_addr:-":${SYSLOGD_UDP_PORT}"}" \
78        -C \
79        -d \
80        -f "${conf_file:-${SYSLOGD_CONFIG}}" \
81        -H \
82        -P "${pid_file:-${SYSLOGD_PIDFILE}}" \
83        -p "${socket:-${SYSLOGD_LOCAL_SOCKET}}" \
84        -S "${privsocket:-${SYSLOGD_LOCAL_PRIVSOCKET}}" \
85        ${other_args} \
86        &
87
88    # Give syslogd a bit of time to spin up.
89    while [ "$((i+=1))" -le 20 ]; do
90        [ -S "${socket:-${SYSLOGD_LOCAL_SOCKET}}" ] && return
91        sleep 0.1
92    done
93    atf_fail "timed out waiting for syslogd to start"
94}
95
96# Simple logger(1) wrapper.
97syslogd_log()
98{
99    atf_check -s exit:0 -o empty -e empty logger $*
100}
101
102# Make syslogd reload its configuration file.
103syslogd_reload()
104{
105    pkill -HUP -F "${1:-${SYSLOGD_PIDFILE}}"
106}
107
108# Stop a private syslogd instance.
109syslogd_stop()
110{
111    local pid_file="${1:-${SYSLOGD_PIDFILE}}"
112
113    pid=$(cat "${pid_file}")
114    if pkill -F "${pid_file}"; then
115        wait "${pid}"
116        rm -f "${pid_file}" "${2:-${SYSLOGD_LOCAL_SOCKET}}" \
117            "${3:-${SYSLOGD_LOCAL_PRIVSOCKET}}"
118    fi
119}
120
121atf_test_case "unix" "cleanup"
122unix_head()
123{
124    atf_set descr "Messages are logged over UNIX transport"
125}
126unix_body()
127{
128    local logfile="${PWD}/unix.log"
129
130    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
131    syslogd_start
132
133    syslogd_log -p user.debug -t unix -h "${SYSLOGD_LOCAL_SOCKET}" \
134        "hello, world (unix)"
135    atf_check -s exit:0 -o match:"unix: hello, world \(unix\)" \
136        tail -n 1 "${logfile}"
137}
138unix_cleanup()
139{
140    syslogd_stop
141}
142
143atf_test_case "inet" "cleanup"
144inet_head()
145{
146    atf_set descr "Messages are logged over INET transport"
147}
148inet_body()
149{
150    local logfile="${PWD}/inet.log"
151
152    [ "$(sysctl -n kern.features.inet)" != "1" ] &&
153        atf_skip "Kernel does not support INET"
154
155    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
156    syslogd_start
157
158    # We have INET transport; make sure we can use it.
159    syslogd_log -4 -p user.debug -t inet -h 127.0.0.1 -P "${SYSLOGD_UDP_PORT}" \
160        "hello, world (v4)"
161    atf_check -s exit:0 -o match:"inet: hello, world \(v4\)" \
162        tail -n 1 "${logfile}"
163}
164inet_cleanup()
165{
166    syslogd_stop
167}
168
169atf_test_case "inet6" "cleanup"
170inet6_head()
171{
172    atf_set descr "Messages are logged over INET6 transport"
173}
174inet6_body()
175{
176    local logfile="${PWD}/inet6.log"
177
178    [ "$(sysctl -n kern.features.inet6)" != "1" ] &&
179        atf_skip "Kernel does not support INET6"
180
181    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
182    syslogd_start
183
184    # We have INET6 transport; make sure we can use it.
185    syslogd_log -6 -p user.debug -t unix -h ::1 -P "${SYSLOGD_UDP_PORT}" \
186        "hello, world (v6)"
187    atf_check -s exit:0 -o match:"unix: hello, world \(v6\)" \
188        tail -n 1 "${logfile}"
189}
190inet6_cleanup()
191{
192    syslogd_stop
193}
194
195atf_test_case "reload" "cleanup"
196reload_head()
197{
198    atf_set descr "SIGHUP correctly refreshes configuration"
199}
200reload_body()
201{
202    logfile="${PWD}/reload.log"
203    printf "user.debug\t/${logfile}\n" > "${SYSLOGD_CONFIG}"
204    syslogd_start
205
206    syslogd_log -p user.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
207        "pre-reload"
208    atf_check -s exit:0 -o match:"reload: pre-reload" tail -n 1 "${logfile}"
209
210    # Override the old rule.
211    truncate -s 0 "${logfile}"
212    printf "news.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
213    syslogd_reload
214
215    syslogd_log -p user.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
216        "post-reload user"
217    syslogd_log -p news.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
218        "post-reload news"
219    atf_check -s exit:0 -o not-match:"reload: post-reload user" cat ${logfile}
220    atf_check -s exit:0 -o match:"reload: post-reload news" cat ${logfile}
221}
222reload_cleanup()
223{
224    syslogd_stop
225}
226
227atf_test_case "prog_filter" "cleanup"
228prog_filter_head()
229{
230    atf_set descr "Messages are only received from programs in the filter"
231}
232prog_filter_body()
233{
234    logfile="${PWD}/prog_filter.log"
235    printf "!prog1,prog2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
236    syslogd_start
237
238    for i in 1 2 3; do
239        syslogd_log -p user.debug -t "prog${i}" -h "${SYSLOGD_LOCAL_SOCKET}" \
240            "hello this is prog${i}"
241    done
242    atf_check -s exit:0 -o match:"prog1: hello this is prog1" cat "${logfile}"
243    atf_check -s exit:0 -o match:"prog2: hello this is prog2" cat "${logfile}"
244    atf_check -s exit:0 -o not-match:"prog3: hello this is prog3" cat "${logfile}"
245
246    # Override the old rule.
247    truncate -s 0 ${logfile}
248    printf "!-prog1,prog2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
249    syslogd_reload
250
251    for i in 1 2 3; do
252        syslogd_log -p user.debug -t "prog${i}" -h "${SYSLOGD_LOCAL_SOCKET}" \
253            "hello this is prog${i}"
254    done
255    atf_check -s exit:0 -o not-match:"prog1: hello this is prog1" cat "${logfile}"
256    atf_check -s exit:0 -o not-match:"prog2: hello this is prog2" cat "${logfile}"
257    atf_check -s exit:0 -o match:"prog3: hello this is prog3" cat "${logfile}"
258}
259prog_filter_cleanup()
260{
261    syslogd_stop
262}
263
264atf_test_case "host_filter" "cleanup"
265host_filter_head()
266{
267    atf_set descr "Messages are only received from hostnames in the filter"
268}
269host_filter_body()
270{
271    logfile="${PWD}/host_filter.log"
272    printf "+host1,host2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
273    syslogd_start
274
275    for i in 1 2 3; do
276        syslogd_log -p user.debug -t "host${i}" -H "host${i}" \
277            -h "${SYSLOGD_LOCAL_SOCKET}" "hello this is host${i}"
278    done
279    atf_check -s exit:0 -o match:"host1: hello this is host1" cat "${logfile}"
280    atf_check -s exit:0 -o match:"host2: hello this is host2" cat "${logfile}"
281    atf_check -s exit:0 -o not-match:"host3: hello this is host3" cat "${logfile}"
282
283    # Override the old rule.
284    truncate -s 0 ${logfile}
285    printf "\-host1,host2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
286    syslogd_reload
287
288    for i in 1 2 3; do
289        syslogd_log -p user.debug -t "host${i}" -H "host${i}" \
290        -h "${SYSLOGD_LOCAL_SOCKET}" "hello this is host${i}"
291    done
292    atf_check -s exit:0 -o not-match:"host1: hello this is host1" cat "${logfile}"
293    atf_check -s exit:0 -o not-match:"host2: hello this is host2" cat "${logfile}"
294    atf_check -s exit:0 -o match:"host3: hello this is host3" cat "${logfile}"
295}
296host_filter_cleanup()
297{
298    syslogd_stop
299}
300
301atf_test_case "prop_filter" "cleanup"
302prop_filter_head()
303{
304    atf_set descr "Messages are received based on conditions in the propery based filter"
305}
306prop_filter_body()
307{
308    logfile="${PWD}/prop_filter.log"
309    printf ":msg,contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
310        > "${SYSLOGD_CONFIG}"
311    syslogd_start
312
313    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
314    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
315    atf_check -s exit:0 -o match:"prop1: FreeBSD" cat "${logfile}"
316    atf_check -s exit:0 -o not-match:"prop2: freebsd" cat "${logfile}"
317
318    truncate -s 0 ${logfile}
319    printf ":msg,!contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
320        > "${SYSLOGD_CONFIG}"
321    syslogd_reload
322
323    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
324    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
325    atf_check -s exit:0 -o not-match:"prop1: FreeBSD" cat "${logfile}"
326    atf_check -s exit:0 -o match:"prop2: freebsd" cat "${logfile}"
327
328    truncate -s 0 ${logfile}
329    printf ":msg,icase_contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
330        > "${SYSLOGD_CONFIG}"
331    syslogd_reload
332
333    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
334    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
335    atf_check -s exit:0 -o match:"prop1: FreeBSD" cat "${logfile}"
336    atf_check -s exit:0 -o match:"prop2: freebsd" cat "${logfile}"
337
338    truncate -s 0 ${logfile}
339    printf ":msg,!icase_contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
340        > "${SYSLOGD_CONFIG}"
341    syslogd_reload
342
343    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
344    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
345    syslogd_log -p user.debug -t "prop3" -h "${SYSLOGD_LOCAL_SOCKET}" "Solaris"
346    atf_check -s exit:0 -o not-match:"prop1: FreeBSD" cat "${logfile}"
347    atf_check -s exit:0 -o not-match:"prop2: freebsd" cat "${logfile}"
348    atf_check -s exit:0 -o match:"prop3: Solaris" cat "${logfile}"
349}
350prop_filter_cleanup()
351{
352    syslogd_stop
353}
354
355atf_test_case "host_action" "cleanup"
356host_action_head()
357{
358    atf_set descr "Sends a message to a specified host"
359}
360host_action_body()
361{
362    local addr="192.0.2.100"
363    local logfile="${PWD}/host_action.log"
364
365    atf_check ifconfig lo1 create
366    atf_check ifconfig lo1 inet "${addr}/24"
367    atf_check ifconfig lo1 up
368
369    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
370    syslogd_start -b "${addr}"
371
372    printf "user.debug\t@${addr}\n" > "${SYSLOGD_CONFIG}.2"
373    syslogd_start \
374        -f "${SYSLOGD_CONFIG}.2" \
375        -P "${SYSLOGD_PIDFILE}.2" \
376        -p "${SYSLOGD_LOCAL_SOCKET}.2" \
377        -S "${SYSLOGD_LOCAL_PRIVSOCKET}.2"
378
379    syslogd_log -p user.debug -t "test" -h "${SYSLOGD_LOCAL_SOCKET}.2" \
380        "message from syslogd2"
381    atf_check -s exit:0 -o match:"test: message from syslogd2" \
382        cat "${logfile}"
383}
384host_action_cleanup()
385{
386    syslogd_stop
387    syslogd_stop \
388        "${SYSLOGD_PIDFILE}.2" \
389        "${SYSLOGD_LOCAL_SOCKET}.2" \
390        "${SYSLOGD_LOCAL_PRIVSOCKET}.2"
391    atf_check ifconfig lo1 destroy
392}
393
394atf_test_case "pipe_action" "cleanup"
395pipe_action_head()
396{
397    atf_set descr "The pipe action evaluates provided command in sh(1)"
398}
399pipe_action_body()
400{
401    logfile="${PWD}/pipe_action.log"
402    printf "\"While I'm digging in the tunnel, the elves will often come to me \
403        with solutions to my problem.\"\n-Saymore Crey" > ${logfile}
404
405    printf "!pipe\nuser.debug\t| sed -i '' -e 's/Saymore Crey/Seymour Cray/g' \
406        ${logfile}\n" > "${SYSLOGD_CONFIG}"
407    syslogd_start
408
409    syslogd_log -p user.debug -t "pipe" -h "${SYSLOGD_LOCAL_SOCKET}" \
410        "fix spelling error"
411    atf_check -s exit:0 -o match:"Seymour Cray" cat "${logfile}"
412}
413pipe_action_cleanup()
414{
415    syslogd_stop
416}
417
418atf_test_case "jail_noinet" "cleanup"
419jail_noinet_head()
420{
421    atf_set descr "syslogd -ss can be run in a jail without INET support"
422    atf_set require.user root
423}
424jail_noinet_body()
425{
426    local logfile
427
428    atf_check jail -c name=syslogd_noinet persist
429
430    logfile="${PWD}/jail_noinet.log"
431    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
432    syslogd_start -j syslogd_noinet -s -s
433
434    syslogd_log -p user.debug -t "test" -h "${SYSLOGD_LOCAL_SOCKET}" \
435        "hello, world"
436    atf_check -s exit:0 -o match:"test: hello, world" cat "${logfile}"
437}
438jail_noinet_cleanup()
439{
440    jail -r syslogd_noinet
441}
442
443atf_init_test_cases()
444{
445    atf_add_test_case "unix"
446    atf_add_test_case "inet"
447    atf_add_test_case "inet6"
448    atf_add_test_case "reload"
449    atf_add_test_case "prog_filter"
450    atf_add_test_case "host_filter"
451    atf_add_test_case "prop_filter"
452    atf_add_test_case "host_action"
453    atf_add_test_case "pipe_action"
454    atf_add_test_case "jail_noinet"
455}
456