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