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: hostname, 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    syslogd \
26        -b ":${SYSLOGD_UDP_PORT}" \
27        -C \
28        -d \
29        -f "${SYSLOGD_CONFIG}" \
30        -H \
31        -p "${SYSLOGD_LOCAL_SOCKET}" \
32        -P "${SYSLOGD_PIDFILE}" \
33        -S "${SYSLOGD_LOCAL_PRIVSOCKET}" \
34        $@ \
35        &
36
37    # Give syslogd a bit of time to spin up.
38    while [ "$((i+=1))" -le 20 ]; do
39        [ -S "${SYSLOGD_LOCAL_SOCKET}" ] && return
40        sleep 0.1
41    done
42    atf_fail "timed out waiting for syslogd to start"
43}
44
45# Simple logger(1) wrapper.
46syslogd_log()
47{
48    atf_check -s exit:0 -o empty -e empty logger $*
49}
50
51# Make syslogd reload its configuration file.
52syslogd_reload()
53{
54    pkill -HUP -F "${SYSLOGD_PIDFILE}"
55}
56
57# Stop a private syslogd instance.
58syslogd_stop()
59{
60    pid=$(cat "${SYSLOGD_PIDFILE}")
61    if pkill -F "${SYSLOGD_PIDFILE}"; then
62        wait "${pid}"
63        rm -f "${SYSLOGD_PIDFILE}" "${SYSLOGD_LOCAL_SOCKET}" \
64            "${SYSLOGD_LOCAL_PRIVSOCKET}"
65    fi
66}
67
68atf_test_case "basic" "cleanup"
69basic_head()
70{
71    atf_set descr "Messages are logged via supported transports"
72}
73basic_body()
74{
75    logfile="${PWD}/basic.log"
76    printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
77    syslogd_start
78
79    syslogd_log -p user.debug -t basic -h "${SYSLOGD_LOCAL_SOCKET}" \
80        "hello, world (unix)"
81    atf_check -s exit:0 -o match:"basic: hello, world \(unix\)" \
82        tail -n 1 "${logfile}"
83
84    # Grab kernel configuration file.
85    sysctl kern.conftxt > conf.txt
86
87    # We have INET transport; make sure we can use it.
88    if grep -qw "INET" conf.txt; then
89        syslogd_log -4 -p user.debug -t basic -h 127.0.0.1 -P "${SYSLOGD_UDP_PORT}" \
90            "hello, world (v4)"
91        atf_check -s exit:0 -o match:"basic: hello, world \(v4\)" \
92            tail -n 1 "${logfile}"
93    fi
94    # We have INET6 transport; make sure we can use it.
95    if grep -qw "INET6" conf.txt; then
96        syslogd_log -6 -p user.debug -t basic -h ::1 -P "${SYSLOGD_UDP_PORT}" \
97            "hello, world (v6)"
98        atf_check -s exit:0 -o match:"basic: hello, world \(v6\)" \
99            tail -n 1 "${logfile}"
100    fi
101}
102basic_cleanup()
103{
104    syslogd_stop
105}
106
107atf_test_case "reload" "cleanup"
108reload_head()
109{
110    atf_set descr "SIGHUP correctly refreshes configuration"
111}
112reload_body()
113{
114    logfile="${PWD}/reload.log"
115    printf "user.debug\t/${logfile}\n" > "${SYSLOGD_CONFIG}"
116    syslogd_start
117
118    syslogd_log -p user.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
119        "pre-reload"
120    atf_check -s exit:0 -o match:"reload: pre-reload" tail -n 1 "${logfile}"
121
122    # Override the old rule.
123    truncate -s 0 "${logfile}"
124    printf "news.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
125    syslogd_reload
126
127    syslogd_log -p user.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
128        "post-reload user"
129    syslogd_log -p news.debug -t reload -h "${SYSLOGD_LOCAL_SOCKET}" \
130        "post-reload news"
131    atf_check -s exit:0 -o not-match:"reload: post-reload user" cat ${logfile}
132    atf_check -s exit:0 -o match:"reload: post-reload news" cat ${logfile}
133}
134reload_cleanup()
135{
136    syslogd_stop
137}
138
139atf_test_case "prog_filter" "cleanup"
140prog_filter_head()
141{
142    atf_set descr "Messages are only received from programs in the filter"
143}
144prog_filter_body()
145{
146    logfile="${PWD}/prog_filter.log"
147    printf "!prog1,prog2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
148    syslogd_start
149
150    for i in 1 2 3; do
151        syslogd_log -p user.debug -t "prog${i}" -h "${SYSLOGD_LOCAL_SOCKET}" \
152            "hello this is prog${i}"
153    done
154    atf_check -s exit:0 -o match:"prog1: hello this is prog1" cat "${logfile}"
155    atf_check -s exit:0 -o match:"prog2: hello this is prog2" cat "${logfile}"
156    atf_check -s exit:0 -o not-match:"prog3: hello this is prog3" cat "${logfile}"
157
158    # Override the old rule.
159    truncate -s 0 ${logfile}
160    printf "!-prog1,prog2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
161    syslogd_reload
162
163    for i in 1 2 3; do
164        syslogd_log -p user.debug -t "prog${i}" -h "${SYSLOGD_LOCAL_SOCKET}" \
165            "hello this is prog${i}"
166    done
167    atf_check -s exit:0 -o not-match:"prog1: hello this is prog1" cat "${logfile}"
168    atf_check -s exit:0 -o not-match:"prog2: hello this is prog2" cat "${logfile}"
169    atf_check -s exit:0 -o match:"prog3: hello this is prog3" cat "${logfile}"
170}
171prog_filter_cleanup()
172{
173    syslogd_stop
174}
175
176atf_test_case "host_filter" "cleanup"
177host_filter_head()
178{
179    atf_set descr "Messages are only received from hostnames in the filter"
180}
181host_filter_body()
182{
183    logfile="${PWD}/host_filter.log"
184    printf "+host1,host2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
185    syslogd_start
186
187    for i in 1 2 3; do
188        syslogd_log -p user.debug -t "host${i}" -H "host${i}" \
189            -h "${SYSLOGD_LOCAL_SOCKET}" "hello this is host${i}"
190    done
191    atf_check -s exit:0 -o match:"host1: hello this is host1" cat "${logfile}"
192    atf_check -s exit:0 -o match:"host2: hello this is host2" cat "${logfile}"
193    atf_check -s exit:0 -o not-match:"host3: hello this is host3" cat "${logfile}"
194
195    # Override the old rule.
196    truncate -s 0 ${logfile}
197    printf "\-host1,host2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
198    syslogd_reload
199
200    for i in 1 2 3; do
201        syslogd_log -p user.debug -t "host${i}" -H "host${i}" \
202        -h "${SYSLOGD_LOCAL_SOCKET}" "hello this is host${i}"
203    done
204    atf_check -s exit:0 -o not-match:"host1: hello this is host1" cat "${logfile}"
205    atf_check -s exit:0 -o not-match:"host2: hello this is host2" cat "${logfile}"
206    atf_check -s exit:0 -o match:"host3: hello this is host3" cat "${logfile}"
207}
208host_filter_cleanup()
209{
210    syslogd_stop
211}
212
213atf_test_case "prop_filter" "cleanup"
214prop_filter_head()
215{
216    atf_set descr "Messages are received based on conditions in the propery based filter"
217}
218prop_filter_body()
219{
220    logfile="${PWD}/prop_filter.log"
221    printf ":msg,contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
222        > "${SYSLOGD_CONFIG}"
223    syslogd_start
224
225    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
226    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
227    atf_check -s exit:0 -o match:"prop1: FreeBSD" cat "${logfile}"
228    atf_check -s exit:0 -o not-match:"prop2: freebsd" cat "${logfile}"
229
230    truncate -s 0 ${logfile}
231    printf ":msg,!contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
232        > "${SYSLOGD_CONFIG}"
233    syslogd_reload
234
235    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
236    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
237    atf_check -s exit:0 -o not-match:"prop1: FreeBSD" cat "${logfile}"
238    atf_check -s exit:0 -o match:"prop2: freebsd" cat "${logfile}"
239
240    truncate -s 0 ${logfile}
241    printf ":msg,icase_contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
242        > "${SYSLOGD_CONFIG}"
243    syslogd_reload
244
245    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
246    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
247    atf_check -s exit:0 -o match:"prop1: FreeBSD" cat "${logfile}"
248    atf_check -s exit:0 -o match:"prop2: freebsd" cat "${logfile}"
249
250    truncate -s 0 ${logfile}
251    printf ":msg,!icase_contains,\"FreeBSD\"\nuser.debug\t${logfile}\n" \
252        > "${SYSLOGD_CONFIG}"
253    syslogd_reload
254
255    syslogd_log -p user.debug -t "prop1" -h "${SYSLOGD_LOCAL_SOCKET}" "FreeBSD"
256    syslogd_log -p user.debug -t "prop2" -h "${SYSLOGD_LOCAL_SOCKET}" "freebsd"
257    syslogd_log -p user.debug -t "prop3" -h "${SYSLOGD_LOCAL_SOCKET}" "Solaris"
258    atf_check -s exit:0 -o not-match:"prop1: FreeBSD" cat "${logfile}"
259    atf_check -s exit:0 -o not-match:"prop2: freebsd" cat "${logfile}"
260    atf_check -s exit:0 -o match:"prop3: Solaris" cat "${logfile}"
261}
262prop_filter_cleanup()
263{
264    syslogd_stop
265}
266
267atf_test_case "pipe_action" "cleanup"
268pipe_action_head()
269{
270    atf_set descr "The pipe action evaluates provided command in sh(1)"
271}
272pipe_action_body()
273{
274    logfile="${PWD}/pipe_action.log"
275    printf "\"While I'm digging in the tunnel, the elves will often come to me \
276        with solutions to my problem.\"\n-Saymore Crey" > ${logfile}
277
278    printf "!pipe\nuser.debug\t| sed -i '' -e 's/Saymore Crey/Seymour Cray/g' \
279        ${logfile}\n" > "${SYSLOGD_CONFIG}"
280    syslogd_start
281
282    syslogd_log -p user.debug -t "pipe" -h "${SYSLOGD_LOCAL_SOCKET}" \
283        "fix spelling error"
284    atf_check -s exit:0 -o match:"Seymour Cray" cat "${logfile}"
285}
286pipe_action_cleanup()
287{
288    syslogd_stop
289}
290
291atf_init_test_cases()
292{
293    atf_add_test_case "basic"
294    atf_add_test_case "reload"
295    atf_add_test_case "prog_filter"
296    atf_add_test_case "host_filter"
297    atf_add_test_case "prop_filter"
298    atf_add_test_case "pipe_action"
299}
300