├── lib ├── .gitignore ├── strlcpy.c ├── utimensat.c ├── strlcat.c └── pidfile.c ├── test ├── .gitignore ├── local.sh ├── mark.sh ├── regression.sh ├── unicode.sh ├── opts.sh ├── remote.sh ├── logger.sh ├── sighup.sh ├── multicast.sh ├── memleak.sh ├── mcast-fwd.sh ├── raw.sh ├── Makefile.am ├── notify.sh ├── hostname.sh ├── api.c ├── property.sh ├── secure.sh ├── rotate_all.sh ├── fwd.sh ├── listen.sh ├── api.sh ├── mcast-iface.sh ├── tag.sh ├── facility.sh ├── parens.sh └── lib.sh ├── autogen.sh ├── .github ├── FUNDING.yml ├── SECURITY.md ├── workflows │ ├── release.yml │ ├── coverity.yml │ └── build.yml ├── CODE-OF-CONDUCT.md └── CONTRIBUTING.md ├── src ├── .gitignore ├── libsyslog.pc.in ├── timer.h ├── socket.h ├── Makefile.am ├── compat.h ├── timer.c ├── socket.c ├── syslog.h ├── syslogd.h ├── logger.c └── queue.h ├── example ├── Makefile.am ├── example.mk ├── example.c ├── LICENSE └── README.md ├── .gitignore ├── syslogd.service.in ├── LICENSE ├── man ├── Makefile.am ├── logger.1 └── syslogp.3 ├── syslog.conf ├── Makefile.am ├── configure.ac └── README.md /lib/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.lo 3 | .dirstamp 4 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | *.trs 4 | api 5 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [troglobit] 4 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.lo 3 | .libs/* 4 | logger 5 | syslogd 6 | libcompat.la 7 | libsyslog.la 8 | libsyslog.pc 9 | -------------------------------------------------------------------------------- /example/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = README.md example.c example.mk 2 | 3 | pkgexampledir = $(docdir)/example 4 | pkgexample_DATA = $(EXTRA_DIST) 5 | -------------------------------------------------------------------------------- /test/local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | run_step "Set up local syslog daemon" setup -m0 5 | run_step "Verify basic logging" log_and_find "foobar" 6 | run_step "Verify alternate socket" log_and_find "$ALTSOCK" "xyzzy" 7 | -------------------------------------------------------------------------------- /test/mark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test '-- MARK --' in log, runs in full secure mode. 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | run_step "Enable -- MARK -- every minute" setup -m1 -ss 6 | run_step "Verify -- MARK -- in log file" tenacious 120 grep "MARK" "${LOG}" 7 | -------------------------------------------------------------------------------- /src/libsyslog.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE@ 7 | Description: RFC5424 compliant syslogp() API and syslog() replacement from sysklogd 8 | Version: @VERSION@ 9 | Requires: 10 | Libs: -L${libdir} -lsyslog 11 | Cflags: -I${includedir} 12 | 13 | -------------------------------------------------------------------------------- /example/example.mk: -------------------------------------------------------------------------------- 1 | # Simple Makefile for syslogp() example application 2 | # This is free and unencumbered software released into the public domain. 3 | 4 | EXEC := example 5 | OBJS := example.o 6 | CFLAGS := `pkg-config --cflags libsyslog` 7 | LDLIBS := `pkg-config --libs --static libsyslog` 8 | 9 | all: $(EXEC) 10 | 11 | $(EXEC): $(OBJS) 12 | -------------------------------------------------------------------------------- /test/regression.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify actual (minor) regressions that we've had in the project 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | verify_tagpid() 6 | { 7 | logger -t foo -I 1234 "You won't get it up the steps." 8 | grep -H 'foo\[1234\]' "$LOG" 9 | } 10 | 11 | run_step "Set up local syslog daemon" setup -m0 12 | run_step "Verify tag[PID] regression" verify_tagpid 13 | -------------------------------------------------------------------------------- /test/unicode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Currently only same as local.sh but with unicode messages 3 | # From https://github.com/troglobit/sysklogd/issues/49 4 | . "${srcdir:-.}/lib.sh" 5 | 6 | run_step "Set up unicode capable syslogd" setup -8 -m0 7 | run_step "Verify logger" log_and_find "öäüÖÄÜ߀¢§" 8 | run_step "Verify logger w/ alt. socket" log_and_find "$ALTSOCK" "…‘’•" 9 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | sysklogd is a small project, as such we have no possibility to support older versions. 6 | The only supported version is the latest released on GitHub: 7 | 8 | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | Contact the project's main author and owner to report and discuss vulnerabilities. 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .config 3 | .deps 4 | .unpacked 5 | m4/ 6 | ID 7 | GTAGS 8 | GPATH 9 | GRTAGS 10 | TAGS 11 | Makefile 12 | Makefile.in 13 | aclocal.m4 14 | autom4te.cache 15 | compile 16 | compile_commands.json 17 | config.h* 18 | config.guess 19 | config.log 20 | config.status 21 | config.sub 22 | configure 23 | depcomp 24 | install-sh 25 | libtool 26 | ltmain.sh 27 | missing 28 | stamp-h1 29 | syslogd.service 30 | test-driver 31 | -------------------------------------------------------------------------------- /syslogd.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=System Logging Service 3 | Documentation=man:syslogd 4 | Documentation=man:syslog.conf 5 | Requires=syslog.socket 6 | 7 | [Service] 8 | EnvironmentFile=-@SYSCONFDIR@/default/syslogd 9 | ExecStart=@SBINDIR@/syslogd -F $SYSLOGD_OPTS 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | StandardOutput=null 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | Alias=syslog.service 17 | -------------------------------------------------------------------------------- /example/example.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: Unlicense 3 | * 4 | * This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | /* Example of how to use NetBSD syslogp() API with libsyslog from sysklogd */ 8 | 9 | #include 10 | #include 11 | 12 | int main(void) 13 | { 14 | openlog("example", LOG_PID, LOG_USER); 15 | syslogp(LOG_NOTICE, "MSGID", NULL, "Kilroy was here."); 16 | closelog(); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /test/opts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | setup_debug() 5 | { 6 | DEBUG=true 7 | 8 | cat <<-EOF > "${CONF}" 9 | # Match all log messages, store in RC5424 format and rotate every 10 MiB 10 | *.* -${LOG} ;rotate=10M:5,RFC5424 11 | EOF 12 | setup -m0 >"${LOG2}" 13 | } 14 | 15 | verify_log_parse() 16 | { 17 | grep ';RFC5424,rotate=10000000:5' "${LOG2}" 18 | } 19 | 20 | run_step "Set up syslogd w/ log rotation and RFC5424" setup_debug 21 | run_step "Verify correct parsing of log options" verify_log_parse 22 | -------------------------------------------------------------------------------- /test/remote.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify that the sending to a remote IP:PORT works. 3 | # shellcheck disable=SC2317 4 | . "${srcdir:-.}/lib.sh" 5 | 6 | setup_remote() 7 | { 8 | cat <<-EOF > "${CONF}" 9 | *.* @127.0.0.2:${PORT2} ;RFC3164 10 | EOF 11 | setup -m0 12 | } 13 | 14 | verify_remote() 15 | { 16 | MSG="kilroy" 17 | 18 | cap_start "$PORT2" 19 | logger "${MSG}" 20 | cap_stop 21 | 22 | cap_find_port "$PORT2" "${MSG}" 23 | } 24 | 25 | run_step "Setup remote syslog, RFC3164" setup_remote 26 | run_step "Verify sending to remote" verify_remote 27 | -------------------------------------------------------------------------------- /test/logger.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify logger capabilities, for now just remote logger 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | setup_loopback() 6 | { 7 | ip link set lo up 8 | } 9 | 10 | verify_logger() 11 | { 12 | cap_start 13 | logger -b -H "$(basename "$0")" -h 127.0.0.3 -I $$ -t test1 "Kilroy was here" 14 | cap_stop 15 | 16 | # Check for the composed BSD procname{PID] syntax 17 | STR="test1\[$$\]" 18 | cap_find "$STR" 19 | } 20 | 21 | run_step "Set up loopback interface" setup_loopback 22 | run_step "Verify remote syslog with stand-alone logger" verify_logger 23 | -------------------------------------------------------------------------------- /test/sighup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify syslogd can survive a few SIGHUP's 3 | # shellcheck disable=SC1090 4 | . "${srcdir:-.}/lib.sh" 5 | 6 | verify_logging() 7 | { 8 | MSG="$*" 9 | logger "${MSG}" 10 | grep "${MSG}" "${LOG}" 11 | } 12 | 13 | rattle_cage() 14 | { 15 | for _ in $(seq "$1"); do 16 | dprint "Shaky shaky ..." 17 | reload 18 | done 19 | } 20 | 21 | run_step "Set up local syslog daemon" setup -m0 22 | run_step "Verify before shakeup" verify_logging "Diving in the Sky" 23 | run_step "Shake it up mister ..." rattle_cage 5 24 | run_step "Verify after shakeup" verify_logging "Summerdream" 25 | -------------------------------------------------------------------------------- /test/multicast.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | GROUP=225.1.2.3 5 | MSG="kilroy was here" 6 | 7 | setup_listen() 8 | { 9 | ip link set lo up state up 10 | ip route add default via 127.0.0.1 11 | 12 | cat <<-EOF > "${CONF}" 13 | *.* $LOG 14 | listen $1:$PORT2 15 | EOF 16 | setup -m0 -nH 17 | } 18 | 19 | verify_snd() 20 | { 21 | cap_start "$PORT2" 22 | logger -h "$GROUP" -H remote -P "$PORT2" "${MSG}" 23 | cap_stop 24 | cap_find_port "$PORT2" "${MSG}" 25 | } 26 | 27 | verify_rcv() 28 | { 29 | grep -H "${MSG}" "$LOG" 30 | } 31 | 32 | run_step "Set up syslogd that listen to $GROUP" setup_listen "$GROUP" 33 | run_step "Verify sending to group $GROUP" verify_snd "$GROUP" 34 | run_step "Verify reception from group $GROUP" verify_rcv "$GROUP" 35 | -------------------------------------------------------------------------------- /test/memleak.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start, SIGHUP, and log a run of syslogd under Valgrind 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | setup_valgrind() 6 | { 7 | VALGRIND="valgrind --leak-check=full --show-leak-kinds=all" 8 | DEBUG=true 9 | 10 | # Only needed for verifying correct RFC3164 parsing 11 | cat <<-EOF >"${CONFD}/99-wall.conf" 12 | *.=emerg * 13 | EOF 14 | setup -m0 >"${LOG2}" 2>&1 15 | } 16 | 17 | inject_logger() 18 | { 19 | logger "Dummy message" 20 | sleep 1 # Wait for any OS delays 21 | } 22 | 23 | inject_reload() 24 | { 25 | reload 26 | sleep 1 # Wait for any OS delays 27 | } 28 | 29 | verify_leaks() 30 | { 31 | kill_pids 32 | sleep 2 # Wait for syslogd to shut down 33 | grep "All heap blocks were freed -- no leaks are possible" "$LOG2" 34 | } 35 | 36 | run_step "Start basic syslogd under valgrind" setup_valgrind 37 | run_step "Inject stimuli: logger" inject_logger 38 | run_step "Inject stimuli: reload" inject_reload 39 | run_step "Verify no leaks in valgrind output" verify_leaks 40 | -------------------------------------------------------------------------------- /test/mcast-fwd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | MSG="Copying foobar to xyzzy" 5 | GROUP=225.1.2.3 6 | TTL=10 7 | 8 | setup_sender() 9 | { 10 | cat <<-EOF >"${CONF}" 11 | *.* @$GROUP ;RFC5424,ttl=$TTL 12 | EOF 13 | setup -m0 14 | 15 | ip route add default via 127.0.0.1 16 | } 17 | 18 | setup_receiver() 19 | { 20 | cat <<-EOF > "${CONF2}" 21 | uucp.info $LOG2 22 | listen $GROUP 23 | EOF 24 | setup2 -m0 -nH 25 | } 26 | 27 | verify_mcast_fwd() 28 | { 29 | cap_start 30 | logger -p uucp.info "$MSG" 31 | cap_stop 32 | 33 | cap_find "$MSG" 34 | } 35 | 36 | verify_mcast_ttl() 37 | { 38 | ttl=$(cap_find "$MSG" |awk '{print $2}') 39 | test "$ttl" -eq "$TTL" 40 | } 41 | 42 | verify_mcast_rcv() 43 | { 44 | grep -H "$MSG" "$LOG2" 45 | } 46 | 47 | run_step "Set up sender syslogd" setup_sender 48 | run_step "Set up receiver syslogd" setup_receiver 49 | run_step "Verify multicast forward" verify_mcast_fwd 50 | run_step "Verify multicast TTL=$TTL" verify_mcast_ttl 51 | run_step "Verify multicast received" verify_mcast_rcv 52 | -------------------------------------------------------------------------------- /test/raw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | ID47="$DIR/id47.log" 5 | DATA="$DIR/data.log" 6 | 7 | setup_rfc5424() 8 | { 9 | cat <<-EOF > "${CONF}" 10 | # Match all log messages, store in RC5424 11 | *.* -${LOG} ;RFC5424 12 | # Match ID47 13 | :msgid, equal, "ID47" 14 | *.* ${ID47} ;RFC5424 15 | :data, regex, ".*eventSource=\"Application\".*" 16 | *.* ${DATA} ;RFC5424 17 | EOF 18 | setup -m0 >"${LOG2}" 19 | } 20 | 21 | verify_netcat() 22 | { 23 | MSG='<165>1 2003-10-11T22:14:15.003Z mymachine.example.com su 12345 ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"][id@2 test="tast"] BOM"su root" failed for lonvick on /dev/pts/8" ' 24 | 25 | echo "$MSG" | nc -w1 -Uu "${SOCK}" 26 | grep -H "mymachine" "${ID47}" 27 | } 28 | 29 | check_log() 30 | { 31 | log="$1"; shift 32 | msg="$*" 33 | 34 | grep -H "$msg" "$log" 35 | } 36 | 37 | run_step "Set up syslogd w/ RFC5424" setup_rfc5424 38 | run_step "Verify parsing of netcat message" verify_netcat 39 | run_step "Verify regexp on structured data" check_log "$DATA" "mymachine" 40 | -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release General 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | name: Build and upload release tarball 11 | if: startsWith(github.ref, 'refs/tags/') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Installing dependencies ... 16 | run: | 17 | sudo apt-get -y update 18 | sudo apt-get -y install tree tshark valgrind 19 | - name: Creating Makefiles ... 20 | run: | 21 | ./autogen.sh 22 | ./configure --prefix=/tmp --with-systemd=/tmp/lib/systemd/system 23 | - name: Enable unprivileged userns (unshare) 24 | run: | 25 | sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 26 | - name: Build release ... 27 | run: | 28 | make release 29 | mkdir -p artifacts/ 30 | mv ../*.tar.* artifacts/ 31 | - name: Extract ChangeLog entry ... 32 | run: | 33 | awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ 34 | |head -n -1 > release.md 35 | cat release.md 36 | - uses: ncipollo/release-action@v1 37 | with: 38 | name: sysklogd ${{ github.ref_name }} 39 | bodyFile: "release.md" 40 | artifacts: "artifacts/*" 41 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = lib.sh opts.sh 2 | EXTRA_DIST += api.sh local.sh unicode.sh remote.sh fwd.sh mark.sh \ 3 | memleak.sh facility.sh notify.sh rotate_all.sh secure.sh \ 4 | logger.sh listen.sh sighup.sh tag.sh hostname.sh \ 5 | property.sh raw.sh regression.sh multicast.sh \ 6 | mcast-fwd.sh mcast-iface.sh parens.sh 7 | CLEANFILES = *~ *.trs *.log 8 | TEST_EXTENSIONS = .sh 9 | TESTS_ENVIRONMENT= unshare -mrun --map-auto 10 | 11 | check_PROGRAMS = api 12 | api_SOURCES = api.c 13 | api_CFLAGS = -I$(srcdir)/../src 14 | api_LDFLAGS = -static 15 | api_LDADD = ../src/libsyslog.la 16 | 17 | TESTS = opts.sh 18 | TESTS += local.sh 19 | TESTS += logger.sh 20 | TESTS += unicode.sh 21 | TESTS += remote.sh 22 | TESTS += api.sh 23 | TESTS += facility.sh 24 | TESTS += fwd.sh 25 | TESTS += listen.sh 26 | TESTS += memleak.sh 27 | TESTS += mark.sh 28 | TESTS += notify.sh 29 | TESTS += rotate_all.sh 30 | TESTS += secure.sh 31 | TESTS += sighup.sh 32 | TESTS += tag.sh 33 | TESTS += parens.sh 34 | TESTS += hostname.sh 35 | TESTS += property.sh 36 | TESTS += raw.sh 37 | TESTS += regression.sh 38 | TESTS += multicast.sh 39 | TESTS += mcast-fwd.sh 40 | TESTS += mcast-iface.sh 41 | 42 | programs: $(check_PROGRAMS) 43 | -------------------------------------------------------------------------------- /test/notify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | NOT1=${DIR}/${NM}-1.sh 5 | NOT2=${DIR}/${NM}-2.sh 6 | NOT1STAMP=${DIR}/${NM}-1.stamp 7 | NOT2STAMP=${DIR}/${NM}-2.stamp 8 | 9 | # shellcheck disable=SC2059 10 | setup_notifiers() 11 | { 12 | printf "#!/bin/sh -\necho script 1: \$* >${NOT1STAMP}\n" >"${NOT1}" 13 | printf "#!/bin/sh -\necho script 2: \$* >${NOT2STAMP}\n" >"${NOT2}" 14 | chmod 0755 "${NOT1}" "${NOT2}" 15 | } 16 | 17 | # Match all log messages, store in RC5424 format and rotate every 1 KiB 18 | setup_syslogd() 19 | { 20 | cat <<-EOF > "${CONFD}/notifier.conf" 21 | notify ${NOT1} 22 | *.* -${LOG} ;rotate=1k:2,RFC5424 23 | notify ${NOT2} 24 | EOF 25 | setup -m0 26 | } 27 | 28 | trigger_rotation() 29 | { 30 | MSG=01234567890123456789012345678901234567890123456789 31 | MSG=$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG 32 | 33 | logger "${MSG}" 34 | logger "1${MSG}" 35 | logger "2${MSG}" 36 | 37 | sleep 1 # Wait for any OS delays 38 | } 39 | 40 | verify_rotation() 41 | { 42 | ls -l "${LOG}.0" 43 | } 44 | 45 | verify_notifiers() 46 | { 47 | grep "script 1: $LOG" "${NOT1STAMP}" \ 48 | && grep "script 2: $LOG" "${NOT2STAMP}" 49 | } 50 | 51 | run_step "Create notifier scripts" setup_notifiers 52 | run_step "Set up syslogd with notifiers" setup_syslogd 53 | run_step "Trigger log rotation" trigger_rotation 54 | run_step "Verify log rotation" verify_rotation 55 | run_step "Verify notifiers have run" verify_notifiers 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 1983, 1988, 1993 The Regents of the University of California. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the University nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /test/hostname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify hstname based filtering. 3 | #set -x 4 | . "${srcdir:-.}/lib.sh" 5 | 6 | HST1="finlandia" 7 | HST2="sibelius" 8 | MSG1="Be Still, My Soul" 9 | MSG2="Oi Suomi, katso" 10 | TEXT="Ack Värmeland, du sköna" 11 | 12 | LOGDIR="$DIR/log" 13 | SYSLOG="${LOGDIR}/syslog" 14 | HST1LG="${LOGDIR}/$HST1.log" 15 | HST2LG="${LOGDIR}/$HST2.log" 16 | 17 | setup_syslogd() 18 | { 19 | mkdir -p "$LOGDIR" 20 | cat <<-EOF >"${CONF}" 21 | #-$HST1,$HST2 22 | *.* -$SYSLOG 23 | #+$HST1 24 | *.* $HST1LG 25 | #+$HST2 26 | *.* $HST2LG 27 | EOF 28 | setup -m0 -8 29 | } 30 | 31 | verify_hst() 32 | { 33 | hst="$1"; shift 34 | log="$1"; shift 35 | msg="$*" 36 | 37 | if [ "$hst" = "@" ]; then 38 | pri=user.panic 39 | usr="kilroy" 40 | else 41 | pri=daemon.notice 42 | usr="jean" 43 | fi 44 | 45 | logger -H "$hst" -t "$usr" -p $pri "$msg" 46 | grep -H "$msg" "$log" 47 | } 48 | 49 | verify_log() 50 | { 51 | log="$1"; shift 52 | msg="$*" 53 | 54 | grep -H "$msg" "$log" 55 | } 56 | 57 | verify_not() 58 | { 59 | verify_log "$@" || return 0 60 | } 61 | 62 | run_step "Set up property based filtering syslogd" setup_syslogd 63 | run_step "Verify basic tag based filtering (1)" verify_hst "$HST1" "$HST1LG" "$MSG1" 64 | run_step "Verify basic tag based filtering (2)" verify_hst "$HST2" "$HST2LG" "$MSG2" 65 | run_step "Verify not in syslog" verify_not "$SYSLOG" "$MSG1" 66 | 67 | run_step "Verify unfiltered host logging" verify_hst "@" "$SYSLOG" "$TEXT" 68 | run_step "Verify unfiltered message not filtered" verify_not "$HST1LG" "$TEXT" 69 | -------------------------------------------------------------------------------- /test/api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Without any options the standard syslog(3) API is used with severity 3 | * LOG_NOTICE without calling openlog(), so the default LOG_USER facility 4 | * is used. 5 | * 6 | * Options: 7 | * -i ID Log using the given identity to LOG_CONSOLE, calls openlog() 8 | * -l Set logmask to LOG_NOTICE and log with LOG_INFO 9 | * -p Use modern syslogp() API and log to LOG_FTP, use with -i ID 10 | * 11 | * See the -i option for made-easy 'grep' check of the log. 12 | * 13 | * When the -l option is used, setlogmask() is set to LOG_NOTICE and the 14 | * LOG_CONSOLE facility is used for logging. The latter is a sysklogd 15 | * (BSD) specific facility. 16 | * 17 | * The -p option triggers the use of the modern syslogp() API and sets 18 | * the facility to LOG_FTP. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include "syslog.h" 24 | 25 | int main(int argc, char *argv[]) 26 | { 27 | char *ident = NULL; 28 | char *msg = getenv("MSG"); 29 | char c; 30 | int severity = LOG_NOTICE; 31 | int facility = LOG_CONSOLE; 32 | int v1 = 0; 33 | 34 | if (!msg) 35 | return 1; 36 | 37 | while ((c = getopt(argc, argv, "i:lp")) != EOF) { 38 | switch (c) { 39 | case 'i': 40 | ident = optarg; 41 | break; 42 | 43 | case 'l': 44 | setlogmask(LOG_UPTO(severity)); 45 | severity = LOG_INFO; 46 | break; 47 | 48 | case 'p': 49 | v1 = 1; 50 | facility = LOG_FTP; 51 | break; 52 | } 53 | } 54 | 55 | if (ident) 56 | openlog(ident, LOG_NOWAIT, facility); 57 | 58 | if (v1) 59 | syslogp(severity, "MSGID", NULL, "%s", msg); 60 | else 61 | syslog(severity, "%s", msg); 62 | 63 | if (ident) 64 | closelog(); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /test/property.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify property based filtering 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | MSG1="Failed password for root from 200.72.41.31 port 40992 ssh2" 6 | MSG2="error: PAM: authentication error for illegal user amanda from 60.28.42.205" 7 | MSG3="pam_unix\(cron:session\): session opened for user root\(uid=0\) by root\(uid=0\)" 8 | 9 | LOGDIR="$DIR/log" 10 | SYSLOG="${LOGDIR}/syslog" 11 | MSGLOG="${LOGDIR}/messages" 12 | ERRLOG="${LOGDIR}/auth-err.log" 13 | BANLOG="${LOGDIR}/ban.log" 14 | 15 | setup_syslogd() 16 | { 17 | mkdir -p "$LOGDIR" 18 | cat <<-EOF >"${CONF}" 19 | *.* -$SYSLOG 20 | :msg, !icase_regex, ".*session opened for user.*" 21 | *.notice -$MSGLOG 22 | :msg, icase_contains, "ERROR" 23 | *.* $ERRLOG 24 | :msg, icase_regex, "failed password for .* from .* port .* ssh[123]" 25 | *.* $BANLOG 26 | EOF 27 | setup -m0 28 | } 29 | 30 | verify_log() 31 | { 32 | tag="$1"; shift 33 | log="$1"; shift 34 | msg="$*" 35 | 36 | logger -i -t "$tag" "$msg" 37 | grep "$msg" "$log" 38 | } 39 | 40 | check_log() 41 | { 42 | log="$1"; shift 43 | msg="$*" 44 | 45 | grep "$msg" "$log" 46 | } 47 | 48 | check_not() 49 | { 50 | check_log "$@" || return 0 51 | } 52 | 53 | run_step "Set up property based filtering syslogd" setup_syslogd 54 | 55 | run_step "Verify generic msg got to syslog" verify_log "CRON" "$SYSLOG" "$MSG3" 56 | run_step "Verify generic msg not in msessages" check_not "$MSGLOG" "$MSG3" 57 | 58 | run_step "Verify auth. error go to auth-err.log" verify_log "sshd" "$ERRLOG" "$MSG2" 59 | run_step "Verify auth. error go to syslog as well" check_log "$SYSLOG" "$MSG2" 60 | 61 | run_step "Verify regex matching to ban.log" verify_log "sshd" "$BANLOG" "$MSG1" 62 | -------------------------------------------------------------------------------- /lib/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | size_t 28 | __strlcpy(char *dst, const char *src, size_t dsize) 29 | { 30 | const char *osrc = src; 31 | size_t nleft = dsize; 32 | 33 | /* Copy as many bytes as will fit. */ 34 | if (nleft != 0) { 35 | while (--nleft != 0) { 36 | if ((*dst++ = *src++) == '\0') 37 | break; 38 | } 39 | } 40 | 41 | /* Not enough room in dst, add NUL and traverse rest of src. */ 42 | if (nleft == 0) { 43 | if (dsize != 0) 44 | *dst = '\0'; /* NUL-terminate dst */ 45 | while (*src++) 46 | ; 47 | } 48 | 49 | return(src - osrc - 1); /* count does not include NUL */ 50 | } 51 | -------------------------------------------------------------------------------- /lib/utimensat.c: -------------------------------------------------------------------------------- 1 | /* Replacement in case utimensat(2) is missing 2 | * 3 | * Copyright (C) 2017-2023 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #ifdef HAVE_FCNTL_H 20 | #include 21 | #endif 22 | #include /* lutimes(), utimes(), utimensat() */ 23 | 24 | #ifndef TIMESPEC_TO_TIMEVAL 25 | #define TIMESPEC_TO_TIMEVAL(tv, ts) { \ 26 | (tv)->tv_sec = (ts)->tv_sec; \ 27 | (tv)->tv_usec = (ts)->tv_nsec / 1000; \ 28 | } 29 | #endif 30 | 31 | int 32 | __utimensat(int dirfd, const char *pathname, const struct timespec ts[2], int flags) 33 | { 34 | int ret = -1; 35 | struct timeval tv[2]; 36 | 37 | (void)flags; 38 | if (dirfd != 0) { 39 | errno = ENOTSUP; 40 | return -1; 41 | } 42 | 43 | TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); 44 | TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); 45 | 46 | #ifdef AT_SYMLINK_NOFOLLOW 47 | if ((flags & AT_SYMLINK_NOFOLLOW) == AT_SYMLINK_NOFOLLOW) 48 | ret = lutimes(pathname, tv); 49 | else 50 | #endif 51 | ret = utimes(pathname, tv); 52 | 53 | return ret; 54 | } 55 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2023 Joachim Wiberg 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the University nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | dist_man1_MANS = 29 | dist_man3_MANS = syslogp.3 30 | dist_man5_MANS = syslog.conf.5 31 | dist_man8_MANS = syslogd.8 32 | 33 | if ENABLE_LOGGER 34 | dist_man1_MANS += logger.1 35 | endif 36 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Stand-alone Example syslogp() Application 2 | ========================================= 3 | 4 | This is a *very* simple stand-alone example application. The purpose is 5 | to show how to use the sysklogd 2.x API, e.g. `syslogp()`, to use "new" 6 | RFC5424 features like MsgID. 7 | 8 | Included in this directory are two files: 9 | 10 | - `example.c`: actual C code example 11 | - `example.mk`: plain Makefile for building `example` 12 | 13 | Provided the two files are in the same (writable) directory, you can 14 | build the application like this: 15 | 16 | make -f example.mk 17 | 18 | 19 | GNU Autotools 20 | ------------- 21 | 22 | If you want to use GNU autoconf & automake instead. The following is 23 | recommended in `configure.ac` and `Makefile.am` to build your 24 | application. 25 | 26 | ```sh 27 | # configure.ac (snippet) 28 | 29 | # Check for pkg-config tool, required for next step 30 | PKG_PROG_PKG_CONFIG 31 | 32 | # Check for required libraries 33 | PKG_CHECK_MODULES([syslog], [libsyslog >= 2.0]) 34 | ``` 35 | 36 | and 37 | 38 | ```Makefile 39 | # Makefile.am (snippet) 40 | 41 | bin_PROGRAMS = example 42 | 43 | example_SOURCES = example.c 44 | example_CFLAGS = $(syslog_CFLAGS) 45 | example_LDADD = $(syslog_LIBS) 46 | ``` 47 | 48 | **NOTE:** Most free/open source software that uses `configure` default 49 | to install to `/usr/local`. However, some Linux distributions do no 50 | longer search that path for installed software, e.g. Fedora and Alpine 51 | Linux. To help your configure script find its dependencies you have 52 | to give the `pkg-config` a prefix path: 53 | 54 | PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure 55 | 56 | 57 | License 58 | ------- 59 | 60 | This example code, `example.c`, this README.md and the `example.mk` 61 | Makefile are free and unencumbered software released into the public 62 | domain. 63 | 64 | -------------------------------------------------------------------------------- /test/secure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify secure_mode changes at runtime w/o having to restart syslogd. 3 | # We want to ensure goint from most secure, to no security, and back, 4 | # works as intended. 5 | . "${srcdir:-.}/lib.sh" 6 | 7 | MSG="Kilroy was here" 8 | 9 | # 10 | # Helper functions 11 | # 12 | 13 | set_secure_mode() 14 | { 15 | cat <<-EOF > "${CONF}" 16 | *.* @127.0.0.2 17 | secure_mode $1 18 | EOF 19 | 20 | if is_running; then 21 | reload 22 | else 23 | setup -m0 24 | fi 25 | 26 | sleep 1 # Wait for any OS delays 27 | } 28 | 29 | do_port_check() 30 | { 31 | dprint "Checking for port $PORT|$PORT2 ..." 32 | netstat -atnup 2>/dev/null | grep "$PORT\|$PORT2" 33 | } 34 | 35 | check_no_port_open() 36 | { 37 | do_port_check || return 0 38 | } 39 | 40 | check_port_open() 41 | { 42 | do_port_check 43 | } 44 | 45 | check_remote_logging() 46 | { 47 | # shellcheck disable=SC2119 48 | cap_start 49 | logger "$MSG" 50 | cap_stop 51 | 52 | cap_find "$MSG" 53 | } 54 | 55 | # 56 | # Test steps 57 | # 58 | 59 | verify_secure_daemon() 60 | { 61 | set_secure_mode 2 62 | check_no_port_open 63 | } 64 | 65 | verify_safe_daemon() 66 | { 67 | set_secure_mode 1 68 | check_no_port_open 69 | } 70 | 71 | verify_default_daemon() 72 | { 73 | set_secure_mode 0 74 | check_port_open 75 | } 76 | 77 | # 78 | # Run test steps 79 | # 80 | 81 | run_step "Verify secure mode 2 - no remote no ports" verify_secure_daemon 82 | 83 | run_step "Verify secure mode 1 - remote, no ports" verify_safe_daemon 84 | run_step "Verify secure mode 1 remote logging" check_remote_logging 85 | 86 | run_step "Verify secure mode 0 - remote, open ports" verify_default_daemon 87 | run_step "Verify secure mode 0 remote logging" check_remote_logging 88 | 89 | run_step "Verify secure mode 1 - remote, no ports" verify_safe_daemon 90 | run_step "Verify secure mode 1 remote logging" check_remote_logging 91 | -------------------------------------------------------------------------------- /lib/strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 24 | * full size of dst, not space left). At most dsize-1 characters 25 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 26 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 27 | * If retval >= dsize, truncation occurred. 28 | */ 29 | size_t 30 | __strlcat(char *dst, const char *src, size_t dsize) 31 | { 32 | const char *odst = dst; 33 | const char *osrc = src; 34 | size_t n = dsize; 35 | size_t dlen; 36 | 37 | /* Find the end of dst and adjust bytes left but don't go past end. */ 38 | while (n-- != 0 && *dst != '\0') 39 | dst++; 40 | dlen = dst - odst; 41 | n = dsize - dlen; 42 | 43 | if (n-- == 0) 44 | return(dlen + strlen(src)); 45 | while (*src != '\0') { 46 | if (n != 0) { 47 | *dst++ = *src; 48 | n--; 49 | } 50 | src++; 51 | } 52 | *dst = '\0'; 53 | 54 | return(dlen + (src - osrc)); /* count does not include NUL */ 55 | } 56 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (C) 2017-2023 Joachim Wiberg 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef SYSKLOGD_TIMER_H_ 32 | #define SYSKLOGD_TIMER_H_ 33 | 34 | int timer_add (int period, void (*cb)(void *), void *arg); 35 | 36 | int timer_start (void); 37 | 38 | int timer_update (void); 39 | time_t timer_now (void); 40 | 41 | int timer_init (void); 42 | void timer_exit (void); 43 | 44 | #endif /* SYSKLOGD_TIMER_H_ */ 45 | -------------------------------------------------------------------------------- /.github/CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all 5 | people who contribute through reporting issues, posting feature 6 | requests, updating documentation, submitting pull requests or patches, 7 | and other activities. 8 | 9 | We are committed to making participation in this project a 10 | harassment-free experience for everyone, regardless of level of 11 | experience, gender, gender identity and expression, sexual orientation, 12 | disability, personal appearance, body size, race, ethnicity, age, 13 | religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | * The use of sexualized language or imagery 18 | * Personal attacks 19 | * Trolling or insulting/derogatory comments 20 | * Public or private harassment 21 | * Publishing other's private information, such as physical or electronic 22 | addresses, without explicit permission 23 | * Other unethical or unprofessional conduct. 24 | 25 | Project maintainers have the right and responsibility to remove, edit, 26 | or reject comments, commits, code, wiki edits, issues, and other 27 | contributions that are not aligned to this Code of Conduct. By adopting 28 | this Code of Conduct, project maintainers commit themselves to fairly 29 | and consistently applying these principles to every aspect of managing 30 | this project. Project maintainers who do not follow or enforce the Code 31 | of Conduct may be permanently removed from the project team. 32 | 33 | This code of conduct applies both within project spaces and in public 34 | spaces when an individual is representing the project or its community. 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may 37 | be reported by opening an issue or contacting one or more of the project 38 | maintainers. 39 | 40 | This Code of Conduct is adapted from the [Contributor Covenant][1], 41 | [version 1.2.0][2]. 42 | 43 | [1]: http://contributor-covenant.org 44 | [2]: http://contributor-covenant.org/version/1/2/0/ 45 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'dev' 7 | 8 | env: 9 | PROJECT_NAME: sysklogd 10 | CONTACT_EMAIL: troglobit@gmail.com 11 | COVERITY_NAME: troglobit-sysklogd 12 | COVERITY_PROJ: troglobit%2Fsysklogd 13 | 14 | jobs: 15 | coverity: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/cache@v4 20 | id: coverity-toolchain-cache 21 | with: 22 | path: cov-analysis-linux64 23 | key: ${{ runner.os }}-coverity 24 | - name: Download Coverity Scan 25 | if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true' 26 | env: 27 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 28 | run: | 29 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 30 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}" \ 31 | -O cov-analysis-linux64.tar.gz 32 | mkdir cov-analysis-linux64 33 | tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 34 | - name: Configure 35 | run: | 36 | ./autogen.sh 37 | ./configure --prefix= 38 | - name: Build 39 | run: | 40 | export PATH=`pwd`/cov-analysis-linux64/bin:$PATH 41 | cov-build --dir cov-int make 42 | - name: Submit results to Coverity Scan 43 | env: 44 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 45 | run: | 46 | tar czvf ${PROJECT_NAME}.tgz cov-int 47 | curl \ 48 | --form project=${COVERITY_NAME} \ 49 | --form token=$TOKEN \ 50 | --form email=${CONTACT_EMAIL} \ 51 | --form file=@${PROJECT_NAME}.tgz \ 52 | --form version=trunk \ 53 | --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ 54 | https://scan.coverity.com/builds?project=${COVERITY_PROJ} 55 | - name: Upload build.log 56 | uses: actions/upload-artifact@v4 57 | with: 58 | name: coverity-build.log 59 | path: cov-int/build-log.txt 60 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Bob the Builder 2 | 3 | # Run on all branches, including all pull requests, except the 'dev' 4 | # branch since that's where we run Coverity Scan (limited tokens/day) 5 | on: 6 | push: 7 | branches: 8 | - '**' 9 | - '!dev' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | # Verify we can build on latest Ubuntu with both gcc and clang 17 | name: ${{ matrix.compiler }} 18 | strategy: 19 | matrix: 20 | compiler: [gcc, clang] 21 | fail-fast: false 22 | runs-on: ubuntu-latest 23 | env: 24 | MAKEFLAGS: -j3 25 | CC: ${{ matrix.compiler }} 26 | steps: 27 | - name: Install dependencies 28 | run: | 29 | sudo apt-get -y update 30 | sudo apt-get -y install tree tshark valgrind 31 | - uses: actions/checkout@v4 32 | - name: Configure 33 | run: | 34 | set -x 35 | ./autogen.sh 36 | mkdir -p build/dir 37 | cd build/dir 38 | ../../configure --prefix=/tmp --with-systemd=/tmp/lib/systemd/system 39 | chmod -R a+w . 40 | - name: Build 41 | run: | 42 | cd build/dir 43 | make 44 | - name: Install Check 45 | run: | 46 | cd build/dir 47 | make V=1 install-strip 48 | tree /tmp || true 49 | ldd /tmp/sbin/syslogd 50 | size /tmp/sbin/syslogd 51 | /tmp/sbin/syslogd -? 52 | - name: Build Example 53 | run: | 54 | mkdir -p /tmp/example 55 | cp -a example/example.* /tmp/example/ 56 | pushd /tmp/example/ 57 | PKG_CONFIG_LIBDIR=/tmp/lib/pkgconfig make -f example.mk 58 | popd 59 | - name: Enable unprivileged userns (unshare) 60 | run: | 61 | sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 62 | - name: Run Tests 63 | run: | 64 | cd build/dir 65 | make check || (cat test/test-suite.log; false) 66 | - uses: actions/upload-artifact@v4 67 | with: 68 | name: ${{ matrix.compiler }}-test-logs 69 | path: build/dir/test/*.log 70 | -------------------------------------------------------------------------------- /test/rotate_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "${srcdir:-.}/lib.sh" 3 | 4 | MSG1="notrotall-1" 5 | MSG2="notrotall-2" 6 | MSG3="notrotall-3" 7 | 8 | NOT=${DIR}/${NM}-1.sh 9 | STP=${DIR}/${NM}-1.stamp 10 | 11 | check_deps() 12 | { 13 | [ -x ../src/logger ] || SKIP 'logger missing' 14 | command -v zgrep >/dev/null 2>&1 || SKIP 'zgrep(1) missing' 15 | } 16 | 17 | # shellcheck disable=SC2059 18 | setup_notifier() 19 | { 20 | printf "#!/bin/sh -\necho script 1: \$* >>${STP}\n" > "${NOT}" 21 | chmod 0755 "${NOT}" 22 | } 23 | 24 | setup_syslogd() 25 | { 26 | cat <<-EOF > "${CONFD}/rotate_all.conf" 27 | notify ${NOT} 28 | *.* -${LOG} ;rotate=10k:2,RFC5424 29 | *.* -${LOG}X ;rotate=10k:2,RFC5424 30 | EOF 31 | setup -m0 32 | } 33 | 34 | log_rotate() 35 | { 36 | rm -f "$STP" 37 | logger "$1" 38 | 39 | rotate 40 | } 41 | 42 | check_rotate() 43 | { 44 | NUM=$1; [ "$NUM" -gt 0 ] && NUM=$NUM.gz 45 | MSG=$2 46 | 47 | test -f "${LOG}.$NUM" && test -f "${LOG}X.$NUM" \ 48 | && zgrep -H "$MSG" "${LOG}.$NUM" \ 49 | && zgrep -H "$MSG" "${LOG}X.$NUM" 50 | } 51 | 52 | check_notifier() 53 | { 54 | test -f "$STP" && grep "script 1" "$STP" \ 55 | && grep -H "$LOG" "$STP" \ 56 | && grep -H "${LOG}X" "$STP" 57 | } 58 | 59 | run_step "Check dependencies (logger + zgrep)" check_deps 60 | run_step "Create notifier script" setup_notifier 61 | run_step "Set up syslogd with notifier" setup_syslogd 62 | 63 | run_step "Rotate and log $MSG1" log_rotate "$MSG1" 64 | run_step "Check first rotation for $MSG1" check_rotate 0 "$MSG1" 65 | run_step "Check notifier" check_notifier 66 | 67 | run_step "Rotate and log $MSG2" log_rotate "$MSG2" 68 | run_step "Check first rotation for $MSG2" check_rotate 0 "$MSG2" 69 | run_step "Check second rotation for $MSG1" check_rotate 1 "$MSG1" 70 | run_step "Check notifier" check_notifier 71 | 72 | run_step "Rotate and log $MSG3" log_rotate "$MSG3" 73 | run_step "Check first rotation for $MSG3" check_rotate 0 "$MSG3" 74 | run_step "Check second rotation for $MSG2" check_rotate 1 "$MSG2" 75 | run_step "Check third rotation for $MSG1" check_rotate 2 "$MSG1" 76 | run_step "Check notifier" check_notifier 77 | -------------------------------------------------------------------------------- /src/socket.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (C) 2017-2023 Joachim Wiberg 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef SYSKLOGD_SOCKET_H_ 32 | #define SYSKLOGD_SOCKET_H_ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | int socket_register(int sd, struct addrinfo *ai, void (*cb)(int, void *), void *arg); 42 | int socket_create (struct addrinfo *ai, char *iface, void (*cb)(int, void *), void *arg); 43 | int socket_close (int sd); 44 | int socket_mcast (int sd, struct addrinfo *ai, char *iface, int ttl); 45 | int socket_ffs (int family); 46 | int socket_poll (struct timeval *timeout); 47 | 48 | #endif /* SYSKLOGD_SOCKET_H_ */ 49 | -------------------------------------------------------------------------------- /syslog.conf: -------------------------------------------------------------------------------- 1 | # /etc/syslog.conf - Configuration file for syslogd(8) 2 | # 3 | # For information about the format of this file, see syslog.conf(5) 4 | # 5 | 6 | # 7 | # First some standard log files. Log by facility. 8 | # 9 | auth,authpriv.* /var/log/auth.log 10 | *.*;auth,authpriv.none -/var/log/syslog 11 | 12 | #cron.* /var/log/cron.log 13 | #daemon.* -/var/log/daemon.log 14 | kern.* -/var/log/kern.log 15 | #lpr.* -/var/log/lpr.log 16 | mail.* -/var/log/mail.log 17 | #user.* -/var/log/user.log 18 | 19 | # 20 | # Logging for the mail system. Split it up so that 21 | # it is easy to write scripts to parse these files. 22 | # 23 | #mail.info -/var/log/mail.info 24 | #mail.warn -/var/log/mail.warn 25 | mail.err /var/log/mail.err 26 | #mail.*;mail.!=info -/var/log/mail 27 | #mail,news.=info -/var/log/info 28 | 29 | # The tcp wrapper loggs with mail.info, we display all 30 | # the connections on tty12 31 | # 32 | #mail.=info /dev/tty12 33 | 34 | # 35 | # Some "catch-all" log files. 36 | # 37 | #*.=debug;\ 38 | # auth,authpriv.none;\ 39 | # news.none;mail.none -/var/log/debug 40 | *.=info;*.=notice;*.=warn;\ 41 | auth,authpriv.none;\ 42 | cron,daemon.none;\ 43 | mail,news.none -/var/log/messages 44 | 45 | # 46 | # Store all critical events, except kernel logs, in critical RFC5424 format. 47 | # Override global log rotation settings, rotate every 10MiB, keep 5 old logs, 48 | # 49 | #*.=crit;kern.none /var/log/critical ;rotate=10M:5,RFC5424 50 | 51 | # Example of sending events to remote syslog server. 52 | # All events from notice and above, except auth, authpriv 53 | # and any kernel message are sent to server finlandia in 54 | # RFC5424 formatted output. 55 | # 56 | #*.notice;auth,authpriv.none;\ 57 | # kern.none @finlandia ;RFC5424 58 | 59 | # Emergencies are sent to anyone logged in 60 | # 61 | *.=emerg * 62 | 63 | # Priority alert and above are sent to the operator 64 | # 65 | #*.alert root,joey 66 | 67 | # 68 | # Secure mode, same as -s, none(0), on(1), full(2). When enabled 69 | # only logging to remote syslog server possible, with full secure 70 | # mode, not even that is possible. We default to prevent syslogd 71 | # from opening UDP/514 and receiving messages from other systems. 72 | # 73 | secure_mode 1 74 | 75 | # 76 | # Global log rotation, same as -r SIZE:COUNT, command line wins. 77 | # 78 | #rotate_size 1M 79 | #rotate_count 5 80 | 81 | # 82 | # Include all config files in /etc/syslog.d/ 83 | # 84 | include /etc/syslog.d/*.conf 85 | -------------------------------------------------------------------------------- /test/fwd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify FWD between two syslogd, second binds 127.0.0.2:5555 3 | # 4 | # Three types of messages are sent: 5 | # 1. A "normal" message, way shorter than any limit 6 | # 2. A long message, matching the udp_size value 7 | # 3. A too long message, truncated to the udp_size value 8 | # 9 | . "${srcdir:-.}/lib.sh" 10 | 11 | # Constants 12 | MAX_UDP_PAYLOAD=480 13 | HEADER="<101>1 2024-12-27T10:39:30.440026+01:00 $(hostname) fwd - NTP123 - " 14 | HEADER_LEN=${#HEADER} 15 | AVAILABLE_MSG_LEN=$((MAX_UDP_PAYLOAD - HEADER_LEN)) 16 | 17 | [ ${AVAILABLE_MSG_LEN} -le 0 ] && FAIL "Header is too long for the udp_size limit." 18 | 19 | # Generate Messages 20 | MSG="short message" 21 | LONG_MSG=$(printf "%-${AVAILABLE_MSG_LEN}s" | tr ' ' 'A') 22 | TOO_LONG_MSG=$(printf "%-$((AVAILABLE_MSG_LEN + 100))s" | tr ' ' 'B') 23 | 24 | setup_sender() 25 | { 26 | # Cap UDP payload to 480 octets 27 | cat <<-EOF >"${CONFD}/fwd.conf" 28 | udp_size ${MAX_UDP_PAYLOAD} 29 | kern.* /dev/null 30 | ntp.* @[::1]:${PORT2} ;RFC5424 31 | EOF 32 | setup -m0 33 | } 34 | 35 | setup_receiver() 36 | { 37 | cat <<-EOF >"${CONFD2}/50-default.conf" 38 | kern.* /dev/null 39 | *.*;kern.none ${LOG2} ;RFC5424 40 | EOF 41 | setup2 -m0 -a "[::1]:*" -b ":${PORT2}" 42 | } 43 | 44 | # Helper function to send and verify reception 45 | send_and_verify() 46 | { 47 | msg="$1" 48 | expected="$2" 49 | 50 | logger -t fwd -p ntp.notice -m "NTP123" "${msg}" 51 | sleep 3 # Allow message to be received, processed, and forwarded 52 | 53 | logged_msg=$(grep "fwd - NTP123 -" "${LOG2}" |tail -1) 54 | message=$(echo "$logged_msg" | sed -n "s/.*fwd - NTP123 - //p") 55 | if [ "${message}" != "${expected}" ]; then 56 | echo "EXPECTED: ${expected}" 57 | echo "GOT: ${message}" 58 | FAIL 59 | fi 60 | dprint "OK, got: $logged_msg" 61 | } 62 | 63 | verify_msg() 64 | { 65 | send_and_verify "${MSG}" "${MSG}" 66 | } 67 | 68 | verify_forward() 69 | { 70 | send_and_verify "${LONG_MSG}" "${LONG_MSG}" 71 | } 72 | 73 | verify_capped() 74 | { 75 | send_and_verify "${TOO_LONG_MSG}" \ 76 | "$(echo "${TOO_LONG_MSG}" | cut -c1-${AVAILABLE_MSG_LEN})" 77 | } 78 | 79 | run_step "Set up sender syslogd to log in RFC5424 format" setup_sender 80 | run_step "Set up receiver syslogd to log to file in RFC5424 format" setup_receiver 81 | run_step "Verify forward of normal message" verify_msg 82 | run_step "Verify forward of long message (480 chars)" verify_forward 83 | run_step "Verify truncation of too-long message" verify_capped 84 | -------------------------------------------------------------------------------- /test/listen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify listen changes in .conf file at runtime w/o having to restart 3 | # syslogd. We want to ensure adding and removing listen addresses work 4 | # as intended. 5 | . "${srcdir:-.}/lib.sh" 6 | 7 | # 8 | # Helper functions 9 | # 10 | 11 | set_listen() 12 | { 13 | echo "secure_mode $1" > "${CONF}" 14 | shift 1 15 | for l in "$@"; do 16 | echo "listen $l" >> "${CONF}" 17 | done 18 | 19 | if is_running; then 20 | reload 21 | else 22 | dprint "Not running, calling setup0 -m0" 23 | setup0 10.0.0.1/24 -m0 24 | fi 25 | } 26 | 27 | do_port_check() 28 | { 29 | dprint "Checking for port $1" 30 | netstat -atnu | grep "$1" 31 | } 32 | 33 | check_not_open() 34 | { 35 | do_port_check "$1" || return 0 36 | } 37 | 38 | check_port_open() 39 | { 40 | do_port_check "$1" 41 | } 42 | 43 | # 44 | # Test steps 45 | # 46 | 47 | verify_secure_daemon() 48 | { 49 | set_listen 2 50 | check_not_open 514 51 | } 52 | 53 | verify_safe_daemon() 54 | { 55 | set_listen 1 56 | check_not_open 514 57 | } 58 | 59 | verify_default_daemon() 60 | { 61 | set_listen 0 62 | check_port_open 514 63 | } 64 | 65 | verify_local_daemon() 66 | { 67 | set_listen 0 127.0.0.1:510 68 | check_port_open 510 69 | } 70 | 71 | verify_bind() 72 | { 73 | set_listen 0 10.0.0.1:512 74 | check_port_open 512 75 | } 76 | 77 | verify_delayed_bind() 78 | { 79 | addr=10.0.0.2 80 | port=513 81 | 82 | set_listen 0 $addr:$port 83 | sleep 1 84 | 85 | dprint "Delayed add of bind address $addr:$port ..." 86 | ip addr add "$addr"/24 dev eth0 87 | 88 | dprint "Waiting for syslogd to react ..." 89 | sleep 5 90 | 91 | check_port_open $port 92 | } 93 | 94 | # 95 | # Run test steps 96 | # 97 | 98 | run_step "Verify listen off - no remote no ports" verify_secure_daemon 99 | run_step "Verify listen off - only send to remote, no ports" verify_safe_daemon 100 | run_step "Verify listen on, default" verify_default_daemon 101 | 102 | run_step "Verify listen on 127.0.0.1:510" verify_local_daemon 103 | run_step "Verify port 514 is closed" check_not_open 514 104 | 105 | run_step "Verify listen on 10.0.0.1:512" verify_bind 106 | run_step "Verify port 510 is closed" check_not_open 510 107 | 108 | run_step "Verify delayed bind to new address 10.0.0.2:513" verify_delayed_bind 109 | run_step "Verify port 512 is closed" check_not_open 512 110 | -------------------------------------------------------------------------------- /test/api.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC2317 3 | . "${srcdir:-.}/lib.sh" 4 | 5 | export MSG="no-openlog-apitest" 6 | 7 | # 8 | # Test steps 9 | # 10 | 11 | verify_basic_syslog() 12 | { 13 | ./api 14 | grep "api: ${MSG}" "${LOG}" 15 | } 16 | 17 | verify_basic_openlog() 18 | { 19 | cat <<-EOF >"${CONFD}/console.conf" 20 | console.* -${LOGCONS} 21 | EOF 22 | reload 23 | 24 | ./api -i foo 25 | tenacious 2 grep "foo: ${MSG}" "${LOGCONS}" 26 | } 27 | 28 | verify_setlogmask_all() 29 | { 30 | ./api -i xyzzy 31 | tenacious 2 grep "xyzzy: ${MSG}" "${LOGCONS}" 32 | } 33 | 34 | # Expected to fail, logs with LOG_INFO 35 | verify_setlogmask_notice() 36 | { 37 | ./api -i bar -l 38 | sleep 1 # Account for any possible delays 39 | grep "bar: ${MSG}" "${LOGCONS}" || return 0 40 | } 41 | 42 | verify_syslogp() 43 | { 44 | cat <<-EOF >"${CONFD}/v1.conf" 45 | ftp.* -${LOGV1} ;RFC5424 46 | EOF 47 | reload 48 | 49 | ./api -i troglobit -p 50 | tenacious 2 grep "troglobit - MSGID - ${MSG}" "${LOGV1}" 51 | } 52 | 53 | verify_rfc5424() 54 | { 55 | ../src/logger -p ftp.notice -u "${SOCK}" -m "MSDSD" -d '[exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"]' "waldo" 56 | tenacious 2 grep "exampleSDID@32473" "${LOGV1}" 57 | } 58 | 59 | verify_fqdn() 60 | { 61 | ../src/logger -p ftp.notice -u "${SOCK}" -m "MSDSD" -H "baz.example.com" "Xyzzy" 62 | tenacious 2 grep "baz.example.com" "${LOGV1}" 63 | } 64 | 65 | verify_localN_notice() 66 | { 67 | cat <<-EOF >"${CONFD}/notice.conf" 68 | *.notice -${LOG2} 69 | EOF 70 | reload 71 | 72 | ../src/logger -p local7.notice -u "${SOCK}" "aye matey" 73 | grep "aye matey" "${LOG2}" 74 | } 75 | 76 | # Expected to fail 77 | verify_localN_info() 78 | { 79 | ../src/logger -p local7.info -u "${SOCK}" "nopenope" 80 | sleep 1 # Account for any possible delays 81 | grep "nopenope" "${LOG2}" 2>/dev/null || return 0 82 | } 83 | 84 | # 85 | # Run test steps 86 | # 87 | 88 | run_step "Set up local syslog daemon" setup -m0 89 | run_step "Verify syslog(), no openlog()" verify_basic_syslog 90 | run_step "Verify openlog() with custom facility" verify_basic_openlog 91 | run_step "Verify setlogmask() default behavior" verify_setlogmask_all 92 | run_step "Verify setlogmask() LOG_NOTICE" verify_setlogmask_notice 93 | run_step "Verify RFC5424 API with syslogp()" verify_syslogp 94 | run_step "Verify RFC5424 API with logger(1)" verify_rfc5424 95 | run_step "Verify RFC5424 FQDN with logger(1)" verify_fqdn 96 | run_step "Verify localN notice with logger(1)" verify_localN_notice 97 | run_step "Verify localN info leak with logger(1)" verify_localN_info 98 | -------------------------------------------------------------------------------- /test/mcast-iface.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify: 3 | # - calling logger with a multicast group on an interface that would 4 | # not be selected from the routing table 5 | # - a syslogd listening to the same group on the same LAN 6 | # 7 | # Noite: unlike other tests, this test relies on a more elaborate 8 | # network setup using a veth pair. 9 | # 10 | . "${srcdir:-.}/lib.sh" 11 | 12 | MSG1="Supersonic, ASOT 253" 13 | MSG2="Beautiful, ASOT 253" 14 | GROUP=225.3.2.1 15 | TTL=3 16 | 17 | 18 | # Runs in a nested unshare and responsible for setting up veth pair 19 | setup_receiver() 20 | { 21 | SYSLOGD=$(realpath ../src/syslogd) 22 | SYSLOGD_ARGS="-KF -m0 -nH" 23 | 24 | cat <<-EOF > "${CONF2}" 25 | uucp.info $LOG2 26 | listen $GROUP%veth0b 27 | EOF 28 | cat <<-EOF > "$DIR/setup.sh" 29 | sleep 1 # wait for parent to create veth pair 30 | 31 | ip link set lo up 32 | ip route add default via 127.0.0.1 33 | 34 | ip addr add 192.168.0.2/24 dev veth0b 35 | ip link set veth0b multicast on up state up 36 | 37 | exec $SYSLOGD $SYSLOGD_ARGS -f "${CONF2}" -p "${SOCK2}" -P "${PID2}" 38 | EOF 39 | chmod +x "$DIR/setup.sh" 40 | 41 | unshare -mrun "$DIR/setup.sh" & 42 | pid=$(echo $! | tee -a "$DIR/PIDs") 43 | dprint "Started as PID $pid" 44 | 45 | ip link add veth0a type veth peer veth0b 46 | ip link set veth0b netns "$pid" 47 | ip addr add 192.168.0.1/24 dev veth0a 48 | ip link set veth0a multicast on up state up 49 | } 50 | 51 | # Runs in first unshare 52 | setup_sender() 53 | { 54 | cat <<-EOF >"${CONF}" 55 | *.* @$GROUP ;RFC5424,iface=veth0a,ttl=$TTL 56 | EOF 57 | setup0 172.16.31.12 -m0 -s 58 | } 59 | 60 | verify_mcast_fwd() 61 | { 62 | cap_start veth0a 514 63 | logger -p uucp.info "$MSG2" 64 | cap_stop 65 | 66 | cap_find "$MSG2" 67 | } 68 | 69 | verify_mcast_ttl() 70 | { 71 | ttl=$(cap_find "$MSG2" |awk '{print $2}') 72 | test "$ttl" -eq "$TTL" 73 | } 74 | 75 | verify_mcast_rcv() 76 | { 77 | grep -H "$MSG2" "$LOG2" 78 | } 79 | 80 | verify_logger_send() 81 | { 82 | cap_start veth0a 514 83 | logger -H logger -p uucp.info -h "$GROUP" -o iface=veth0a "$MSG1" 84 | cap_stop 85 | 86 | cap_find "$MSG1" 87 | } 88 | 89 | verify_logger_recv() 90 | { 91 | grep -H "$MSG1" "$LOG2" 92 | } 93 | 94 | run_step "Set up receiver syslogd" setup_receiver 95 | run_step "Set up sender syslogd" setup_sender 96 | 97 | run_step "Verify multicast forward" verify_mcast_fwd 98 | run_step "Verify multicast TTL=$TTL" verify_mcast_ttl 99 | run_step "Verify multicast received" verify_mcast_rcv 100 | 101 | run_step "Verify logger to group + iface" verify_logger_send 102 | run_step "Verify logger message received" verify_logger_recv 103 | -------------------------------------------------------------------------------- /test/tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify log filtering based on ident/tag for both RFC3154 (BSD) 3 | # and RFC5424 formatted log messages sent to syslogd. A logged 4 | # message can also contain a [PID], so the combinations of various 5 | # tags + pid are also covered. 6 | # 7 | # Regression test for issue #102. 8 | # 9 | . "${srcdir:-.}/lib.sh" 10 | 11 | TG1=pimd 12 | TG2=mrouted 13 | TG3=in.tftpd 14 | MSG="Multicast, a perfect weapon for an imperfect time." 15 | UNX="In UNIX we trust" 16 | DOT="We bring 512 byte block gifts" 17 | 18 | LOGDIR="$DIR/log" 19 | SYSLOG="${LOGDIR}/syslog" 20 | TG1LOG="${LOGDIR}/$TG1.log" 21 | TG2LOG="${LOGDIR}/$TG2.log" 22 | TG3LOG="${LOGDIR}/$TG3.log" 23 | 24 | setup_syslogd() 25 | { 26 | mkdir -p "$LOGDIR" 27 | cat <<-EOF >"${CONF}" 28 | #!-$TG1,$TG2 29 | *.* -$SYSLOG 30 | #!$TG1 31 | *.* $TG1LOG 32 | #!$TG2 33 | *.* $TG2LOG 34 | #!$TG3 35 | *.* $TG3LOG 36 | EOF 37 | setup -m0 38 | } 39 | 40 | # Verify both RFC3164 (BSD) log format and RFC5424, because 41 | # they have different format parsers in syslogd. Generates 42 | # three additional variants of the given log message: rev, 43 | # rot13, and alphabetically sorted. 44 | verify_tag() 45 | { 46 | tag="$1"; shift 47 | log="$1"; shift 48 | msg="$*" 49 | rev=$(echo "$msg" | rev) 50 | rot=$(echo "$msg" | tr 'a-zA-Z' 'n-za-mN-ZA-M') 51 | bin=$(echo "$msg" | sed 's/./&\n/g' | sort | tr -d '\n') 52 | 53 | # BSD log format (with -b) 54 | logger -b -ip user.debug -t "$tag" "$msg" 55 | verify_log "$log" "$msg" | grep "$tag" || return 1 56 | 57 | # RFC5424 (default) 58 | logger -ip user.debug -t "$tag" "$rev" 59 | verify_log "$log" "$rev" | grep "$tag" || return 1 60 | 61 | # BSD without -p flag 62 | logger -b -i -t "$tag" "$rot" 63 | verify_log "$log" "$rot" | grep "$tag" || return 1 64 | 65 | # RFC5424 without -p flag 66 | logger -i -t "$tag" "$bin" 67 | verify_log "$log" "$bin" | grep "$tag" || return 1 68 | } 69 | 70 | verify_log() 71 | { 72 | log="$1"; shift 73 | msg="$*" 74 | 75 | grep "$msg" "$log" 76 | } 77 | 78 | verify_not() 79 | { 80 | verify_log "$@" || return 0 81 | } 82 | 83 | run_step "Set up property based filtering syslogd" setup_syslogd 84 | run_step "Verify basic tag based filtering (1)" verify_tag "$TG1" "$TG1LOG" "$MSG" 85 | run_step "Verify basic tag based filtering (2)" verify_tag "$TG2" "$TG2LOG" "$MSG" 86 | run_step "Verify basic tag based filtering (3)" verify_tag "$TG3" "$TG3LOG" "$DOT" 87 | run_step "Verify not in syslog" verify_not "$SYSLOG" "$MSG" 88 | 89 | run_step "Verify unfiltered tag logging" verify_tag "foo" "$SYSLOG" "$UNX" 90 | run_step "Verify unfiltered message in syslog" verify_log "$SYSLOG" "$UNX" 91 | run_step "Verify unfiltered message not filtered" verify_not "$TG1LOG" "$UNX" 92 | -------------------------------------------------------------------------------- /test/facility.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test message to various facilities 3 | # shellcheck disable=SC2317 4 | . "${srcdir:-.}/lib.sh" 5 | 6 | LOGFILE="${DIR}/sudo.log" 7 | LOGDIR="$DIR/log" 8 | LOGMSG="hey ho here we go" 9 | 10 | AUTHMSG="Here is the password: p455w0rd" 11 | AUTHLOG=${LOGDIR}/auth.log 12 | 13 | DBGLOG=${LOGDIR}/debug 14 | MSGLOG=${LOGDIR}/messages 15 | 16 | 17 | setup_facility() 18 | { 19 | mkdir -p "$LOGDIR" 20 | install -m 600 /dev/null "${LOGDIR}/auth.log" 21 | 22 | cat <<-EOF >"${CONFD}/facility.conf" 23 | auth,authpriv.* $AUTHLOG 24 | *.*;auth,authpriv.none;\ 25 | local7.!=notice -${LOGDIR}/syslog 26 | daemon.* -${LOGDIR}/daemon.log 27 | kern.* -${LOGDIR}/kern.log 28 | lpr.* -${LOGDIR}/lpr.log 29 | mail.* -${LOGDIR}/mail.log 30 | user.* -${LOGDIR}/user.log 31 | mail.info -${LOGDIR}/mail.info 32 | mail.warn -${LOGDIR}/mail.warn 33 | mail.err ${LOGDIR}/mail.err 34 | *.=debug;\ 35 | auth,authpriv.none;\ 36 | news.none;mail.none -$DBGLOG 37 | *.=info;*.=notice;*.=warn;\ 38 | auth,authpriv.none;\ 39 | cron,daemon.none;\ 40 | local7.!=notice;\ 41 | mail,news.none -$MSGLOG 42 | *.emerg * 43 | local7.=notice $LOGFILE 44 | EOF 45 | setup -m0 46 | } 47 | 48 | verify_logdir_exists() 49 | { 50 | ls "$AUTHLOG" # Must exist 51 | } 52 | 53 | # shellcheck disable=SC2012 54 | verify_authlog_perms() 55 | { 56 | perms=$(ls -l "$AUTHLOG" | tee "$DIR/foo" | awk '{print $1}') 57 | cat "$DIR/foo" 58 | test "$perms" = "-rw-------" 59 | } 60 | 61 | verify_logfile_exists() 62 | { 63 | ls "$LOGFILE" # Must exist 64 | } 65 | 66 | verify_logfile() 67 | { 68 | TAG="facility" 69 | 70 | logger -t $TAG -p local7.notice "${LOGMSG}" 71 | sleep 1 72 | grep "$TAG: ${LOGMSG}" "$LOGFILE" 73 | } 74 | 75 | # Ensure the dedicated local7.notice message reached no other log file 76 | verify_leaks() 77 | { 78 | find "$LOGDIR" -type f -exec grep -q "$LOGMSG" {} \; -quit 79 | } 80 | 81 | verify_authpriv() 82 | { 83 | logger -t login -p authpriv.debug "$AUTHMSG" 84 | grep "$AUTHMSG" "$AUTHLOG" 85 | } 86 | 87 | # Ensure $AUTHMSG is only in $AUTHLOG 88 | verify_authleaks() 89 | { 90 | find "$LOGDIR" -type f ! -wholename "$AUTHLOG" -exec grep -H "$AUTHMSG" {} \; -quit 91 | } 92 | 93 | run_step "Set up facility filtering daemon" setup_facility 94 | run_step "Verify log dir exists" verify_logdir_exists 95 | run_step "Verify auth.log permissions" verify_authlog_perms 96 | run_step "Verify log file exists" verify_logfile_exists 97 | run_step "Verify local7.notice log file" verify_logfile 98 | run_step "Verify no local7 log leaks" verify_leaks 99 | run_step "Verify authpriv logging" verify_authpriv 100 | run_step "Verify no authpriv leaks" verify_authleaks 101 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to sysklogd 2 | ======================== 3 | 4 | We welcome any and all help in the form of bug reports, fixes, patches 5 | for new features, *preferably as GitHub pull requests*. Other methods 6 | are of course also possible: emailing the maintainer a patch or even a 7 | raw file, or simply emailing a feature request or an alert of a problem. 8 | However, email questions/requests/alerts always risk memory exhaustion 9 | on the part of the maintainer(s). 10 | 11 | If you are unsure of what to do, or how to implement an idea or bug fix, 12 | open an issue with the title `"[RFC: Unsure if this is a bug ... ?"`, 13 | or similar, so we can discuss it. Talking about the code first is the best 14 | way to get started before submitting a pull request. 15 | 16 | Either way, when sending an email, patch, or pull request, start by 17 | stating the version the change is made against, what it does, and most 18 | importanyl -- why. 19 | 20 | Please take care to ensure you follow the project *coding style* and the 21 | commit message format. If you follow these recommendations you help the 22 | maintainer(s) and make it easier for them to include your code. 23 | 24 | 25 | Coding Style 26 | ------------ 27 | 28 | > **Tip:** Always submit code that follows the style of surrounding code! 29 | 30 | First of all, lines are allowed to be longer than 72 characters these 31 | days. In fact, there exist no enforced maximum, but keeping it around 32 | 100 chars is OK. 33 | 34 | The coding style itself is otherwise strictly Linux [KNF][]. 35 | 36 | 37 | Commit Messages 38 | --------------- 39 | 40 | Commit messages exist to track *why* a change was made. Try to be as 41 | clear and concise as possible in your commit messages, and always, be 42 | proud of your work and set up a proper GIT identity for your commits: 43 | 44 | git config --global user.name "Jane Doe" 45 | git config --global user.email jane.doe@example.com 46 | 47 | Example commit message from the [Pro Git][gitbook] online book, notice 48 | how `git commit -s` is used to automatically add a `Signed-off-by`: 49 | 50 | Brief, but clear and concise summary of changes 51 | 52 | More detailed explanatory text, if necessary. Wrap it to about 72 53 | characters or so. In some contexts, the first line is treated as 54 | the subject of an email and the rest of the text as the body. The 55 | blank line separating the ummary from the body is critical (unless 56 | you omit the body entirely); tools like rebase can get confused if 57 | you run the two together. 58 | 59 | Further paragraphs come after blank lines. 60 | 61 | - Bullet points are okay, too 62 | 63 | - Typically a hyphen or asterisk is used for the bullet, preceded 64 | by a single space, with blank lines in between, but conventions 65 | vary here 66 | 67 | Signed-off-by: Jane Doe 68 | 69 | 70 | Code of Conduct 71 | --------------- 72 | 73 | It is expected of everyone engaging in the project to, in the words of 74 | Bill & Ted; [be excellent to each other][conduct]. 75 | 76 | 77 | [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form 78 | [gitbook]: https://git-scm.com/book/ch5-2.html 79 | [conduct]: https://github.com/troglobit/sysklogd/blob/master/.github/CODE-OF-CONDUCT.md 80 | 81 | -------------------------------------------------------------------------------- /test/parens.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify parentheses handling in log tags for RFC3164 messages. 3 | # Regression test for issue #104. 4 | # 5 | . "${srcdir:-.}/lib.sh" 6 | 7 | 8 | MSG1="Failed to execute /usr/bin/pkttyagent: No such file or directory" 9 | MSG2="Normal application message" 10 | MSG3="Version specific message" 11 | MSG4="Service startup message" 12 | 13 | LOGDIR="$DIR/log" 14 | SYSLOG="${LOGDIR}/syslog" 15 | 16 | setup_syslogd() 17 | { 18 | mkdir -p "$LOGDIR" 19 | cat <<-EOF >"${CONF}" 20 | # Log everything for testing 21 | *.* -$SYSLOG 22 | EOF 23 | setup -m0 24 | } 25 | 26 | 27 | extract_tag() 28 | { 29 | msg="$1" 30 | actual_line=$(grep "$msg" "$SYSLOG" | tail -1) 31 | if [ -n "$actual_line" ]; then 32 | echo "$actual_line" | sed -n 's/.*[0-9][0-9] [^ ]* \([^:]*\):.*/\1/p' 33 | fi 34 | } 35 | 36 | show_result() 37 | { 38 | input="$1" 39 | expected="$2" 40 | got="$3" 41 | 42 | echo "Input: '$input'" 43 | echo "Expected: '$expected'" 44 | echo "Got: '$got'" 45 | } 46 | 47 | verify() 48 | { 49 | input_tag="$1" 50 | expected_tag="$2" 51 | msg="$3" 52 | expected_pattern="${4:-$expected_tag}" 53 | 54 | actual_tag=$(extract_tag "$msg") 55 | if [ -n "$actual_tag" ]; then 56 | show_result "$input_tag" "$expected_tag" "$actual_tag" 57 | if grep -q "$expected_pattern.*$msg" "$SYSLOG"; then 58 | return 0 59 | else 60 | echo "Log contents:" 61 | cat "$SYSLOG" 62 | return 1 63 | fi 64 | else 65 | echo "Message not found in log" 66 | echo "Log contents:" 67 | cat "$SYSLOG" 68 | return 1 69 | fi 70 | } 71 | 72 | test_normal_tag() 73 | { 74 | logger -b -ip user.info -t "normal-app" "$MSG2" 75 | verify "normal-app" "normal-app" "$MSG2" 76 | } 77 | 78 | test_paren_stripping() 79 | { 80 | logger -b -ip user.info -t "(polkit-agent)" "$MSG1" 81 | verify "(polkit-agent)" "polkit-agent" "$MSG1" 82 | } 83 | 84 | test_service_stripping() 85 | { 86 | logger -b -ip user.info -t "(service-name)" "$MSG4" 87 | verify "(service-name)" "service-name" "$MSG4" 88 | } 89 | 90 | test_partial_parens() 91 | { 92 | logger -b -ip user.info -t "app(version)" "$MSG3" 93 | verify "app(version)" "app(version)" "$MSG3" 94 | } 95 | 96 | test_normal_tag_with_pid() 97 | { 98 | pid="$$" 99 | 100 | logger -b -ip user.info -t "normal-app[${pid}]" "$MSG2" 101 | verify "normal-app[${pid}]" "normal-app[${pid}]" "$MSG2" "normal-app\[${pid}\]" 102 | } 103 | 104 | test_paren_with_pid() 105 | { 106 | pid="$$" 107 | 108 | logger -b -ip user.info -t "(polkit-agent)[$pid]" "$MSG1" 109 | verify "(polkit-agent)[$pid]" "polkit-agent[$pid]" "$MSG1" "polkit-agent\[$pid\]" 110 | } 111 | 112 | 113 | run_step "Set up syslogd for parentheses testing" setup_syslogd 114 | run_step "Test parenthetical tag stripping" test_paren_stripping 115 | run_step "Test normal tag preservation" test_normal_tag 116 | run_step "Test partial parentheses preservation" test_partial_parens 117 | run_step "Test service parenthetical tag" test_service_stripping 118 | run_step "Test parenthetical tag with PID" test_paren_with_pid 119 | run_step "Test normal tag with PID" test_normal_tag_with_pid 120 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2023 Joachim Wiberg 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the University nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | SUBDIRS = example man src test 29 | doc_DATA = README.md LICENSE ChangeLog.md syslog.conf 30 | EXTRA_DIST = README.md LICENSE ChangeLog.md syslog.conf 31 | 32 | if HAVE_SYSTEMD 33 | systemd_DATA = syslogd.service 34 | endif 35 | 36 | # 37 | # Check if tagged in git 38 | # 39 | release-hook: 40 | @if [ ! `git tag -l v$(PACKAGE_VERSION) | grep $(PACKAGE_VERSION)` ]; then \ 41 | echo; \ 42 | printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ 43 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 44 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 45 | printf "OK, aborting release.\n"; \ 46 | exit 1; \ 47 | fi; \ 48 | echo; \ 49 | else \ 50 | echo; \ 51 | printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ 52 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 53 | echo; \ 54 | fi 55 | 56 | # 57 | # Target to run when building a release 58 | # 59 | release: release-hook distcheck 60 | @for file in $(DIST_ARCHIVES); do \ 61 | md5sum $$file > ../$$file.md5; \ 62 | sha256sum $$file > ../$$file.sha256; \ 63 | done 64 | @mv $(DIST_ARCHIVES) ../ 65 | @echo 66 | @echo "Resulting release files =======================================================================" 67 | @for file in $(DIST_ARCHIVES); do \ 68 | printf "%-30s Distribution tarball\n" $$file; \ 69 | printf "%-30s " $$file.md5; cat ../$$file.md5 | cut -f1 -d' '; \ 70 | printf "%-30s " $$file.sha256; cat ../$$file.sha256 | cut -f1 -d' '; \ 71 | done 72 | 73 | # Workaround for systemd unit file duing distcheck 74 | DISTCHECK_CONFIGURE_FLAGS = --with-systemd=$$dc_install_base/$(systemd) 75 | DISTCLEANFILES = lib/.libs/* 76 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2023 Joachim Wiberg 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the University nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | bin_PROGRAMS = 29 | sbin_PROGRAMS = syslogd 30 | lib_LTLIBRARIES = libsyslog.la 31 | 32 | if ENABLE_LOGGER 33 | bin_PROGRAMS += logger 34 | endif 35 | 36 | AM_CFLAGS = -W -Wall -Wextra -std=c99 -gdwarf-4 37 | AM_CFLAGS += -Wno-unused-result -Wno-unused-parameter -fno-strict-aliasing 38 | AM_CPPFLAGS = -DSYSCONFDIR=\"@sysconfdir@\" -DRUNSTATEDIR=\"@runstatedir@\" 39 | AM_CPPFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_GNU_SOURCE 40 | 41 | syslogd_SOURCES = syslogd.c syslogd.h socket.c socket.h syslog.h 42 | syslogd_SOURCES += timer.c timer.h queue.h compat.h 43 | syslogd_CPPFLAGS = $(AM_CPPFLAGS) -D_XOPEN_SOURCE=600 44 | syslogd_LDADD = $(LIBS) $(LIBOBJS) 45 | 46 | logger_SOURCES = logger.c syslog.h 47 | logger_CPPFLAGS = $(AM_CPPFLAGS) -D_XOPEN_SOURCE=600 48 | logger_LDADD = $(LIBS) $(LIBOBJS) 49 | logger_LDADD += libsyslog.la 50 | 51 | pkgconfigdir = $(libdir)/pkgconfig 52 | pkgincludedir = $(includedir)/syslog 53 | pkgconfig_DATA = libsyslog.pc 54 | pkginclude_HEADERS = syslog.h 55 | libsyslog_la_SOURCES = syslog.c syslog.h compat.h 56 | libsyslog_la_CPPFLAGS = $(AM_CPPFLAGS) -D_XOPEN_SOURCE=600 57 | libsyslog_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2:0:2 58 | libsyslog_la_LIBADD = $(LTLIBOBJS) 59 | 60 | # Both libsyslog and syslogd/logger require objects like lib/pidfile.o. 61 | # For libsyslog_la, the object files should be compiled with -fPIC, but 62 | # for syslogd and logger these object files should not be compled with 63 | # -fPIC. There is a race issue when the two different lib/pidfile.o are 64 | # compiled at the same time, which cause errors like: 65 | # 66 | # ld: syslogd-syslogd.o: in function `main': syslogd.c:417: undefined 67 | # reference to `__pidfile' 68 | # 69 | # Work around the problem by make LIBOBJS depend on libsyslog.la, 70 | # so that LIBOBJS/syslogd/logger will start compile after libsyslog.la 71 | # is completed 72 | $(LIBOBJS): $(lib_LTLIBRARIES) 73 | -------------------------------------------------------------------------------- /lib/pidfile.c: -------------------------------------------------------------------------------- 1 | /* Updated by troglobit for libite/finit/uftpd projects 2016/07/04 */ 2 | /* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */ 3 | /* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ 4 | 5 | /*- 6 | * Copyright (c) 1999 The NetBSD Foundation, Inc. 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Jason R. Thorpe. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions 14 | * are met: 15 | * 1. Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include /* utimensat() */ 35 | #include /* utimensat() on *BSD */ 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include "compat.h" /* For utimensat() if missing */ 42 | 43 | static char *pidfile_path = NULL; 44 | static pid_t pidfile_pid = 0; 45 | 46 | static void pidfile_cleanup(void); 47 | 48 | const char *__pidfile_path = RUNSTATEDIR; 49 | const char *__pidfile_name = NULL; 50 | 51 | int 52 | __pidfile(const char *basename) 53 | { 54 | int save_errno; 55 | int atexit_already; 56 | pid_t pid; 57 | FILE *f; 58 | 59 | if (basename == NULL) 60 | basename = getprogname(); 61 | 62 | pid = getpid(); 63 | atexit_already = 0; 64 | 65 | if (pidfile_path != NULL) { 66 | if (!access(pidfile_path, R_OK) && pid == pidfile_pid) { 67 | utimensat(0, pidfile_path, NULL, 0); 68 | return (0); 69 | } 70 | free(pidfile_path); 71 | pidfile_path = NULL; 72 | __pidfile_name = NULL; 73 | atexit_already = 1; 74 | } 75 | 76 | if (basename[0] != '/') { 77 | if (asprintf(&pidfile_path, "%s/%s.pid", __pidfile_path, basename) == -1) 78 | return (-1); 79 | } else { 80 | if (asprintf(&pidfile_path, "%s", basename) == -1) 81 | return (-1); 82 | } 83 | 84 | if ((f = fopen(pidfile_path, "w")) == NULL) { 85 | save_errno = errno; 86 | free(pidfile_path); 87 | pidfile_path = NULL; 88 | errno = save_errno; 89 | return (-1); 90 | } 91 | 92 | if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) { 93 | save_errno = errno; 94 | (void) fclose(f); 95 | (void) unlink(pidfile_path); 96 | free(pidfile_path); 97 | pidfile_path = NULL; 98 | errno = save_errno; 99 | return (-1); 100 | } 101 | (void) fclose(f); 102 | __pidfile_name = pidfile_path; 103 | 104 | /* 105 | * LITE extension, no need to set up another atexit() handler 106 | * if user only called us to update the mtime of the PID file 107 | */ 108 | if (atexit_already) 109 | return (0); 110 | 111 | pidfile_pid = pid; 112 | if (atexit(pidfile_cleanup) < 0) { 113 | save_errno = errno; 114 | (void) unlink(pidfile_path); 115 | free(pidfile_path); 116 | pidfile_path = NULL; 117 | pidfile_pid = 0; 118 | errno = save_errno; 119 | return (-1); 120 | } 121 | 122 | return (0); 123 | } 124 | 125 | static void 126 | pidfile_cleanup(void) 127 | { 128 | if (pidfile_path != NULL && pidfile_pid == getpid()) { 129 | (void) unlink(pidfile_path); 130 | free(pidfile_path); 131 | pidfile_path = NULL; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1983, 1988, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef SYSKLOGD_COMPAT_H_ 31 | #define SYSKLOGD_COMPAT_H_ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /* 40 | * The following macro is used to remove const cast-away warnings 41 | * from gcc -Wcast-qual; it should be used with caution because it 42 | * can hide valid errors; in particular most valid uses are in 43 | * situations where the API requires it, not to cast away string 44 | * constants. We don't use *intptr_t on purpose here and we are 45 | * explicit about unsigned long so that we don't have additional 46 | * dependencies. 47 | */ 48 | #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 49 | 50 | /* Pthread wrapper for BSD LWP mutexes */ 51 | typedef pthread_mutex_t mutex_t; 52 | 53 | #ifndef mutex_lock 54 | #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER 55 | #define mutex_lock(m) pthread_mutex_lock(m) 56 | #define mutex_unlock(m) pthread_mutex_unlock(m) 57 | #endif 58 | 59 | /* BSD have sa_len, Linux/GNU doesn't, added with 4.3-Reno */ 60 | #if defined(_AIX) || (defined(BSD) && (BSD >= 199006)) 61 | #define HAVE_SA_LEN 62 | #endif 63 | 64 | #ifndef HAVE_STRLCPY 65 | #define strlcpy __strlcpy 66 | size_t strlcpy(char *dst, const char *src, size_t siz); 67 | #endif 68 | 69 | #ifndef HAVE_STRLCAT 70 | #define strlcat __strlcat 71 | size_t strlcat(char *dst, const char *src, size_t siz); 72 | #endif 73 | 74 | #ifndef HAVE_PIDFILE 75 | #define pidfile __pidfile 76 | int pidfile(const char *basename); 77 | #endif 78 | 79 | #ifndef HAVE_UTIMENSAT 80 | #define utimensat __utimensat 81 | int utimensat(int dirfd, const char *pathname, const struct timespec ts[2], int flags); 82 | #endif 83 | 84 | #ifndef HAVE_GETPROGNAME 85 | static inline char *getprogname(void) 86 | { 87 | extern char *__progname; 88 | return __progname; 89 | } 90 | #endif 91 | 92 | #ifndef HAVE_STRTOBYTES 93 | static inline int strtobytes(char *arg) 94 | { 95 | int mod = 0, bytes; 96 | size_t pos; 97 | 98 | if (!arg) 99 | return -1; 100 | 101 | pos = strspn(arg, "0123456789"); 102 | if (arg[pos] != 0) { 103 | if (arg[pos] == 'G') 104 | mod = 3; 105 | else if (arg[pos] == 'M') 106 | mod = 2; 107 | else if (arg[pos] == 'k') 108 | mod = 1; 109 | else 110 | return -1; 111 | 112 | arg[pos] = 0; 113 | } 114 | 115 | bytes = atoi(arg); 116 | while (mod--) 117 | bytes *= 1000; 118 | 119 | return bytes; 120 | } 121 | #endif 122 | 123 | static inline void parse_rotation(char *optarg, off_t *size, int *num) 124 | { 125 | char buf[100]; 126 | char *c; 127 | int sz = 0, cnt = 0; 128 | 129 | strlcpy(buf, optarg, sizeof(buf)); 130 | c = strchr(buf, ':'); 131 | if (c) { 132 | *c++ = 0; 133 | cnt = atoi(c); 134 | } 135 | 136 | sz = strtobytes(buf); 137 | if (sz > 0) 138 | *size = sz; 139 | if (cnt > 0) 140 | *num = cnt; 141 | } 142 | 143 | #endif /* SYSKLOGD_COMPAT_H_ */ 144 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (C) 2017-2023 Joachim Wiberg 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "queue.h" 39 | #include "socket.h" 40 | 41 | struct timer { 42 | LIST_ENTRY(timer) tmr_link; 43 | 44 | int tmr_period; /* period time in seconds */ 45 | time_t tmr_timeout; 46 | 47 | void (*tmr_cb)(void *arg); 48 | void *tmr_arg; 49 | }; 50 | 51 | static LIST_HEAD(, timer) tmr_head = LIST_HEAD_INITIALIZER(); 52 | 53 | static struct timespec now; 54 | static int timer_fd[2]; 55 | 56 | /* 57 | * what time is it? 58 | */ 59 | int timer_update(void) 60 | { 61 | return clock_gettime(CLOCK_MONOTONIC, &now); 62 | } 63 | 64 | time_t timer_now(void) 65 | { 66 | return now.tv_sec; 67 | } 68 | 69 | /* 70 | * create periodic timer (seconds) 71 | */ 72 | int timer_add(int period, void (*cb)(void *), void *arg) 73 | { 74 | struct timer *tmr; 75 | 76 | tmr = calloc(1, sizeof(*tmr)); 77 | if (!tmr) 78 | return -1; 79 | 80 | tmr->tmr_period = period; 81 | tmr->tmr_cb = cb; 82 | tmr->tmr_arg = arg; 83 | 84 | LIST_INSERT_HEAD(&tmr_head, tmr, tmr_link); 85 | 86 | return 0; 87 | } 88 | 89 | static int __timer_start(void) 90 | { 91 | struct timer *next, *tmr; 92 | time_t sec; 93 | 94 | LIST_FOREACH(tmr, &tmr_head, tmr_link) { 95 | if (tmr->tmr_timeout == 0) 96 | tmr->tmr_timeout = timer_now() + tmr->tmr_period; 97 | } 98 | 99 | next = LIST_FIRST(&tmr_head); 100 | LIST_FOREACH(tmr, &tmr_head, tmr_link) { 101 | if (next->tmr_timeout > tmr->tmr_timeout) 102 | next = tmr; 103 | } 104 | 105 | sec = next->tmr_timeout - timer_now(); 106 | if (sec <= 0) 107 | sec = 1; 108 | 109 | return alarm((unsigned int)sec); 110 | } 111 | 112 | /* 113 | * start timers 114 | */ 115 | int timer_start(void) 116 | { 117 | if (LIST_EMPTY(&tmr_head)) 118 | return -1; 119 | 120 | timer_update(); 121 | 122 | return __timer_start(); 123 | } 124 | 125 | /* 126 | * callback for activity on pipe 127 | */ 128 | static void timer_cb(int sd, void *arg) 129 | { 130 | struct timer *tmr; 131 | char dummy; 132 | 133 | (void)read(sd, &dummy, 1); 134 | 135 | timer_update(); 136 | 137 | LIST_FOREACH(tmr, &tmr_head, tmr_link) { 138 | if (tmr->tmr_timeout > timer_now()) 139 | continue; 140 | 141 | if (tmr->tmr_cb) 142 | tmr->tmr_cb(tmr->tmr_arg); 143 | tmr->tmr_timeout = 0; 144 | } 145 | 146 | __timer_start(); 147 | } 148 | 149 | /* 150 | * Write to pipe to create an event on SIGALRM 151 | */ 152 | static void sigalarm_handler(int signo) 153 | { 154 | (void)signo; 155 | (void)write(timer_fd[1], "!", 1); 156 | } 157 | 158 | /* 159 | * register signal pipe and callback 160 | */ 161 | int timer_init(void) 162 | { 163 | static int initialized = 0; 164 | struct sigaction sa; 165 | int rc; 166 | 167 | if (initialized) 168 | return 0; 169 | 170 | if (pipe(timer_fd)) { 171 | warn("pipe()"); 172 | return -1; 173 | } 174 | 175 | memset(&sa, 0, sizeof(sa)); 176 | sa.sa_handler = sigalarm_handler; 177 | sa.sa_flags = SA_RESTART; 178 | sigemptyset(&sa.sa_mask); 179 | if (sigaction(SIGALRM, &sa, NULL)) { 180 | warn("sigaction()"); 181 | goto err; 182 | } 183 | 184 | rc = fcntl(timer_fd[0], F_GETFL, 0); 185 | if (rc != -1) { 186 | if (fcntl(timer_fd[0], F_SETFL, rc | O_NONBLOCK) < 0) 187 | warn("Failed setting pipe() descriptor non-blocking"); 188 | } 189 | 190 | rc = socket_register(timer_fd[0], NULL, timer_cb, NULL); 191 | if (rc < 0) { 192 | warn("socket_register()"); 193 | goto err; 194 | } 195 | 196 | initialized = 1; 197 | 198 | return 0; 199 | err: 200 | close(timer_fd[0]); 201 | close(timer_fd[1]); 202 | 203 | return -1; 204 | } 205 | 206 | /* 207 | * deregister signal pipe and callbacks 208 | */ 209 | void timer_exit(void) 210 | { 211 | struct timer *tmr, *tmp; 212 | 213 | alarm(0); 214 | 215 | socket_close((timer_fd[0])); 216 | close(timer_fd[1]); 217 | 218 | LIST_FOREACH_SAFE(tmr, &tmr_head, tmr_link, tmp) { 219 | LIST_REMOVE(tmr, tmr_link); 220 | free(tmr); 221 | } 222 | } 223 | 224 | /** 225 | * Local Variables: 226 | * indent-tabs-mode: t 227 | * c-file-style: "linux" 228 | * End: 229 | */ 230 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2024 Joachim Wiberg 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the University nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | AC_INIT([sysklogd], [2.7.2], 29 | [https://github.com/troglobit/sysklogd/issues],, 30 | [https://github.com/troglobit/sysklogd]) 31 | AC_CONFIG_AUX_DIR(aux) 32 | AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) 33 | LT_INIT([pic-only]) 34 | AM_SILENT_RULES([yes]) 35 | 36 | AC_CONFIG_MACRO_DIRS([m4]) 37 | AC_CONFIG_SRCDIR([src/syslogd.c]) 38 | AC_CONFIG_HEADER([config.h]) 39 | AC_CONFIG_FILES([Makefile 40 | example/Makefile 41 | man/Makefile 42 | src/Makefile 43 | src/libsyslog.pc 44 | test/Makefile 45 | syslogd.service]) 46 | 47 | AC_PROG_CC 48 | AC_PROG_INSTALL 49 | AC_HEADER_STDC 50 | 51 | # Check for required packages 52 | PKG_PROG_PKG_CONFIG 53 | 54 | # Check for usually missing API's, which we can replace 55 | AC_REPLACE_FUNCS([pidfile strlcpy strlcat utimensat]) 56 | AC_CONFIG_LIBOBJ_DIR([lib]) 57 | 58 | # Check for utmp.h 59 | AC_CHECK_HEADERS([utmp.h]) 60 | 61 | # Check for fork() 62 | AC_CHECK_FUNCS([fork]) 63 | 64 | # Check for setsid() 65 | AC_CHECK_FUNCS([setsid]) 66 | 67 | # Check for other library functions 68 | AC_CHECK_FUNCS([getprogname strtobytes]) 69 | 70 | # Command line options 71 | AC_ARG_WITH(dns-delay, 72 | AS_HELP_STRING([--with-dns-delay=SEC], [Retry delay resolving DNS names, default: 60]), 73 | [dns_delay=$withval], [dns_delay='no']) 74 | 75 | AC_ARG_WITH(suspend-time, 76 | AS_HELP_STRING([--with-suspend-time=SEC], [Retry delay for sending to remote, default: 180]), 77 | [suspend_time=$withval], [suspend_time='no']) 78 | 79 | AC_ARG_WITH(systemd, 80 | [AS_HELP_STRING([--with-systemd=DIR], [Directory for systemd service files])],, 81 | [with_systemd=auto]) 82 | 83 | AC_ARG_WITH(logger, 84 | AS_HELP_STRING([--without-logger], [Build without extended logger tool, default: enabled]), 85 | [logger=$withval], [logger='yes']) 86 | 87 | AS_IF([test "x$logger" != "xno"], with_logger="yes", with_logger="no") 88 | AM_CONDITIONAL([ENABLE_LOGGER], [test "x$with_logger" != "xno"]) 89 | 90 | AS_IF([test "x$dns_delay" != "xno"],[ 91 | AS_IF([test "x$dns_delay" = "xyes"],[ 92 | AC_MSG_ERROR([Must supply argument])]) 93 | ] 94 | AC_DEFINE_UNQUOTED(INET_DNS_DELAY, $dns_delay, [Retry delay for resolving DNS names, default: 60]), 95 | dns_delay=60) 96 | 97 | AS_IF([test "x$suspend_time" != "xno"],[ 98 | AS_IF([test "x$suspend_time" = "xyes"],[ 99 | AC_MSG_ERROR([Must supply argument])]) 100 | ] 101 | AC_DEFINE_UNQUOTED(INET_SUSPEND_TIME, $suspend_time, [Retry delay for sending to remote syslog servers, default: 180]), 102 | suspend_time=180) 103 | 104 | # Check where to install the systemd .service file 105 | AS_IF([test "x$with_systemd" = "xyes" -o "x$with_systemd" = "xauto"], [ 106 | def_systemd=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) 107 | AS_IF([test "x$def_systemd" = "x"], 108 | [AS_IF([test "x$with_systemd" = "xyes"], 109 | [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) 110 | with_systemd=no], [with_systemd="$def_systemd"])]) 111 | AS_IF([test "x$with_systemd" != "xno"], 112 | [AC_SUBST([systemddir], [$with_systemd])]) 113 | AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemd" != "xno"]) 114 | 115 | # Expand $sbindir and @$sysconfdir early, for systemd unit file 116 | # NOTE: This does *not* take prefix/exec_prefix override at "make 117 | # install" into account, unfortunately. 118 | test "x$prefix" = xNONE && prefix=$ac_default_prefix 119 | test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' 120 | SBINDIR=`eval echo $sbindir` 121 | SBINDIR=`eval echo $SBINDIR` 122 | AC_SUBST(SBINDIR) 123 | SYSCONFDIR=`eval echo $sysconfdir` 124 | AC_SUBST(SYSCONFDIR) 125 | 126 | # Workaround for as-of-yet unreleased runstatedir support, planned for 127 | # autoconf 2.70, which some major distros have backported. 128 | AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run") 129 | AC_SUBST(runstatedir) 130 | 131 | AC_OUTPUT 132 | 133 | # Expand directories for configuration summary, unexpanded defaults: 134 | # runstatedir => ${localstatedir}/run 135 | RUNSTATEDIR=`eval echo $runstatedir` 136 | RUNSTATEDIR=`eval echo $RUNSTATEDIR` 137 | 138 | cat <> %-78s\e[0m\n" "$1" 34 | } 35 | 36 | # Dimmed text 37 | dprint() 38 | { 39 | printf "\e[2m%-78s\e[0m\n" "$1" 40 | } 41 | 42 | step() 43 | { 44 | heading=${1:-} 45 | if [ -n "$heading" ]; then 46 | num=$((72 - ${#heading} - 1)) 47 | printf "\n\e[1mStep $STEP ― %s " "$heading" 48 | STEP=$((STEP + 1)) 49 | printf -- "―%.0s" $(seq 1 $num) 50 | printf "\e[0m\n" 51 | else 52 | printf "\e[1m――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\e[0m\n" 53 | fi 54 | } 55 | 56 | SKIP() 57 | { 58 | print "TEST: SKIP" 59 | [ $# -gt 0 ] && echo "$*" 60 | exit 77 61 | } 62 | 63 | FAIL() 64 | { 65 | print "TEST: FAIL" 66 | [ $# -gt 0 ] && echo "$*" 67 | exit 99 68 | } 69 | 70 | OK() 71 | { 72 | print "TEST: OK" 73 | [ $# -gt 0 ] && echo "$*" 74 | exit 0 75 | } 76 | 77 | run_step() 78 | { 79 | desc=$1 80 | func=$2 81 | shift 2 82 | 83 | step "${desc}" 84 | if eval "${func} $*"; then 85 | dprint "OK" 86 | else 87 | FAIL "${desc} failed." 88 | fi 89 | } 90 | 91 | tenacious() 92 | { 93 | timeout=$1 94 | shift 95 | 96 | while [ "$timeout" -gt 0 ]; do 97 | "$@" && return 98 | timeout=$((timeout - 1)) 99 | sleep 1 100 | done 101 | 102 | FAIL "Timed out $*" 103 | } 104 | 105 | # Start collector in background, note: might need sudo! 106 | cap_start() 107 | { 108 | command -v tshark >/dev/null 2>&1 || SKIP 'tshark missing' 109 | if [ $# -gt 1 ]; then 110 | iface=$1 111 | shift 112 | else 113 | iface=lo 114 | fi 115 | port=${1:-514} 116 | 117 | tshark -Qni "$iface" -w "${CAP}" port "$port" 2>/dev/null & 118 | TPID="$!" 119 | echo "$TPID" >> "$DIR/PIDs" 120 | sleep 1 121 | } 122 | 123 | cap_stop() 124 | { 125 | sleep 1 126 | kill -TERM "${TPID}" 127 | wait "${TPID}" 128 | } 129 | 130 | # Optional port argument to remap for syslog dissector 131 | cap_dump() 132 | { 133 | if [ $# -gt 0 ]; then 134 | data="-d udp.port==$1,syslog" 135 | fi 136 | 137 | # shellcheck disable=SC2086 138 | tshark -r "${CAP}" $data -o 'gui.column.format:"N:o","%m","TTL","%Cus:ip.ttl","Source","%us","Destination","%ud","src port","%S","dst port","%D","Info","%i"' 2>/dev/null 139 | } 140 | 141 | cap_find() 142 | { 143 | cap_dump | grep "$@" 144 | } 145 | 146 | # Remap traffic on port to syslog 147 | cap_find_port() 148 | { 149 | port=$1; shift 150 | cap_dump "$port" | grep "$@" 151 | } 152 | 153 | logger() 154 | { 155 | [ -x ../src/logger ] || SKIP 'logger missing' 156 | 157 | if [ $# -gt 1 ] && [ -S "$1" ]; then 158 | sock="$1" 159 | shift 160 | elif [ $# -eq 1 ]; then 161 | sock="${SOCK}" 162 | fi 163 | 164 | if [ -S "$sock" ]; then 165 | ../src/logger -u "$sock" "$@" 166 | else 167 | ../src/logger "$@" 168 | fi 169 | } 170 | 171 | log_and_find() 172 | { 173 | [ $# -gt 1 ] && altsock="$1" && shift 174 | message="$*" 175 | 176 | logger "${altsock}" "$message" 177 | grep "$message" "$LOG" 178 | } 179 | 180 | # Helper to poll for a file with a timeout 181 | poll() 182 | { 183 | file=$1 184 | timeout=${2:-10} # Default timeout 10 seconds 185 | start_time=$(date +%s.%N) 186 | 187 | while [ ! -f "$file" ]; do 188 | sleep 0.1 189 | current_time=$(date +%s.%N) 190 | elapsed=$(echo "$current_time - $start_time" | bc) 191 | if [ "$(echo "$elapsed >= $timeout" | bc)" -eq 1 ]; then 192 | return 1 193 | fi 194 | done 195 | 196 | return 0 197 | } 198 | 199 | # shellcheck disable=SC2002 200 | do_setup() 201 | { 202 | order=$1 203 | pidfn=$2 204 | logfn=${2}.log 205 | shift 2 206 | opts="$*" 207 | 208 | ip link set lo up 209 | 210 | dprint "Starting $order syslogd ..." 211 | cmd="../src/syslogd -dKF ${opts}" 212 | [ -n "$VALGRIND" ] && cmd="${VALGRIND} ${cmd}" 213 | 214 | if [ -z "$DEBUG" ]; then 215 | $cmd >"$logfn" 2>&1 & 216 | else 217 | $cmd & 218 | fi 219 | 220 | if ! poll "${pidfn}"; then 221 | FAIL "Failed starting $order syslogd" 222 | fi 223 | pid=$(cat "${pidfn}" | tee -a "$DIR/PIDs") 224 | dprint "Started as PID $pid" 225 | 226 | # Enable debugging ... 227 | if [ -z "$VALGRIND" ]; then 228 | dprint "Enabling debugging, sending USR1 to $pid ..." 229 | kill -USR1 "$pid" 230 | fi 231 | } 232 | 233 | # stand-alone single syslogd 234 | setup0() 235 | { 236 | addr=$1; shift 237 | 238 | ip link set lo up state up 239 | ip addr add ::1/128 dev lo 2>/dev/null 240 | ip link add eth0 type dummy 241 | ip link set eth0 up state up 242 | ip addr add "$addr" dev eth0 243 | 244 | do_setup "stand-alone" "${PID}" "$*" -f "${CONF}" -p "${SOCK}" -C "${CACHE}" -P "${PID}" 245 | } 246 | 247 | # set up and start primary syslogd 248 | setup() 249 | { 250 | if [ ! -f "${CONF}" ]; then 251 | cat <<-EOF > "${CONF}" 252 | # Local log file, needed by most tests 253 | *.* -${LOG} 254 | include ${CONFD}/*.conf 255 | EOF 256 | fi 257 | 258 | do_setup "primary" "${PID}" "$*" -H -b ":${PORT}" -f "${CONF}" -p "${SOCK}" \ 259 | -p "${ALTSOCK}" -C "${CACHE}" -P "${PID}" 260 | } 261 | 262 | # set up and start second syslogd, e.g., for remote.sh 263 | setup2() 264 | { 265 | if [ ! -f "${CONF2}" ]; then 266 | cat <<-EOF > "${CONF2}" 267 | include ${CONFD2}/*.conf 268 | EOF 269 | fi 270 | 271 | do_setup "secondary" "${PID2}" "$*" -f "${CONF2}" -p "${SOCK2}" \ 272 | -C "${CACHE2}" -P "${PID2}" 273 | } 274 | 275 | is_running() 276 | { 277 | if [ -f "$PID" ]; then 278 | kill -0 "$(cat "$PID")" 279 | else 280 | false 281 | fi 282 | } 283 | 284 | do_reload() 285 | { 286 | # shellcheck disable=SC2046 287 | kill -HUP $(cat "$1") 288 | sleep 1 289 | } 290 | 291 | reload() 292 | { 293 | do_reload "${PID}" 294 | } 295 | 296 | reload2() 297 | { 298 | do_reload "${PID2}" 299 | } 300 | 301 | rotate() 302 | { 303 | kill -USR2 "$(cat "${PID}")" 304 | sleep 1 305 | } 306 | 307 | # Stop all lingering collectors and other tools 308 | kill_pids() 309 | { 310 | # shellcheck disable=SC2162 311 | if [ -f "$DIR/PIDs" ]; then 312 | while read ln; do kill "$ln" 2>/dev/null; done < "$DIR/PIDs" 313 | rm "$DIR/PIDs" 314 | fi 315 | } 316 | 317 | teardown() 318 | { 319 | kill_pids 320 | if [ -z "$DEBUG" ]; then 321 | sleep 1 322 | rm -rf "${DIR}" 323 | else 324 | dprint "In DEBUG mode, not cleaning up log files in $DIR" 325 | fi 326 | } 327 | 328 | signal() 329 | { 330 | echo 331 | if [ "$1" != "EXIT" ]; then 332 | print "Got signal $1, cleaning up ..." 333 | fi 334 | teardown 335 | } 336 | 337 | # props to https://stackoverflow.com/a/2183063/1708249 338 | # shellcheck disable=SC2064 339 | trapit() 340 | { 341 | func=$1; shift 342 | for sig; do 343 | trap "$func $sig" "$sig" 344 | done 345 | } 346 | 347 | # Runs once when including lib.sh 348 | mkdir -p "${CONFD}" 349 | mkdir -p "${CONFD2}" 350 | touch "$DIR/PIDs" 351 | trapit signal INT TERM QUIT EXIT 352 | 353 | # When running tests standalone, not for `make check` 354 | if [ -z "$srcdir" ]; then 355 | export srcdir="." 356 | print "Reexec $0 in an unshare ..." 357 | exec unshare -mrun "$0" 358 | fi 359 | -------------------------------------------------------------------------------- /man/logger.1: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" Copyright (c) 2018-2023 Joachim Wiberg 3 | .\" All rights reserved. 4 | .\" 5 | .\" Redistribution and use in source and binary forms, with or without 6 | .\" modification, are permitted provided that the following conditions 7 | .\" are met: 8 | .\" 1. Redistributions of source code must retain the above copyright 9 | .\" notice, this list of conditions and the following disclaimer. 10 | .\" 2. Redistributions in binary form must reproduce the above copyright 11 | .\" notice, this list of conditions and the following disclaimer in the 12 | .\" documentation and/or other materials provided with the distribution. 13 | .\" 3. Neither the name of the University nor the names of its contributors 14 | .\" may be used to endorse or promote products derived from this software 15 | .\" without specific prior written permission. 16 | .\" 17 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | .\" SUCH DAMAGE. 28 | .Dd Dec 31, 2024 29 | .Dt LOGGER 1 30 | .Os sysklogd 31 | .Sh NAME 32 | .Nm logger 33 | .Nd Send messages to system log daemon, or a log file 34 | .Sh SYNOPSIS 35 | .Nm 36 | .Op Fl 46bchiknsv 37 | .Op Fl d Ar SD 38 | .Op Fl f Ar FILE 39 | .Op Fl h Ar HOST 40 | .Op Fl H Ar HOSTNAME 41 | .Op Fl I Ar PID 42 | .Op Fl m Ar MSGID 43 | .Op Fl o Ar OPTS 44 | .Op Fl p Ar PRIO 45 | .Op Fl P Ar PORT 46 | .Op Fl r Ar SIZE:NUM 47 | .Op Fl t Ar TAG 48 | .Op Fl u Ar SOCK 49 | .Op Ar MESSAGE 50 | .Sh DESCRIPTIOMN 51 | .Nm 52 | can be used to log messages to a local or remote system log daemon, or a 53 | log file, from a UNIX shell, or script. The new 54 | .Xr syslogp 3 55 | API is always used but 56 | .Nm syslogd 57 | is bypassed in the remote log daemon and local log file use-cases. The 58 | log file is also be automatically rotated. See below for log rotation 59 | options. 60 | .Pp 61 | Without a 62 | .Ar MESSAGE 63 | argument, 64 | .Nm 65 | waits for input on 66 | .Ar stdin , 67 | consuming all data until EOF. 68 | .Sh OPTIONS 69 | This program follows the usual UNIX command line syntax: 70 | .Bl -tag -width Ds 71 | .It Fl 4 72 | Force 73 | .Nm 74 | to use IPv4 addresses only. 75 | .It Fl 6 76 | Force 77 | .Nm 78 | to use IPv6 addresses only. 79 | .It Fl b 80 | Use RFC3164 (BSD) style format, default: RFC5424. 81 | .It Fl c 82 | Log to console 83 | .Ql ( LOG_CONS ) 84 | if 85 | .Fn syslog 86 | fails to send message to 87 | .Xr syslogd 8 . 88 | .It Fl d Ar SD 89 | Log this in the structured data (SD) field of an RFC5424 style log 90 | message. See 91 | .Fl m 92 | for caveats. Also, please note that 93 | .Ar sd 94 | has to be passed as one argument and will require careful quoting when 95 | used from the shell. 96 | .It Fl f Ar FILE 97 | Log file to write messages to, instead of syslog daemon. 98 | .Nm 99 | accepts 100 | .Fl f- 101 | as an alias for 102 | .Ar stdout . 103 | .It Fl H Ar hostname 104 | Set the hostname in the header of the message to specified value. 105 | If not specified, the host part of 106 | .Xr gethostname 3 107 | will be used. This is the same as using the special character 108 | .Ql @ 109 | as the 110 | .Ar hostname . 111 | .It Fl h Ar host 112 | Send the message to the remote system 113 | .Ar host 114 | instead of logging it locally. 115 | .It Fl I Ar PID 116 | Like 117 | .Fl i , 118 | but uses 119 | .Ar PID . 120 | Useful when logging from shell scripts that send multiple messages. 121 | E.g., the following arguments might be a useful template: 122 | .Bd -literal -offset indent 123 | logger -t $(basename $0) -I $$ 124 | .Ed 125 | .It Fl i 126 | Log the process id of the logger process with each line 127 | .Ql ( LOG_PID ) . 128 | .It Fl k 129 | Log to kernel 130 | .Pa /dev/kmsg 131 | if 132 | .Pa /dev/log 133 | doesn't exist yet. Only works on Linux systems and only if 134 | .Fl u Ar SOCK 135 | and 136 | .Fl f Ar FILE 137 | are 138 | .Sy not used . 139 | When 140 | .Nm syslogd 141 | eventually starts, it will recognize these messages, due to not having 142 | kernel facility, and log them properly. Highly useful for userspace 143 | scripts and applications running before 144 | .Nm syslogd 145 | has started. E.g., mount helpers and similar. 146 | .It Fl m Ar MSGID 147 | The MSGID used for the message. Requires RFC5424 support in 148 | .Xr syslogd 8 149 | for receiving the message and also for storing it properly in a log file 150 | or sending remote in correctly formatted RFC5424 style. 151 | .It Fl n 152 | Open log file immediately 153 | .Ql ( LOG_NDELAY ) . 154 | .It Fl o Ar OPTS 155 | Set multicast options, separate multiple options with comma: 156 | .Bl -tag 157 | .It Ar iface=IFNAME 158 | Outbound interface when sending to a multicast group address. By 159 | default the kernel relies on the routing table, falling back to the 160 | default route if nothing more specific exists. 161 | .It Ar ttl=<1..255> 162 | IP TTL of outbound syslog messages when sending to a multicast group. 163 | The default TTL for multicast is 1, meaning it is confined to the LAN. 164 | .El 165 | .It Fl P Ar port 166 | Send the message to the specified 167 | .Ar port 168 | number on a remote system, 169 | which can be specified as a service name 170 | or as a decimal number. 171 | The default is 172 | .Dq Li syslog . 173 | If an unknown service name is used, 174 | .Nm 175 | prints a warning and falls back to port 514. 176 | .It Fl p Ar PRIO 177 | Priority, numeric or in 178 | .Ar facility.severity 179 | pair format, default: 180 | .Nm user.notice . 181 | .It Fl r Ar SIZE:NUM 182 | Controls log file rotation. 183 | .Ar SIZE 184 | denotes number of bytes before rotating, default: 200 kB. 185 | .Ar NUM 186 | denotes number of rotated files to keep when logging to a file, default: 187 | 5. 188 | .It Fl s 189 | Log to stderr as well as the system log. 190 | .It Fl t Ar TAG 191 | Log using the specified tag, default: username. 192 | .It Fl u Ar SOCK 193 | Log to UNIX domain socket 194 | .Ar SOCK 195 | instead of the default 196 | .Pa /dev/log . 197 | .It Fl v 198 | Show program version. 199 | .It Ar MESSAGE 200 | Log message to write. Remember to use single/double quotes if calling 201 | .Nm 202 | from a shell prompt due to expansion the shell does. If no message is 203 | given 204 | .Nm 205 | will read from 206 | .Ar stdin 207 | until EOF. In this mode every new row (newline separated) is converted 208 | into an independent 209 | .Xr syslogp 3 210 | call. 211 | .El 212 | .Sh EXAMPLES 213 | .Bd -unfilled -offset left 214 | logger -t dropbear -p auth.notice "Successful login for user 'admin' from 1.2.3.4" 215 | logger -t udhcpc -f /tmp/script.log "New lease 1.2.3.200 obtained for interface eth0" 216 | .Ed 217 | .Sh FILES 218 | .Bl -tag -width /dev/log -compact 219 | .It Ar FILE 220 | If a custom log file is selected, using 221 | .Fl f Ar FILE , 222 | then this file is opened and written to by 223 | .Nm . 224 | When log file rotation is enabled, using 225 | .Fl r Ar SIZE:NUM , 226 | .Nm 227 | creates 228 | .Pa FILE.1 FILE.2 FILE.3.gz 229 | etc. 230 | .It Pa /dev/log 231 | Socket used for communicating with 232 | .Xr syslogd 8 . 233 | When built on BSD 234 | .Pa /var/run/log 235 | is used. 236 | .El 237 | .Sh SEE ALSO 238 | .Xr syslogp 3 239 | .Xr syslogd 8 240 | .Sh AUTHORS 241 | .Nm 242 | was originally written by Joachim Wiberg to be a part of the 243 | .Xr finit 1 244 | system monitor (PID 1), where it is called 245 | .Nm logit . 246 | It is included here to complement 247 | .Xr syslogd 8 248 | and be extended upon in the sysklogd project. 249 | .Sh STANDARDS 250 | The 251 | .Nm 252 | command is expected to be IEEE Std 1003.2 ("POSIX.2") compatible, with 253 | extensions for RFC5424 from NetBSD and custom log file and log file 254 | rotation unique to the sysklogd project. 255 | -------------------------------------------------------------------------------- /src/socket.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (C) 2017-2023 Joachim Wiberg 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "config.h" 32 | 33 | #include 34 | #ifdef HAVE_FCNTL_H 35 | #include 36 | #endif 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include /* if_nametoindex() */ 43 | #include /* IN_MULTICAST, IN6_IS_ADDR_MULTICAST */ 44 | #include 45 | 46 | #include "queue.h" 47 | #include "socket.h" 48 | #include "syslogd.h" 49 | 50 | struct sock { 51 | LIST_ENTRY(sock) link; 52 | 53 | struct addrinfo ai; 54 | int sd; 55 | 56 | void (*cb)(int, void *arg); 57 | void *arg; 58 | }; 59 | 60 | static int max_fdnum = -1; 61 | LIST_HEAD(, sock) sl = LIST_HEAD_INITIALIZER(); 62 | 63 | 64 | int nfds(void) 65 | { 66 | return max_fdnum + 1; 67 | } 68 | 69 | /* 70 | * register socket/fd/pipe created elsewhere, optional callback 71 | */ 72 | int socket_register(int sd, struct addrinfo *ai, void (*cb)(int, void *), void *arg) 73 | { 74 | struct sock *entry = NULL; 75 | 76 | entry = calloc(1, sizeof(*entry)); 77 | if (!entry) 78 | goto err; 79 | 80 | if (ai) { 81 | memcpy(&entry->ai, ai, sizeof(*ai)); 82 | 83 | entry->ai.ai_addr = calloc(1, ai->ai_addrlen); 84 | if (!entry->ai.ai_addr) 85 | goto eaddr; 86 | 87 | memcpy(entry->ai.ai_addr, ai->ai_addr, ai->ai_addrlen); 88 | } 89 | 90 | entry->sd = sd; 91 | entry->cb = cb; 92 | entry->arg = arg; 93 | LIST_INSERT_HEAD(&sl, entry, link); 94 | 95 | /* Keep track for select() */ 96 | if (sd > max_fdnum) 97 | max_fdnum = sd; 98 | 99 | return sd; 100 | eaddr: free(entry); 101 | err: return -1; 102 | } 103 | 104 | static int socket_opts(int sd, int family, int secure) 105 | { 106 | socklen_t len, slen; 107 | int on = 1; 108 | 109 | if (secure) 110 | goto skip; 111 | 112 | /* 113 | * This first one is best-effort only, try to increase receive 114 | * buffer size. Alert user on failure and proceed. 115 | */ 116 | slen = sizeof(len); 117 | if (getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &len, &slen) == 0 && len < RCVBUF_MINSIZE) { 118 | len = RCVBUF_MINSIZE; 119 | if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len))) 120 | ERR("Failed increasing size of socket receive buffer"); 121 | } 122 | 123 | skip: switch (family) { 124 | case AF_INET6: 125 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { 126 | ERR("setsockopt (IPV6_ONLY), suspending IPv6"); 127 | return -1; 128 | } 129 | /* fallthrough */ 130 | case AF_INET: 131 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { 132 | ERR("setsockopt(REUSEADDR), suspending inet"); 133 | return -1; 134 | } 135 | #ifdef SO_REUSEPORT 136 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { 137 | ERR("setsockopt(REUSEPORT), suspending inet"); 138 | } 139 | #endif 140 | break; 141 | } 142 | 143 | return 0; 144 | } 145 | 146 | static int is_multicast(struct addrinfo *ai) 147 | { 148 | if (ai->ai_family == AF_INET) { 149 | struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; 150 | 151 | if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) 152 | return 1; 153 | } else if (ai->ai_family == AF_INET6) { 154 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; 155 | 156 | if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 157 | return 1; 158 | } 159 | 160 | return 0; 161 | } 162 | 163 | /* 164 | * Check if IP address actually is a multiast group, then join it so 165 | * the kernel stops blocking the traffic. 166 | */ 167 | static int join_group(int sd, struct addrinfo *ai, char *iface) 168 | { 169 | struct group_req gr = { 0 }; 170 | unsigned int ifindex = 0; 171 | int proto = -1; 172 | 173 | if (!is_multicast(ai)) 174 | return 0; 175 | 176 | if (iface && (ifindex = if_nametoindex(iface)) == 0) 177 | return -1; 178 | 179 | gr.gr_interface = ifindex; 180 | if (ai->ai_family == AF_INET) { 181 | struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; 182 | 183 | proto = IPPROTO_IP; 184 | memcpy(&gr.gr_group, sin, sizeof(*sin)); 185 | } else if (ai->ai_family == AF_INET6) { 186 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; 187 | 188 | proto = IPPROTO_IPV6; 189 | memcpy(&gr.gr_group, sin6, sizeof(*sin6)); 190 | } 191 | 192 | /* Likely AF_UNIX, or a unicast address, skip join */ 193 | if (proto == -1) 194 | return 0; 195 | 196 | return setsockopt(sd, proto, MCAST_JOIN_GROUP, &gr, sizeof(gr)); 197 | } 198 | 199 | /* 200 | * create socket, with optional callback for reading inbound data 201 | */ 202 | int socket_create(struct addrinfo *ai, char *iface, void (*cb)(int, void *), void *arg) 203 | { 204 | struct sockaddr_un *sun = (struct sockaddr_un *)ai->ai_addr; 205 | mode_t mode = ai->ai_protocol; 206 | int secure = ai->ai_flags & AI_SECURE; 207 | int type = ai->ai_socktype | SOCK_CLOEXEC | SOCK_NONBLOCK; 208 | int sd; 209 | 210 | if (ai->ai_family == AF_UNIX) { 211 | (void)unlink(sun->sun_path); 212 | ai->ai_protocol = 0; 213 | } 214 | 215 | sd = socket(ai->ai_family, type, ai->ai_protocol); 216 | if (sd < 0) 217 | return -1; 218 | 219 | if (socket_opts(sd, ai->ai_family, secure)) 220 | goto err; 221 | 222 | if (secure) 223 | goto skip; 224 | 225 | if (join_group(sd, ai, iface) < 0) 226 | goto err; 227 | 228 | if (bind(sd, ai->ai_addr, ai->ai_addrlen) < 0) 229 | goto err; 230 | 231 | skip: if (ai->ai_family == AF_UNIX) { 232 | if (chmod(sun->sun_path, mode) < 0) 233 | goto err; 234 | } 235 | 236 | if (socket_register(sd, ai, cb, arg) < 0) 237 | goto err; 238 | 239 | return sd; 240 | err: close(sd); 241 | return -1; 242 | } 243 | 244 | int socket_close(int sd) 245 | { 246 | struct sockaddr_un *sun; 247 | struct sock *entry, *tmp; 248 | 249 | LIST_FOREACH_SAFE(entry, &sl, link, tmp) { 250 | if (entry->sd != sd) 251 | continue; 252 | 253 | LIST_REMOVE(entry, link); 254 | close(entry->sd); 255 | if (entry->ai.ai_family == AF_UNIX) { 256 | sun = (struct sockaddr_un *)entry->ai.ai_addr; 257 | (void)unlink(sun->sun_path); 258 | } 259 | free(entry->ai.ai_addr); 260 | free(entry); 261 | 262 | return 0; 263 | } 264 | 265 | errno = ENOENT; 266 | return -1; 267 | } 268 | 269 | /* Set multicast forwarding parameters if fwd address is multicast */ 270 | int socket_mcast(int sd, struct addrinfo *ai, char *iface, int ttl) 271 | { 272 | struct ip_mreqn imr = { 0 }; 273 | int idx = 0; 274 | int rc = 0; 275 | 276 | if (!is_multicast(ai)) 277 | return 0; 278 | 279 | if (iface) { 280 | idx = if_nametoindex(iface); 281 | if (idx == 0) 282 | return -1; 283 | } 284 | 285 | /* Sanity check, also ensures we set a TTL */ 286 | if (ttl <= 0 || ttl > 255) 287 | ttl = 1; 288 | 289 | switch (ai->ai_family) { 290 | case AF_INET: 291 | imr.imr_ifindex = idx; 292 | rc += setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr)); 293 | rc += setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); 294 | break; 295 | case AF_INET6: 296 | rc += setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx)); 297 | rc += setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); 298 | break; 299 | } 300 | 301 | return rc; 302 | } 303 | 304 | int socket_ffs(int family) 305 | { 306 | struct sock *entry; 307 | 308 | LIST_FOREACH(entry, &sl, link) { 309 | if (entry->ai.ai_family == family) 310 | return entry->sd; 311 | } 312 | 313 | errno = ENONET; 314 | return -1; 315 | } 316 | 317 | int socket_poll(struct timeval *timeout) 318 | { 319 | int num; 320 | fd_set fds; 321 | struct sock *entry; 322 | 323 | FD_ZERO(&fds); 324 | LIST_FOREACH(entry, &sl, link) 325 | FD_SET(entry->sd, &fds); 326 | 327 | num = select(nfds(), &fds, NULL, NULL, timeout); 328 | if (num <= 0) { 329 | /* Log all errors, except when signalled, ignore failures. */ 330 | if (num < 0 && EINTR != errno) 331 | WARN("Failed select(): %s", strerror(errno)); 332 | 333 | return num; 334 | } 335 | 336 | LIST_FOREACH(entry, &sl, link) { 337 | if (!FD_ISSET(entry->sd, &fds)) 338 | continue; 339 | 340 | if (entry->cb) 341 | entry->cb(entry->sd, entry->arg); 342 | } 343 | 344 | return num; 345 | } 346 | 347 | /** 348 | * Local Variables: 349 | * indent-tabs-mode: t 350 | * c-file-style: "linux" 351 | * End: 352 | */ 353 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | .--. .--. .--. 3 | .-----.--.--.-----| |--| :-----.-----.--| | 4 | |__ --| | |__ --| <| | _ | _ | _ | RFC3164 :: syslogd for Linux 5 | |_____|___ |_____|__|__|__|_____|___ |_____| RFC5424 :: w/NetBSD syslogp() 6 | |_____| |_____| 7 | 8 | <23>Aug 24 05:14:15 192.0.2.1 myproc[8710]: Kilroy was here. 9 | <23>1 2019-11-04T00:50:15.001234+01:00 troglobit myproc 8710 - - Kilroy was here. 10 | ``` 11 | [![BSD Badge][]][BSD License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan] 12 | 13 | Table of Contents 14 | ----------------- 15 | 16 | * [Introduction](#introduction) 17 | * [Using -lsyslog](#using--lsyslog) 18 | * [Build & Install](#build--install) 19 | * [Building from GIT](#building-from-git) 20 | * [Origin & References](#origin--references) 21 | 22 | > **Tip:** the Gentoo project has a very nice article detailing sysklogd 23 | > ➤ 24 | 25 | 26 | Introduction 27 | ------------ 28 | 29 | This is the continuation of the original Debian/Ubuntu syslog daemon, 30 | updated with full [RFC3164][] and [RFC5424][] support from NetBSD and 31 | FreeBSD. The package includes the `libsyslog.{a,so}` library with a 32 | `syslog.h` header replacement, the `syslogd` daemon, and a command 33 | line tool called `logger`. 34 | 35 | - https://man.troglobit.com/man1/logger.1.html 36 | - https://man.troglobit.com/man8/syslogd.8.html 37 | - https://man.troglobit.com/man5/syslog.conf.5.html 38 | 39 | `libsyslog` and `syslog/syslog.h`, derived directly from NetBSD, expose 40 | `syslogp()` and other new features available only in [RFC5424][]: 41 | 42 | - https://man.troglobit.com/man3/syslogp.3.html 43 | - https://netbsd.gw.com/cgi-bin/man-cgi?syslog+3+NetBSD-current 44 | 45 | The `syslogd` daemon is an enhanced version of the standard Berkeley 46 | utility program, updated with DNA from FreeBSD. It provides logging of 47 | messages received from the kernel, programs and facilities on the local 48 | host as well as messages from remote hosts. Although fully compatible 49 | with standard C-library implementations of the `syslog()` API (GLIBC, 50 | musl libc, uClibc), `libsyslog` must be used in your application to 51 | unlock the new [RFC5424][] `syslogp()` API. 52 | 53 | The included `logger` tool is primarily made for use with sysklogd, but 54 | can be used stand-alone too. It is not command line compatible with the 55 | "standard" Linux logger tool from the bsdutils project. Instead it is 56 | compatible with the actual BSD logger tool(s) -- only major difference 57 | is its support for `-I PID`, similar to the bsdutils `--id=PID`. The 58 | `logger` tool can be used from the command line, or script, to send both 59 | RFC5424 (default) and old-style (BSD) RFC3164 formatted messages using 60 | `libsyslog` to `syslogd` for local processing, or to a remote server. 61 | 62 | Main differences from the original sysklogd package are: 63 | 64 | - The separate `klogd` daemon is no longer part of the sysklogd project, 65 | syslogd now natively supports logging kernel messages as well 66 | - *Major* command line changes to `syslogd`, for compatibility with *BSD 67 | - Supports `include /etc/syslog.d/*.conf` directive, see example .conf 68 | - Built-in log-rotation support, with compression by default, useful for 69 | embedded systems. No need for cron and/or a separate log rotate daemon 70 | - Full [RFC3164][] and [RFC5424][] support from NetBSD and FreeBSD 71 | - Support for sending RFC3164 style remote syslog messages, including 72 | timestamp and hostname. Defaults to send w/o for compatibility 73 | - Support for sending RFC5424 style remote syslog messages 74 | - Support for sending messages to a custom port on a remote server 75 | - Support for listening to a custom port 76 | - Support for remote peer filtering, from FreeBSD 77 | - Support for disabling DNS reverse lookups for each remote log message 78 | - Support for FreeBSD Secure Mode, remote logging enabled by default(!) 79 | - Support for FreeBSD style property based filtering. Filter messages 80 | using host or program name, regexp, substring match, and more! 81 | - Support for remote logging to a multicast group, as well as acting as 82 | a multicast group receiver of syslog messages, both IPv4 and IPv6 83 | - Includes a fit for purpose `logger` tool, compatible with `syslogd`, 84 | leveraging the full RFC5424 capabilities (`msgid` etc.) 85 | - Includes a syslog library and system header replacement for logging 86 | - FreeBSD socket receive buffer size patch 87 | - Avoid blocking `syslogd` if console is backed up 88 | - Touch PID file on `SIGHUP`, for integration with [Finit][] 89 | - GNU configure & build system to ease porting/cross-compiling 90 | - Support for configuring remote syslog timeout 91 | 92 | Please file bug reports, or send pull requests for bug fixes and/or 93 | proposed extensions at [GitHub][Home]. 94 | 95 | 96 | Using -lsyslog 97 | -------------- 98 | 99 | libsyslog is by default installed as a library with a header file: 100 | 101 | ```C 102 | #include 103 | ``` 104 | 105 | The output from the `pkg-config` tool holds no surprises: 106 | 107 | ```sh 108 | $ pkg-config --libs --static --cflags libsyslog 109 | -I/usr/local/include -L/usr/local/lib -lsyslog 110 | ``` 111 | 112 | The prefix path `/usr/local/` shown here is only the default. Use the 113 | `configure` script to select a different prefix when installing libsyslog. 114 | 115 | For GNU autotools based projects, instead of issuing the `pkg-config` 116 | command manually, use the following in `configure.ac`: 117 | 118 | ```sh 119 | # Check for required libraries 120 | PKG_CHECK_MODULES([syslog], [libsyslog >= 2.0]) 121 | ``` 122 | 123 | and for your "proggy" in `Makefile.am`: 124 | 125 | ```sh 126 | proggy_CFLAGS = $(syslog_CFLAGS) 127 | proggy_LDADD = $(syslog_LIBS) 128 | ``` 129 | 130 | The distribution comes with an [example][] program that utilizes the 131 | NetBSD API and links against libsyslog. 132 | 133 | 134 | Build & Install 135 | --------------- 136 | 137 | The GNU Configure & Build system use `/usr/local` as the default install 138 | prefix. In many cases this is useful, but this means the configuration 139 | files and cache files will also use that same prefix. Most users have 140 | come to expect those files in `/etc/` and `/var/run/` and configure has 141 | a few useful options that are recommended to use: 142 | 143 | ```sh 144 | ./configure --prefix=/usr --sysconfdir=/etc --runstatedir=/run 145 | make -j5 146 | sudo make install-strip 147 | ``` 148 | 149 | You may want to remove the `--prefix=/usr` option. Most users prefer 150 | non-distro binaries in `/usr/local` or `/opt`. 151 | 152 | > **Note:** the `--runstatedir` option should point to a filesystem 153 | > that is cleaned at reboot. syslogd relies on this for 154 | > its `syslogd.cache` file, which keeps track of the last 155 | > read kernel log message from `/dev/kmsg`. 156 | 157 | 158 | Building from GIT 159 | ----------------- 160 | 161 | If you want to contribute, or just try out the latest but unreleased 162 | features, then you need to know a few things about the [GNU build 163 | system][buildsystem]: 164 | 165 | - `configure.ac` and a per-directory `Makefile.am` are key files 166 | - `configure` and `Makefile.in` are generated from `autogen.sh`, 167 | they are not stored in GIT but automatically generated for the 168 | release tarballs 169 | - `Makefile` is generated by `configure` script 170 | 171 | To build from GIT you first need to clone the repository and run the 172 | `autogen.sh` script. This requires `automake` and `autoconf` to be 173 | installed on your system. 174 | 175 | ```sh 176 | git clone https://github.com/troglobit/sysklogd.git 177 | cd sysklogd/ 178 | ./autogen.sh 179 | ./configure && make 180 | ``` 181 | 182 | GIT sources are a moving target and are not recommended for production 183 | systems, unless you know what you are doing! 184 | 185 | **Note:** some systems may have an older, or a vanilla, version of the 186 | GNU autoconf package that does not support `--runstatedir` (above). 187 | Users on such systems are recommended to use `--localstatedir`, the 188 | `$runstatedir` used by sysklogd is derived from that if missing. 189 | 190 | 191 | Origin & References 192 | ------------------- 193 | 194 | This is the continuation of the original sysklogd by Dr. G.W. Wettstein 195 | and [Martin Schulze][]. Currently maintained, and almost completely 196 | rewritten by [Joachim Wiberg][], who spliced in fresh DNA strands from 197 | the NetBSD and FreeBSD projects. Much of the code base is NetBSD, but 198 | the command line interface is FreeBSD. 199 | 200 | > **Note:** the project name remains `sysklogd`, which was a combination 201 | > of the names of the two main daemons, `syslogd` and `klogd`. However, 202 | > since v2.0 `klogd` no longer exists, kernel logging is now native to 203 | > `syslogd`. 204 | 205 | The project was previously licensed under the GNU GPL, but since the 206 | removal of `klogd`, man pages, and resync with the BSDs the project is 207 | now [3-clause BSD][BSD License] licensed. 208 | 209 | [RFC3164]: https://tools.ietf.org/html/rfc3164 210 | [RFC5424]: https://tools.ietf.org/html/rfc5424 211 | [Martin Schulze]: http://www.infodrom.org/projects/sysklogd/ 212 | [Joachim Wiberg]: https://troglobit.com 213 | [Finit]: https://github.com/troglobit/finit 214 | [Home]: https://github.com/troglobit/sysklogd 215 | [example]: https://github.com/troglobit/sysklogd/tree/master/example 216 | [buildsystem]: https://airs.com/ian/configure/ 217 | [BSD License]: https://en.wikipedia.org/wiki/BSD_licenses 218 | [BSD Badge]: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg 219 | [GitHub]: https://github.com/troglobit/sysklogd/actions/workflows/build.yml/ 220 | [GitHub Status]: https://github.com/troglobit/sysklogd/actions/workflows/build.yml/badge.svg 221 | [Coverity Scan]: https://scan.coverity.com/projects/19540 222 | [Coverity Status]: https://scan.coverity.com/projects/19540/badge.svg 223 | -------------------------------------------------------------------------------- /src/syslog.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: syslog.h,v 1.34.8.3 2017/12/03 11:39:21 jdolecek Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1982, 1986, 1988, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)syslog.h 8.1 (Berkeley) 6/2/93 32 | */ 33 | 34 | #ifndef _SYS_SYSLOG_H_ /* From NetBSD, for co-existance with C-library header */ 35 | #define _SYS_SYSLOG_H_ 36 | 37 | #include 38 | 39 | /* 40 | * Default on *BSD is /var/run/log, but on Linux systems with systemd 41 | * (journald) this is reserved and may already exist as a directory. 42 | * For compatibility with GLIBC syslog API, for those who opt not to 43 | * use this replacement API, we use the default/traditional Linux path 44 | * /dev/log in the sysklogd project. 45 | */ 46 | #ifndef __linux__ 47 | #define _PATH_LOG "/var/run/log" 48 | #else 49 | #define _PATH_LOG "/dev/log" 50 | #endif 51 | 52 | /* 53 | * priorities/facilities are encoded into a single 32-bit quantity, where the 54 | * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility 55 | * (0-big number). Both the priorities and the facilities map roughly 56 | * one-to-one to strings in the syslogd(8) source code. This mapping is 57 | * included in this file. 58 | * 59 | * priorities (these are ordered) 60 | */ 61 | #define LOG_EMERG 0 /* system is unusable */ 62 | #define LOG_ALERT 1 /* action must be taken immediately */ 63 | #define LOG_CRIT 2 /* critical conditions */ 64 | #define LOG_ERR 3 /* error conditions */ 65 | #define LOG_WARN 4 /* warning conditions, alias */ 66 | #define LOG_WARNING 4 /* warning conditions */ 67 | #define LOG_NOTICE 5 /* normal but significant condition */ 68 | #define LOG_INFO 6 /* informational */ 69 | #define LOG_DEBUG 7 /* debug-level messages */ 70 | 71 | #define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */ 72 | /* extract priority */ 73 | #define LOG_PRI(p) ((p) & LOG_PRIMASK) 74 | #define LOG_MAKEPRI(fac, pri) ((fac) | (pri)) 75 | 76 | #ifdef SYSLOG_NAMES 77 | #define INTERNAL_INVPRI 0x00 /* Value to indicate no priority in f_pmask */ 78 | #define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ 79 | /* mark "facility" */ 80 | #define INTERNAL_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */ 81 | #define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES << 3, 0) 82 | #undef CODE 83 | typedef struct _code { 84 | const char *c_name; 85 | int c_val; 86 | } CODE; 87 | 88 | CODE prioritynames[] = { 89 | { "alert", LOG_ALERT }, 90 | { "crit", LOG_CRIT }, 91 | { "debug", LOG_DEBUG }, 92 | { "emerg", LOG_EMERG }, 93 | { "err", LOG_ERR }, 94 | { "error", LOG_ERR }, /* DEPRECATED */ 95 | { "info", LOG_INFO }, 96 | { "none", INTERNAL_NOPRI }, /* INTERNAL */ 97 | { "notice", LOG_NOTICE }, 98 | { "panic", LOG_EMERG }, /* DEPRECATED */ 99 | { "warn", LOG_WARNING }, /* DEPRECATED */ 100 | { "warning", LOG_WARNING }, 101 | { "*", INTERNAL_ALLPRI }, /* INTERNAL */ 102 | { NULL, -1 } 103 | }; 104 | #endif /* SYSLOG_NAMES */ 105 | 106 | /* facility codes */ 107 | #define LOG_KERN (0<<3) /* kernel messages */ 108 | #define LOG_USER (1<<3) /* random user-level messages */ 109 | #define LOG_MAIL (2<<3) /* mail system */ 110 | #define LOG_DAEMON (3<<3) /* system daemons */ 111 | #define LOG_AUTH (4<<3) /* security/authorization messages */ 112 | #define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ 113 | #define LOG_LPR (6<<3) /* line printer subsystem */ 114 | #define LOG_NEWS (7<<3) /* network news subsystem */ 115 | #define LOG_UUCP (8<<3) /* UUCP subsystem */ 116 | #define LOG_CRON (9<<3) /* clock daemon */ 117 | #define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ 118 | #define LOG_FTP (11<<3) /* ftp daemon */ 119 | #define LOG_NTP (12<<3) /* NTP subsystem */ 120 | #define LOG_SECURITY (13<<3) /* Log audit, for audit trails */ 121 | #define LOG_AUDIT LOG_SECURITY /* Alias for RFC5424 compat. */ 122 | #define LOG_CONSOLE (14<<3) /* Log alert */ 123 | #define LOG_CRON_SOL (15<<3) /* clock daemon (Solaris) */ 124 | #define LOG_CRON2 LOG_CRON_SOL /* Alias for RFC5424 compat. */ 125 | #define LOG_LOCAL0 (16<<3) /* reserved for local use */ 126 | #define LOG_LOCAL1 (17<<3) /* reserved for local use */ 127 | #define LOG_LOCAL2 (18<<3) /* reserved for local use */ 128 | #define LOG_LOCAL3 (19<<3) /* reserved for local use */ 129 | #define LOG_LOCAL4 (20<<3) /* reserved for local use */ 130 | #define LOG_LOCAL5 (21<<3) /* reserved for local use */ 131 | #define LOG_LOCAL6 (22<<3) /* reserved for local use */ 132 | #define LOG_LOCAL7 (23<<3) /* reserved for local use */ 133 | 134 | #define LOG_NFACILITIES 24 /* current number of facilities */ 135 | #define LOG_FACMASK 0x03f8 /* mask to extract facility part */ 136 | /* facility of pri */ 137 | #define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) 138 | 139 | #ifdef SYSLOG_NAMES 140 | CODE facilitynames[] = { 141 | { "auth", LOG_AUTH }, 142 | { "authpriv", LOG_AUTHPRIV }, 143 | { "console", LOG_CONSOLE }, 144 | { "cron", LOG_CRON }, 145 | { "cron_sol", LOG_CRON_SOL }, /* Solaris cron */ 146 | { "cron2", LOG_CRON2 }, 147 | { "daemon", LOG_DAEMON }, 148 | { "ftp", LOG_FTP }, 149 | { "kern", LOG_KERN }, 150 | { "lpr", LOG_LPR }, 151 | { "mail", LOG_MAIL }, 152 | { "mark", INTERNAL_MARK }, /* INTERNAL */ 153 | { "news", LOG_NEWS }, 154 | { "ntp", LOG_NTP }, 155 | { "security", LOG_SECURITY }, 156 | { "audit", LOG_AUDIT }, 157 | { "syslog", LOG_SYSLOG }, 158 | { "user", LOG_USER }, 159 | { "uucp", LOG_UUCP }, 160 | { "local0", LOG_LOCAL0 }, 161 | { "local1", LOG_LOCAL1 }, 162 | { "local2", LOG_LOCAL2 }, 163 | { "local3", LOG_LOCAL3 }, 164 | { "local4", LOG_LOCAL4 }, 165 | { "local5", LOG_LOCAL5 }, 166 | { "local6", LOG_LOCAL6 }, 167 | { "local7", LOG_LOCAL7 }, 168 | { NULL, -1 } 169 | }; 170 | #endif /* SYSLOG_NAMES */ 171 | 172 | #ifdef __KERNEL__ 173 | #define LOG_PRINTF -1 /* pseudo-priority to indicate use of printf */ 174 | #endif 175 | 176 | /* 177 | * arguments to setlogmask. 178 | */ 179 | #define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */ 180 | #define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */ 181 | 182 | /* 183 | * Option flags for openlog. 184 | * 185 | * LOG_ODELAY no longer does anything. 186 | * LOG_NDELAY is the inverse of what it used to be. 187 | */ 188 | #define LOG_PID 0x001 /* log the pid with each message */ 189 | #define LOG_CONS 0x002 /* log on the console if errors in sending */ 190 | #define LOG_ODELAY 0x004 /* delay open until first syslog() (default) */ 191 | #define LOG_NDELAY 0x008 /* don't delay open */ 192 | #define LOG_NOWAIT 0x010 /* don't wait for console forks: DEPRECATED */ 193 | #define LOG_PERROR 0x020 /* log to stderr as well */ 194 | #define LOG_PTRIM 0x040 /* trim anything syslog added when writing to stderr */ 195 | #define LOG_NLOG 0x080 /* don't write to the system log */ 196 | #define LOG_STDOUT 0x100 /* like nlog, for debugging syslogp() API */ 197 | #define LOG_RFC3164 0x200 /* Log to remote/ipc socket in old BSD format */ 198 | 199 | #ifndef __KERNEL__ 200 | 201 | /* Used by reentrant functions */ 202 | 203 | struct syslog_data { 204 | int log_version; 205 | int log_file; 206 | int log_connected; 207 | int log_opened; 208 | int log_stat; 209 | const char *log_tag; 210 | const char *log_sockpath; /* Path to socket */ 211 | char log_hostname[256]; /* MAXHOSTNAMELEN */ 212 | int log_fac; 213 | int log_mask; 214 | void *log_host; /* struct sockaddr* */ 215 | int log_pid; 216 | char *log_iface; /* Multicast interface */ 217 | int log_ttl; /* Multicast TTL */ 218 | }; 219 | 220 | #define SYSLOG_DATA_INIT { \ 221 | .log_version = 1, \ 222 | .log_file = -1, \ 223 | .log_connected = 0, \ 224 | .log_opened = 0, \ 225 | .log_stat = 0, \ 226 | .log_tag = NULL, \ 227 | .log_sockpath = NULL, \ 228 | .log_hostname = { '\0' }, \ 229 | .log_fac = LOG_USER, \ 230 | .log_mask = 0xff, \ 231 | .log_host = NULL, \ 232 | .log_pid = -1, \ 233 | .log_iface = NULL, \ 234 | .log_ttl = 1, \ 235 | } 236 | 237 | #ifdef __cplusplus 238 | extern "C" { 239 | #endif 240 | void openlog (const char *, int, int); 241 | void closelog (void); 242 | 243 | int setlogmask (int); 244 | 245 | void syslog (int, const char *, ...); 246 | void vsyslog (int, const char *, va_list); 247 | 248 | void syslogp (int, const char *, const char *, const char *, ...); 249 | void vsyslogp (int, const char *, const char *, const char *, va_list); 250 | 251 | void openlog_r (const char *, int, int, struct syslog_data *); 252 | void closelog_r (struct syslog_data *); 253 | 254 | int setlogmask_r (int, struct syslog_data *); 255 | 256 | void syslog_r (int, struct syslog_data *, const char *, ...); 257 | void vsyslog_r (int, struct syslog_data *, const char *, va_list); 258 | 259 | void syslogp_r (int, struct syslog_data *, const char *, const char *, 260 | const char *, ...); 261 | void vsyslogp_r (int, struct syslog_data *, const char *, const char *, 262 | const char *, va_list); 263 | #ifdef __cplusplus 264 | } 265 | #endif 266 | 267 | #else /* !__KERNEL__ */ 268 | 269 | void logpri(int); 270 | void log(int, const char *, ...); 271 | void vlog(int, const char *, va_list); 272 | void addlog(const char *, ...); 273 | void logwakeup(void); 274 | 275 | #endif /* !__KERNEL__ */ 276 | 277 | #endif /* !_SYS_SYSLOG_H_ */ 278 | -------------------------------------------------------------------------------- /src/syslogd.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1983, 1988, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SYSKLOGD_SYSLOGD_H_ 33 | #define SYSKLOGD_SYSLOGD_H_ 34 | 35 | #include "config.h" 36 | 37 | #include 38 | #include 39 | #include /* struct addrinfo */ 40 | #include 41 | #include 42 | #ifdef __linux__ 43 | #include 44 | #endif 45 | #include /* MAXHOSTNAMELEN */ 46 | #include 47 | #include 48 | #include 49 | #include /* struct sockaddr_un */ 50 | 51 | #include "queue.h" 52 | #include "syslog.h" 53 | 54 | #ifndef MAXLINE 55 | #define MAXLINE 2048 /* maximum line length */ 56 | #endif 57 | #define MAXSVLINE MAXLINE /* maximum saved line length */ 58 | #define DEFUPRI (LOG_USER | LOG_NOTICE) 59 | #define DEFSPRI (LOG_KERN | LOG_CRIT) 60 | #define TIMERINTVL 30 /* interval for checking flush/nslookup */ 61 | #define RCVBUF_MINSIZE (80 * 1024) /* minimum size of dgram rcv buffer */ 62 | 63 | /* 64 | * Linux uses EIO instead of EBADFD (mrn 12 May 96) 65 | */ 66 | #ifdef __linux__ 67 | #define EHANGUP EIO 68 | #else 69 | #define EHANGUP EBADFD 70 | #endif 71 | 72 | #ifndef UTMP_FILE 73 | #ifdef UTMP_FILENAME 74 | #define UTMP_FILE UTMP_FILENAME 75 | #else 76 | #ifdef _PATH_UTMP 77 | #define UTMP_FILE _PATH_UTMP 78 | #else 79 | #define UTMP_FILE "/etc/utmp" 80 | #endif 81 | #endif 82 | #endif 83 | 84 | #ifndef _PATH_LOGCONF 85 | #define _PATH_LOGCONF SYSCONFDIR "/syslog.conf" 86 | #endif 87 | 88 | #ifndef _PATH_LOGPID 89 | #define _PATH_LOGPID RUNSTATEDIR "/syslogd.pid" 90 | #endif 91 | 92 | #ifndef _PATH_CACHE 93 | #define _PATH_CACHE RUNSTATEDIR "/syslogd.cache" 94 | #endif 95 | 96 | #ifndef _PATH_DEV 97 | #define _PATH_DEV "/dev/" 98 | #endif 99 | 100 | #ifndef _PATH_CONSOLE 101 | #define _PATH_CONSOLE "/dev/console" 102 | #endif 103 | 104 | #ifndef _PATH_TTY 105 | #define _PATH_TTY "/dev/tty" 106 | #endif 107 | 108 | #ifndef _PATH_LOG 109 | #define _PATH_LOG "/dev/log" 110 | #endif 111 | 112 | #ifndef _PATH_KLOG 113 | #define _PATH_KLOG "/proc/kmsg" 114 | #endif 115 | 116 | #ifdef UT_NAMESIZE 117 | #define UNAMESZ UT_NAMESIZE /* length of a login name */ 118 | #else 119 | #define UNAMESZ 8 /* length of a login name */ 120 | #endif 121 | #define MAXUNAMES 20 /* maximum number of user names */ 122 | #define MAXFNAME 200 /* max file pathname length */ 123 | 124 | #ifndef INET_DNS_DELAY 125 | #define INET_DNS_DELAY 60 126 | #endif 127 | 128 | #ifndef INET_SUSPEND_TIME 129 | #define INET_SUSPEND_TIME 180 /* equal to 3 minutes */ 130 | #endif 131 | 132 | #define LIST_DELIMITER ':' /* delimiter between two hosts */ 133 | 134 | #define AI_SECURE 0x8000 /* Tell socket_create() to not bind() */ 135 | 136 | #define O_CREATE O_WRONLY | O_APPEND | O_CREAT 137 | 138 | /* From The Practice of Programming, by Kernighan and Pike */ 139 | #ifndef NELEMS 140 | #define NELEMS(array) (sizeof(array) / sizeof(array[0])) 141 | #endif 142 | 143 | /* Stringification macros, see signal_init() for an example */ 144 | #define xstr(s) str(s) 145 | #define str(s) #s 146 | 147 | /* Helper internal log macros */ 148 | #define ERR(fmt, args...) flog(LOG_SYSLOG | LOG_ERR, fmt ": %s", ##args, strerror(errno)) 149 | #define ERRX(fmt, args...) flog(LOG_SYSLOG | LOG_ERR, fmt, ##args) 150 | #define WARN(fmt, args...) flog(LOG_SYSLOG | LOG_WARN, fmt, ##args) 151 | #define NOTE(fmt, args...) flog(LOG_SYSLOG | LOG_NOTICE, fmt, ##args) 152 | #define INFO(fmt, args...) flog(LOG_SYSLOG | LOG_INFO, fmt, ##args) 153 | 154 | /* 155 | * Help macros to convert between sockaddr types 156 | */ 157 | #define sstosa(ss) ((struct sockaddr *)(ss)) 158 | #define sstosin(ss) ((struct sockaddr_in *)(void *)(ss)) 159 | #define satosin(sa) ((struct sockaddr_in *)(void *)(sa)) 160 | #define sstosin6(ss) ((struct sockaddr_in6 *)(void *)(ss)) 161 | #define satosin6(sa) ((struct sockaddr_in6 *)(void *)(sa)) 162 | #ifndef s6_addr32 163 | #define s6_addr32 __u6_addr.__u6_addr32 164 | #endif 165 | #define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ 166 | (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ 167 | (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ 168 | (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ 169 | (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) 170 | 171 | /* 172 | * klogctl(2) commands on Linux to control kernel logging to console. 173 | */ 174 | #define SYSLOG_ACTION_CONSOLE_OFF 6 175 | #define SYSLOG_ACTION_CONSOLE_ON 7 176 | 177 | #ifdef __linux__ 178 | #define kern_console_off() if (!KeepKernConsole) klogctl(SYSLOG_ACTION_CONSOLE_OFF, NULL, 0) 179 | #define kern_console_on() if (!KeepKernConsole) klogctl(SYSLOG_ACTION_CONSOLE_ON, NULL, 0) 180 | #else 181 | #define kern_console_off() do { } while (0) 182 | #define kern_console_on() do { } while (0) 183 | #endif 184 | 185 | /* 186 | * Flags to logmsg(). 187 | */ 188 | #define IGN_CONS 0x001 /* don't print on console */ 189 | #define SYNC_FILE 0x002 /* do fsync on file after printing */ 190 | #define ADDDATE 0x004 /* add a date to the message */ 191 | #define MARK 0x008 /* this message is a mark */ 192 | #define RFC3164 0x010 /* format log message according to RFC 3164 */ 193 | #define RFC5424 0x020 /* format log message according to RFC 5424 */ 194 | 195 | /* Syslog timestamp formats. */ 196 | #define BSDFMT_DATELEN 0 197 | #define BSDFMT_DATEFMT NULL 198 | 199 | #define RFC3164_DATELEN 15 200 | #define RFC3164_DATEFMT "%b %e %H:%M:%S" 201 | 202 | #define RFC5424_DATELEN 32 203 | #define RFC5424_DATEFMT "%FT%T.______%z" 204 | 205 | /* 206 | * Helper macros for "message repeated" messages 207 | */ 208 | #define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1) 209 | #define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) 210 | #define BACKOFF(f) \ 211 | if (++(f)->f_repeatcount > MAXREPEAT) \ 212 | (f)->f_repeatcount = MAXREPEAT; 213 | 214 | /* values for f_type */ 215 | #define F_UNUSED 0 /* unused entry */ 216 | #define F_FILE 1 /* regular file */ 217 | #define F_TTY 2 /* terminal */ 218 | #define F_CONSOLE 3 /* console terminal */ 219 | #define F_FORW 4 /* remote machine */ 220 | #define F_USERS 5 /* list of users */ 221 | #define F_WALL 6 /* everyone logged on */ 222 | #define F_FORW_SUSP 7 /* suspended host forwarding */ 223 | #define F_FORW_UNKN 8 /* unknown host forwarding */ 224 | #define F_PIPE 9 /* named pipe */ 225 | 226 | /* 227 | * Struct to hold property-based filters 228 | */ 229 | struct prop_filter { 230 | uint8_t prop_type; 231 | #define PROP_TYPE_NOOP 0 232 | #define PROP_TYPE_MSG 1 233 | #define PROP_TYPE_HOSTNAME 2 234 | #define PROP_TYPE_PROGNAME 3 235 | #define PROP_TYPE_MSGID 4 236 | #define PROP_TYPE_DATA 5 237 | uint8_t cmp_type; 238 | #define PROP_CMP_CONTAINS 1 239 | #define PROP_CMP_EQUAL 2 240 | #define PROP_CMP_STARTS 3 241 | #define PROP_CMP_REGEX 4 242 | uint16_t cmp_flags; 243 | #define PROP_FLAG_EXCLUDE (1 << 0) 244 | #define PROP_FLAG_ICASE (1 << 1) 245 | union { 246 | char *p_strval; 247 | regex_t *p_re; 248 | } pflt_uniptr; 249 | #define pflt_strval pflt_uniptr.p_strval 250 | #define pflt_re pflt_uniptr.p_re 251 | size_t pflt_strlen; 252 | }; 253 | 254 | /* 255 | * Struct to hold records of peers and sockets 256 | */ 257 | struct peer { 258 | TAILQ_ENTRY(peer) pe_link; 259 | char *pe_name; 260 | char *pe_serv; 261 | char *pe_iface; 262 | int pe_mark; 263 | mode_t pe_mode; 264 | int pe_sock[16]; 265 | size_t pe_socknum; 266 | }; 267 | 268 | /* 269 | * Struct to hold records of network addresses that are allowed to log 270 | * to us. 271 | */ 272 | struct allowedpeer { 273 | SIMPLEQ_ENTRY(allowedpeer) next; 274 | int isnumeric; 275 | u_short port; 276 | union { 277 | struct { 278 | struct sockaddr_storage addr; 279 | struct sockaddr_storage mask; 280 | } numeric; 281 | char *name; 282 | } u; 283 | #define a_addr u.numeric.addr 284 | #define a_mask u.numeric.mask 285 | #define a_name u.name 286 | }; 287 | 288 | /* Timestamps of log entries. */ 289 | struct logtime { 290 | struct tm tm; 291 | suseconds_t usec; 292 | }; 293 | 294 | /* message buffer container used for processing, formatting, and queueing */ 295 | struct buf_msg { 296 | int pri; 297 | char pribuf[8]; 298 | int flags; 299 | struct logtime timestamp; 300 | char timebuf[33]; 301 | char *recvhost; 302 | char *hostname; 303 | char *app_name; 304 | char *proc_id; 305 | char *msgid; 306 | char *sd; /* structured data */ 307 | char *msg; /* message content */ 308 | }; 309 | 310 | /* 311 | * This structure represents the files that will have log 312 | * copies printed. 313 | * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY 314 | * or if f_type is F_PIPE and f_pid > 0. 315 | */ 316 | struct filed { 317 | SIMPLEQ_ENTRY(filed) f_link; 318 | 319 | short f_type; /* entry type, see below */ 320 | short f_file; /* file descriptor */ 321 | time_t f_time; /* time this was last written */ 322 | char *f_host; /* host from which to recd. */ 323 | char *f_program; /* program(s) this applies to */ 324 | struct prop_filter *f_prop_filter; /* property-based filter */ 325 | u_char f_pmask[LOG_NFACILITIES + 1]; /* priority mask */ 326 | union { 327 | char f_uname[MAXUNAMES][UNAMESZ + 1]; 328 | struct { 329 | char f_hname[MAXHOSTNAMELEN + 1]; 330 | char f_serv[20]; 331 | struct addrinfo *f_addr; 332 | } f_forw; /* forwarding address */ 333 | char f_fname[MAXFNAME]; 334 | } f_un; 335 | char f_prevline[MAXSVLINE]; /* last message logged */ 336 | struct logtime f_lasttime; /* time of last occurrence */ 337 | char f_prevhost[MAXHOSTNAMELEN + 1]; /* host from which recd. */ 338 | int f_prevpri; /* pri of f_prevline */ 339 | size_t f_prevlen; /* length of f_prevline */ 340 | size_t f_prevcount; /* repetition cnt of prevline */ 341 | size_t f_repeatcount; /* number of "repeated" msgs */ 342 | int f_flags; /* store some additional flags */ 343 | int f_rotatecount; 344 | int f_rotatesz; 345 | char *f_iface; /* only for multicast fwd */ 346 | int f_ttl; /* only for multicast fwd */ 347 | }; 348 | 349 | /* 350 | * Log rotation notifiers 351 | */ 352 | struct notifier { 353 | TAILQ_ENTRY(notifier) n_link; 354 | char *n_program; 355 | }; 356 | 357 | void flog(int pri, char *fmt, ...); 358 | 359 | #endif /* SYSKLOGD_SYSLOGD_H_ */ 360 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 2018-2023 Joachim Wiberg 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #include "config.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #define SYSLOG_NAMES 48 | #include "compat.h" 49 | #include "syslog.h" 50 | 51 | static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION; 52 | static struct syslog_data log = SYSLOG_DATA_INIT; 53 | 54 | static int create(char *path, mode_t mode, uid_t uid, gid_t gid) 55 | { 56 | return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid); 57 | } 58 | 59 | /* 60 | * This function triggers a log rotates of @file when size >= @sz bytes 61 | * At most @num old versions are kept and by default it starts gzipping 62 | * .2 and older log files. If gzip is not available in $PATH then @num 63 | * files are kept uncompressed. 64 | */ 65 | static int logrotate(char *file, int num, off_t sz) 66 | { 67 | struct stat st; 68 | int cnt; 69 | 70 | if (stat(file, &st)) 71 | return 1; 72 | 73 | if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) { 74 | if (num > 0) { 75 | size_t len = strlen(file) + 10 + 1; 76 | char ofile[len]; 77 | char nfile[len]; 78 | 79 | /* First age zipped log files */ 80 | for (cnt = num; cnt > 2; cnt--) { 81 | snprintf(ofile, len, "%s.%d.gz", file, cnt - 1); 82 | snprintf(nfile, len, "%s.%d.gz", file, cnt); 83 | 84 | /* May fail because ofile doesn't exist yet, ignore. */ 85 | (void)rename(ofile, nfile); 86 | } 87 | 88 | for (cnt = num; cnt > 0; cnt--) { 89 | snprintf(ofile, len, "%s.%d", file, cnt - 1); 90 | snprintf(nfile, len, "%s.%d", file, cnt); 91 | 92 | /* May fail because ofile doesn't exist yet, ignore. */ 93 | (void)rename(ofile, nfile); 94 | 95 | if (cnt == 2 && !access(nfile, F_OK)) { 96 | size_t len = 5 + strlen(nfile) + 1; 97 | char cmd[len]; 98 | 99 | snprintf(cmd, len, "gzip %s", nfile); 100 | system(cmd); 101 | 102 | remove(nfile); 103 | } 104 | } 105 | 106 | if (rename(file, nfile)) 107 | (void)truncate(file, 0); 108 | else 109 | create(file, st.st_mode, st.st_uid, st.st_gid); 110 | } else { 111 | if (truncate(file, 0)) 112 | syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno)); 113 | } 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | static void log_kmsg(FILE *fp, char *ident, int pri, int opts, char *buf) 120 | { 121 | while (isspace(*buf)) 122 | buf++; 123 | 124 | /* Always add [PID] so syslogd can find this later on */ 125 | fprintf(fp, "<%d>%s[%d]:%s\n", pri, ident, getpid(), buf); 126 | } 127 | 128 | static int nslookup(const char *host, const char *svcname, int family, struct sockaddr *sa) 129 | { 130 | struct addrinfo hints, *ai, *result; 131 | int error; 132 | 133 | memset(&hints, 0, sizeof(hints)); 134 | hints.ai_flags = !host ? AI_PASSIVE : 0; 135 | hints.ai_family = family; 136 | hints.ai_socktype = SOCK_DGRAM; 137 | 138 | error = getaddrinfo(host, svcname, &hints, &result); 139 | if (error == EAI_SERVICE) { 140 | warnx("%s/udp: unknown service, trying syslog port 514", svcname); 141 | svcname = "514"; 142 | error = getaddrinfo(host, svcname, &hints, &result); 143 | } 144 | if (error) { 145 | warnx("%s (%s:%s)", gai_strerror(error), host, svcname); 146 | return 1; 147 | } 148 | 149 | for (ai = result; ai; ai = ai->ai_next) { 150 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 151 | continue; 152 | 153 | memcpy(sa, ai->ai_addr, ai->ai_addrlen); 154 | break; 155 | } 156 | freeaddrinfo(result); 157 | 158 | return 0; 159 | } 160 | 161 | static int checksz(FILE *fp, off_t sz) 162 | { 163 | struct stat st; 164 | 165 | if (!fp) 166 | return 0; 167 | 168 | fsync(fileno(fp)); 169 | if (sz <= 0) 170 | return 0; 171 | 172 | if (!fstat(fileno(fp), &st) && st.st_size > sz) { 173 | fclose(fp); 174 | return 1; 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | static char *chomp(char *str) 181 | { 182 | char *p; 183 | 184 | if (!str || strlen(str) < 1) { 185 | errno = EINVAL; 186 | return NULL; 187 | } 188 | 189 | p = str + strlen(str) - 1; 190 | while (p >= str && *p == '\n') 191 | *p-- = 0; 192 | 193 | return str; 194 | } 195 | 196 | /* 197 | * Parse possible systemd style log prefix from message 198 | * For details, see libsystemd/sd-daemon.h 199 | * 200 | * Returns the level, and updates buf to point past the prefix 201 | */ 202 | static int parse_level(char **buf, int severity) 203 | { 204 | char *msg = *buf; 205 | 206 | if (msg[0] == '<' && msg[2] == '>' && msg[1] >= '0' && msg[1] <= '7') { 207 | int level; 208 | 209 | switch (msg[1]) { 210 | case '0': level = LOG_EMERG; break; 211 | case '1': level = LOG_ALERT; break; 212 | case '2': level = LOG_CRIT; break; 213 | case '3': level = LOG_ERR; break; 214 | case '4': level = LOG_WARNING; break; 215 | case '5': level = LOG_NOTICE; break; 216 | case '6': level = LOG_INFO; break; 217 | case '7': level = LOG_DEBUG; break; 218 | default: level = severity; break; 219 | } 220 | 221 | *buf = msg + 3; 222 | return (severity & ~LOG_PRIMASK) | level; 223 | } 224 | 225 | return severity; 226 | } 227 | 228 | static int parse_prio(const char *arg, int *f, int *l) 229 | { 230 | char buf[strlen(arg) + 1]; 231 | char *fac, *pri; 232 | int found = 0; 233 | 234 | strlcpy(buf, arg, sizeof(buf)); 235 | fac = buf; 236 | 237 | pri = strchr(buf, '.'); 238 | if (pri) { 239 | *pri++ = 0; 240 | 241 | for (int i = 0; prioritynames[i].c_name; i++) { 242 | if (strcmp(prioritynames[i].c_name, pri)) 243 | continue; 244 | 245 | *l = prioritynames[i].c_val; 246 | found = 1; 247 | break; 248 | } 249 | 250 | if (!found) 251 | return 1; 252 | found = 0; 253 | } 254 | 255 | for (int i = 0; facilitynames[i].c_name; i++) { 256 | if (strcmp(facilitynames[i].c_name, fac)) 257 | continue; 258 | 259 | *f = facilitynames[i].c_val; 260 | found = 1; 261 | break; 262 | } 263 | 264 | if (!found) 265 | return 1; 266 | 267 | return 0; 268 | } 269 | 270 | static int parse_opts(char *arg, char **iface, int *ttl) 271 | { 272 | char *subopts = arg; 273 | char *value; 274 | enum { 275 | IFACE_OPT = 0, 276 | TTL_OPT, 277 | }; 278 | char *const token[] = { 279 | [IFACE_OPT] = "iface", 280 | [TTL_OPT] = "ttl", 281 | NULL 282 | }; 283 | 284 | while (*subopts != '\0') { 285 | switch (getsubopt(&subopts, token, &value)) { 286 | case IFACE_OPT: 287 | if (!value) 288 | return 1; 289 | *iface = value; 290 | break; 291 | case TTL_OPT: 292 | if (!value) 293 | return 1; 294 | *ttl = atoi(value); 295 | if (*ttl < 1 || *ttl > 255) 296 | return 1; 297 | break; 298 | default: 299 | return 1; 300 | } 301 | } 302 | 303 | return 0; 304 | } 305 | 306 | static int usage(int code) 307 | { 308 | printf("Usage: logger [OPTIONS] [MESSAGE]\n" 309 | "\n" 310 | "Write MESSAGE (or line-by-line stdin) to syslog, or file (with logrotate).\n" 311 | "\n" 312 | " -4 Prefer IPv4 address when sending remote, see -h\n" 313 | " -6 Prefer IPv6 address when sending remote, see -h\n" 314 | " -b Use RFC3164 (BSD) style format, default: RFC5424\n" 315 | " -c Log to console (LOG_CONS) on failure\n" 316 | " -d SD Log SD as RFC5424 style 'structured data' in message\n" 317 | " -f FILE Log file to write messages to, instead of syslog daemon\n" 318 | " -h HOST Send (UDP) message to this remote syslog server (IP or DNS name)\n" 319 | " -H NAME Use NAME instead of system hostname in message header\n" 320 | " -i Log process ID of the logger process with each line (LOG_PID)\n" 321 | " -I PID Log process ID using PID, recommend using PID $$ for shell scripts\n" 322 | #ifdef __linux__ 323 | " -k Log to kernel /dev/kmsg if /dev/log doesn't exist yet\n" 324 | #endif 325 | " -m MSGID Log message using this RFC5424 style MSGID\n" 326 | " -n Open log file immediately (LOG_NDELAY)\n" 327 | " -o OPT Set outbound multicast options:\n" 328 | " iface=IFNAME\n" 329 | " ttl=1-255\n" 330 | " -p PRIO Log message priority (numeric or facility.severity pair)\n" 331 | " -P PORT Use PORT (or named UDP service) for remote server, default: syslog\n" 332 | " -r S[:R] Enable log file rotation, default: 200 kB \e[4ms\e[0mize, 5 \e[4mr\e[0motations\n" 333 | " -s Log to stderr as well as the system log\n" 334 | " -t TAG Log using the specified tag (defaults to user name)\n" 335 | " -u SOCK Log to UNIX domain socket `SOCK` instead of default %s\n" 336 | " -? This help text\n" 337 | " -v Show program version\n" 338 | "\n" 339 | "Bug report address: %s\n", _PATH_LOG, PACKAGE_BUGREPORT); 340 | #ifdef PACKAGE_URL 341 | printf("Project home page: %s\n", PACKAGE_URL); 342 | #endif 343 | 344 | return code; 345 | } 346 | 347 | int main(int argc, char *argv[]) 348 | { 349 | char *ident = NULL, *logfile = NULL; 350 | char *host = NULL, *sockpath = NULL; 351 | char *msgid = NULL, *sd = NULL; 352 | char *svcname = "syslog"; 353 | off_t size = 200 * 1024; 354 | int facility = LOG_USER; 355 | int severity = LOG_NOTICE; 356 | int family = AF_UNSPEC; 357 | struct sockaddr sa; 358 | int allow_kmsg = 0; 359 | char buf[512] = ""; 360 | char *iface = NULL; 361 | int log_opts = 0; 362 | FILE *fp = NULL; 363 | int c, num = 5; 364 | int rotate = 0; 365 | int ttl = 1; 366 | 367 | while ((c = getopt(argc, argv, "46?bcd:f:h:H:iI:km:no:p:P:r:st:u:v")) != EOF) { 368 | switch (c) { 369 | case '4': 370 | family = AF_INET; 371 | break; 372 | 373 | case '6': 374 | family = AF_INET6; 375 | break; 376 | 377 | case 'b': 378 | log_opts |= LOG_RFC3164; 379 | break; 380 | 381 | case 'c': 382 | log_opts |= LOG_CONS; 383 | break; 384 | 385 | case 'd': 386 | sd = optarg; 387 | break; 388 | 389 | case 'f': 390 | logfile = optarg; 391 | break; 392 | 393 | case 'h': 394 | host = optarg; 395 | break; 396 | 397 | case 'H': 398 | if (strcmp(optarg, "@")) 399 | strlcpy(log.log_hostname, optarg, sizeof(log.log_hostname)); 400 | break; 401 | 402 | case 'i': 403 | log_opts |= LOG_PID; 404 | break; 405 | 406 | case 'I': 407 | log_opts |= LOG_PID; 408 | log.log_pid = atoi(optarg); 409 | break; 410 | 411 | case 'k': 412 | #ifdef __linux__ 413 | allow_kmsg = 1; 414 | #else 415 | errx(1, "-k is not supported on non-Linux systems."); 416 | #endif 417 | break; 418 | 419 | case 'm': 420 | msgid = optarg; 421 | break; 422 | 423 | case 'n': 424 | log_opts |= LOG_NDELAY; 425 | break; 426 | 427 | case 'o': 428 | if (parse_opts(optarg, &iface, &ttl)) { 429 | printf("Invalid option argument '-%c %s'\n", c, optarg); 430 | return usage(1); 431 | } 432 | break; 433 | 434 | case 'p': 435 | if (parse_prio(optarg, &facility, &severity)) { 436 | printf("Invalid option argument '-%c %s'\n", c, optarg); 437 | return usage(1); 438 | } 439 | break; 440 | 441 | case 'P': 442 | svcname = optarg; 443 | break; 444 | 445 | case 'r': 446 | parse_rotation(optarg, &size, &num); 447 | if (size > 0 && num > 0) 448 | rotate = 1; 449 | break; 450 | 451 | case 's': 452 | log_opts |= LOG_PERROR; 453 | break; 454 | 455 | case 't': 456 | ident = optarg; 457 | break; 458 | 459 | case 'u': 460 | sockpath = optarg; 461 | break; 462 | 463 | case 'v': /* version */ 464 | printf("%s\n", version_info); 465 | return 0; 466 | 467 | case '?': 468 | return usage(0); 469 | 470 | default: 471 | printf("Unsupported option '-%c'\n", c); 472 | return usage(1); 473 | } 474 | } 475 | 476 | if (!ident) 477 | ident = getenv("LOGNAME") ?: getenv("USER"); 478 | 479 | if (optind < argc) { 480 | size_t pos = 0, len = sizeof(buf); 481 | 482 | while (optind < argc) { 483 | size_t bytes; 484 | 485 | bytes = snprintf(&buf[pos], len, "%s%s", pos ? " " : "", argv[optind++]); 486 | pos += bytes; 487 | len -= bytes; 488 | } 489 | } 490 | 491 | if (logfile) { 492 | if (strcmp(logfile, "-")) { 493 | log_opts |= LOG_NLOG; 494 | fp = fopen(logfile, "a"); 495 | if (!fp) 496 | err(1, "Failed opening %s for writing", logfile); 497 | } else { 498 | log_opts |= LOG_STDOUT; 499 | fp = stdout; 500 | } 501 | 502 | log.log_file = fileno(fp); 503 | } else if (sockpath) { 504 | if (access(sockpath, W_OK)) 505 | err(1, "Socket path %s", sockpath); 506 | log.log_sockpath = sockpath; 507 | } else if (allow_kmsg && access(_PATH_LOG, W_OK)) { 508 | /* 509 | * -k and /dev/log is not yet up, user wants to prevent 510 | * logging to console and instead use the detour around 511 | * the kernel logger until syslogd has started. 512 | */ 513 | while (!access("/dev/kmsg", W_OK)) { 514 | int pri = facility | severity; 515 | 516 | fp = fopen("/dev/kmsg", "w"); 517 | if (!fp) 518 | break; /* fall back to log syslogp_r() */ 519 | 520 | if (!buf[0]) { 521 | while ((fgets(buf, sizeof(buf), stdin))) 522 | log_kmsg(fp, ident, pri, log_opts, chomp(buf)); 523 | } else 524 | log_kmsg(fp, ident, pri, log_opts, buf); 525 | 526 | return fclose(fp); 527 | } 528 | } else if (host) { 529 | log.log_host = &sa; 530 | log.log_iface = iface; 531 | log.log_ttl = ttl; 532 | if (nslookup(host, svcname, family, &sa)) 533 | return 1; 534 | log_opts |= LOG_NDELAY; 535 | } 536 | 537 | openlog_r(ident, log_opts, facility, &log); 538 | 539 | if (!buf[0]) { 540 | while ((fgets(buf, sizeof(buf), stdin))) { 541 | char *msg = chomp(buf); 542 | int level; 543 | 544 | level = parse_level(&msg, severity); 545 | syslogp_r(level, &log, msgid, sd, "%s", msg); 546 | } 547 | } else 548 | syslogp_r(severity, &log, msgid, sd, "%s", buf); 549 | 550 | closelog_r(&log); 551 | 552 | if (logfile && rotate && checksz(fp, size)) 553 | logrotate(logfile, num, size); 554 | 555 | return 0; 556 | } 557 | 558 | -------------------------------------------------------------------------------- /man/syslogp.3: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" Copyright (c) 1985, 1991, 1993 3 | .\" The Regents of the University of California. 4 | .\" All rights reserved. 5 | .\" 6 | .\" Redistribution and use in source and binary forms, with or without 7 | .\" modification, are permitted provided that the following conditions 8 | .\" are met: 9 | .\" 1. Redistributions of source code must retain the above copyright 10 | .\" notice, this list of conditions and the following disclaimer. 11 | .\" 2. Redistributions in binary form must reproduce the above copyright 12 | .\" notice, this list of conditions and the following disclaimer in the 13 | .\" documentation and/or other materials provided with the distribution. 14 | .\" 3. Neither the name of the University nor the names of its contributors 15 | .\" may be used to endorse or promote products derived from this software 16 | .\" without specific prior written permission. 17 | .\" 18 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | .\" SUCH DAMAGE. 29 | .\" 30 | .\" @(#)syslog.3 8.1 (Berkeley) 6/4/93 31 | .\" 32 | .Dd August 1, 2022 33 | .Dt SYSLOGP 3 34 | .Os 35 | .Sh NAME 36 | .Nm syslog , 37 | .Nm syslog_r , 38 | .Nm vsyslog , 39 | .Nm vsyslog_r , 40 | .Nm syslogp , 41 | .Nm syslogp_r , 42 | .Nm vsyslogp , 43 | .Nm vsyslogp_r , 44 | .Nm openlog , 45 | .Nm openlog_r , 46 | .Nm closelog , 47 | .Nm closelog_r , 48 | .Nm setlogmask , 49 | .Nm setlogmask_r 50 | .Nd control system log 51 | .Sh LIBRARY 52 | .Lb libc 53 | .Sh SYNOPSIS 54 | .In syslog.h 55 | .Ft void 56 | .Fn syslog "int priority" "const char *message" "..." 57 | .Ft void 58 | .Fn syslog_r "int priority" "struct syslog_data *data" "const char *message" "..." 59 | .Ft void 60 | .Fn syslogp "int priority" "const char *msgid" "const char *sdfmt" "const char *message" "..." 61 | .Ft void 62 | .Fn syslogp_r "int priority" "struct syslog_data *data" "const char *msgid" "const char *sdfmt" "const char *message" "..." 63 | .\" .Ft void 64 | .\" .Fn syslog_ss "int priority" "struct syslog_data *data" "const char *message" "..." 65 | .Ft void 66 | .Fn openlog "const char *ident" "int logopt" "int facility" 67 | .Ft void 68 | .Fn openlog_r "const char *ident" "int logopt" "int facility" "struct syslog_data *data" 69 | .Ft void 70 | .Fn closelog void 71 | .Ft void 72 | .Fn closelog_r "struct syslog_data *data" 73 | .Ft int 74 | .Fn setlogmask "int maskpri" 75 | .Ft int 76 | .Fn setlogmask_r "int maskpri" "struct syslog_data *data" 77 | .In stdarg.h 78 | .Ft void 79 | .Fn vsyslog "int priority" "const char *message" "va_list args" 80 | .Ft void 81 | .Fn vsyslog_r "int priority" "struct syslog_data *data" "const char *message" "va_list args" 82 | .Ft void 83 | .Fn vsyslogp "int priority" "const char *msgid" "const char *sdfmt" "const char *message" "va_list args" 84 | .Ft void 85 | .Fn vsyslogp_r "int priority" "struct syslog_data *data" "const char *msgid" "const char *sdfmt" "const char *message" "va_list args" 86 | .\" .Ft void 87 | .\" .Fn vsyslog_ss "int priority" "struct syslog_data *data" "const char *message" "va_list args" 88 | .Sh DESCRIPTION 89 | The 90 | .Fn syslog 91 | function 92 | writes 93 | .Fa message 94 | to the system message logger. 95 | The message is then written to the system console, log files, 96 | logged-in users, or forwarded to other machines as appropriate (see 97 | .Xr syslogd 8 ) . 98 | .Pp 99 | The message is identical to a 100 | .Xr printf 3 101 | format string, except that 102 | .Ql %m 103 | is replaced by the current error 104 | message. 105 | (As denoted by the global variable 106 | .Va errno ; 107 | see 108 | .Xr strerror 3 . ) 109 | A trailing newline is added if none is present. 110 | .\" shouldn't the newline statement be removed? 111 | .\" when logging through a socket a newline is 112 | .\" not added nor is it required. -- ms 113 | .Pp 114 | The 115 | .Fn syslog_r 116 | function is a multithread-safe version of the 117 | .Fn syslog 118 | function. It takes a pointer to a 119 | .Fa syslog_data 120 | structure which is used to store information. This parameter must be 121 | initialized before 122 | .Fn syslog_r 123 | is called. The 124 | .Dv SYSLOG_DATA_INIT 125 | constant is used for this purpose. The 126 | .Fa syslog_data 127 | structure and the 128 | .Dv SYSLOG_DATA_INIT 129 | constant are defined as: 130 | .Bd -literal -offset indent 131 | struct syslog_data { 132 | int log_version; 133 | int log_file; 134 | int log_connected; 135 | int log_opened; 136 | int log_stat; 137 | const char *log_tag; 138 | const char *log_sockpath; 139 | char log_hostname[256]; 140 | int log_fac; 141 | int log_mask; 142 | struct sockaddr *log_host; 143 | int log_pid; 144 | }; 145 | 146 | #define SYSLOG_DATA_INIT { \e 147 | .log_version = 1, \e 148 | .log_file = -1, \e 149 | .log_connected = 0, \e 150 | .log_opened = 0, \e 151 | .log_stat = 0, \e 152 | .log_tag = 0, \e 153 | .log_sockpath = NULL, \e 154 | .log_hostname = { '\\0' }, \e 155 | .log_fac = LOG_USER, \e 156 | .log_mask = 0xff, \e 157 | .log_host = NULL, \e 158 | .log_pid = -1, \e 159 | } 160 | .Ed 161 | .Pp 162 | The structure is composed of the following elements: 163 | .Bl -tag -width connected -offset indent 164 | .It Va log_file 165 | contains the file descriptor of the file where the message is logged 166 | .It Va log_connected 167 | indicates if connect has been done 168 | .It Va log_opened 169 | indicates if 170 | .Fn openlog_r 171 | has been called 172 | .It Va log_stat 173 | status bits, set by 174 | .Fn openlog_r 175 | .It Va log_tag 176 | string to tag the entry with 177 | .It Va log_sockpath 178 | UNIX domain socket used, can be changed by user 179 | .It Va log_hostname 180 | Hostname used, initialized to 181 | .Fn gethostname 182 | if unset, can be changed by user 183 | .It Va log_fac 184 | facility code 185 | .It Va log_mask 186 | mask of priorities to be logged 187 | .It Va log_host 188 | .Va struct sockaddr * 189 | host to send UDP message to, if set by user, enables logging to a remote 190 | syslog server bypassing any local syslog server 191 | .It Va log_pid 192 | Process ID used in log message header. Can be changed by user, if unset 193 | defaults to call 194 | .Fn getpid 195 | .El 196 | .\" .Pp 197 | .\" The 198 | .\" .Fn syslog_ss 199 | .\" is the async-signal-safe version of 200 | .\" .Fn syslog_r 201 | .\" and is also multithread-safe. 202 | .\" It has the following limitations: 203 | .\" .Bl -enum -offset indent 204 | .\" .It 205 | .\" The format string cannot contain multi-byte character sequences. 206 | .\" .It 207 | .\" Floating point formats are not supported and print 208 | .\" .Dq UNK . 209 | .\" .It 210 | .\" The time of the event is not sent to 211 | .\" .Xr syslogd 8 . 212 | .\" .It 213 | .\" The error string in the %m format is not printed symbolically but as 214 | .\" .Dq Error %d . 215 | .\" .El 216 | .\" .Pp 217 | .\" For more information about async-signal-safe functions and signal handlers, see 218 | .\" .Xr signal 7 . 219 | .Pp 220 | The 221 | .Fn vsyslog 222 | function 223 | is an alternative form in which the arguments have already been captured 224 | using the variable-length argument facilities of 225 | .Xr stdarg 3 . 226 | .Pp 227 | The 228 | .Fn syslogp 229 | variants take additional arguments which correspond to new fields in the 230 | syslog-protocol message format. 231 | All three arguments are evaluated as 232 | .Xr printf 3 233 | format strings and any of them can be 234 | .Dv NULL . 235 | This enables applications to use message IDs, structured data, and UTF-8 encoded 236 | content in messages. 237 | .Pp 238 | The message is tagged with 239 | .Fa priority . 240 | Priorities are encoded as a 241 | .Fa facility 242 | and a 243 | .Em level . 244 | The facility describes the part of the system 245 | generating the message. 246 | The level is selected from the following 247 | .Em ordered 248 | (high to low) list: 249 | .Bl -tag -width LOG_AUTHPRIV 250 | .It Dv LOG_EMERG 251 | A panic condition. 252 | This is normally broadcast to all users. 253 | .It Dv LOG_ALERT 254 | A condition that should be corrected immediately, such as a corrupted 255 | system database. 256 | .It Dv LOG_CRIT 257 | Critical conditions, e.g., hard device errors. 258 | .It Dv LOG_ERR 259 | Errors. 260 | .It Dv LOG_WARNING 261 | Warning messages. 262 | .It Dv LOG_WARN 263 | Warning messages, alias. 264 | .It Dv LOG_NOTICE 265 | Conditions that are not error conditions, 266 | but should possibly be handled specially. 267 | .It Dv LOG_INFO 268 | Informational messages. 269 | .It Dv LOG_DEBUG 270 | Messages that contain information 271 | normally of use only when debugging a program. 272 | .El 273 | .Pp 274 | The 275 | .Fn vsyslog_r 276 | is used the same way as 277 | .Fn vsyslog 278 | except that it takes an additional pointer to a 279 | .Fa syslog_data 280 | structure. 281 | It is a multithread-safe version of the 282 | .Fn vsyslog 283 | function described above. 284 | .\" The 285 | .\" .Fn vsyslog_ss 286 | .\" is the async-signal-safe version of 287 | .\" .Fn vsyslog_r , 288 | .\" is also multithread-safe, and has the same limitations as 289 | .\" .Fn syslog_ss . 290 | .Pp 291 | The 292 | .Fn openlog 293 | function 294 | provides for more specialized processing of the messages sent 295 | by 296 | .Fn syslog 297 | and 298 | .Fn vsyslog . 299 | The parameter 300 | .Fa ident 301 | is a string that will be prepended to every message. 302 | The 303 | .Fa logopt 304 | argument 305 | is a bit field specifying logging options, which is formed by 306 | .Tn OR Ns 'ing 307 | one or more of the following values: 308 | .Bl -tag -width LOG_AUTHPRIV 309 | .It Dv LOG_CONS 310 | If 311 | .Fn syslog 312 | cannot pass the message to 313 | .Xr syslogd 8 314 | it will attempt to write the message to the console 315 | .Pq Dq Pa /dev/console . 316 | .It Dv LOG_NDELAY 317 | Open the connection to 318 | .Xr syslogd 8 319 | immediately. 320 | Normally the open is delayed until the first message is logged. 321 | Useful for programs that need to manage the order in which file 322 | descriptors are allocated. 323 | .It Dv LOG_NLOG 324 | Stops syslog from writing to the system log. 325 | Only useful with 326 | .Dv LOG_PERROR . 327 | .It Dv LOG_PERROR 328 | Write the message to standard error output as well to the system log. 329 | .It Dv LOG_PID 330 | Log the process id with each message: useful for identifying 331 | instantiations of daemons. 332 | (This PID is placed within brackets 333 | between the ident and the message.) 334 | .It Dv LOG_PTRIM 335 | Trim anything syslog added to the message before writing to 336 | standard error output. 337 | .It Dv LOG_RFC3164 338 | Use RFC3164 (BSD) style log messages, the default is new-style RFC5424. 339 | .El 340 | .Pp 341 | The 342 | .Fa facility 343 | parameter encodes a default facility to be assigned to all messages 344 | that do not have an explicit facility encoded: 345 | .Bl -tag -width LOG_AUTHPRIV 346 | .It Dv LOG_AUTH 347 | The authorization system: 348 | .Xr login 1 , 349 | .Xr su 1 , 350 | .Xr getty 8 , 351 | etc. 352 | .It Dv LOG_AUTHPRIV 353 | The same as 354 | .Dv LOG_AUTH , 355 | but logged to a file readable only by 356 | selected individuals. 357 | .It Dv LOG_CRON 358 | The cron daemon: 359 | .Xr cron 8 . 360 | .It Dv LOG_DAEMON 361 | System daemons, such as 362 | .Xr routed 8 , 363 | that are not provided for explicitly by other facilities. 364 | .It Dv LOG_FTP 365 | The file transfer protocol daemon: 366 | .Xr ftpd 8 . 367 | .It Dv LOG_KERN 368 | Messages generated by the kernel. 369 | These cannot be generated by any user processes. 370 | .It Dv LOG_LPR 371 | The line printer spooling system: 372 | .Xr lpr 1 , 373 | .Xr lpc 8 , 374 | .Xr lpd 8 , 375 | etc. 376 | .It Dv LOG_MAIL 377 | The mail system. 378 | .It Dv LOG_NEWS 379 | The network news system. 380 | .It Dv LOG_SYSLOG 381 | Messages generated internally by 382 | .Xr syslogd 8 . 383 | .It Dv LOG_USER 384 | Messages generated by random user processes. 385 | This is the default facility identifier if none is specified. 386 | .It Dv LOG_UUCP 387 | The uucp system. 388 | .It Dv LOG_LOCAL0 389 | Reserved for local use. 390 | Similarly for 391 | .Dv LOG_LOCAL1 392 | through 393 | .Dv LOG_LOCAL7 . 394 | .El 395 | .Pp 396 | The 397 | .Fn openlog_r 398 | function is the multithread-safe version of the 399 | .Fn openlog 400 | function. 401 | It takes an additional pointer to a 402 | .Fa syslog_data 403 | structure. 404 | This function must be used in conjunction with the other 405 | multithread-safe functions. 406 | .Pp 407 | The 408 | .Fn closelog 409 | function 410 | can be used to close the log file. 411 | .Pp 412 | The 413 | .Fn closelog_r 414 | does the same thing as 415 | .Xr closelog 3 416 | but in a multithread-safe way and takes an additional 417 | pointer to a 418 | .Fa syslog_data 419 | structure. 420 | .Pp 421 | The 422 | .Fn setlogmask 423 | function 424 | sets the log priority mask to 425 | .Fa maskpri 426 | and returns the previous mask. 427 | Calls to 428 | .Fn syslog 429 | with a priority not set in 430 | .Fa maskpri 431 | are rejected. 432 | The mask for an individual priority 433 | .Fa pri 434 | is calculated by the macro 435 | .Fn LOG_MASK pri ; 436 | the mask for all priorities up to and including 437 | .Fa toppri 438 | is given by the macro 439 | .Fn LOG_UPTO toppri . 440 | The default allows all priorities to be logged. 441 | .Pp 442 | The 443 | .Fn setlogmask_r 444 | function is the multithread-safe version of 445 | .Fn setlogmask . 446 | It takes an additional pointer to a 447 | .Fa syslog_data 448 | structure. 449 | .Sh RETURN VALUES 450 | The routines 451 | .Fn closelog , 452 | .Fn closelog_r , 453 | .Fn openlog , 454 | .Fn openlog_r , 455 | .Fn syslog , 456 | .Fn syslog_r , 457 | .Fn vsyslog , 458 | .Fn vsyslog_r , 459 | .Fn syslogp , 460 | .Fn syslogp_r , 461 | .Fn vsyslogp , 462 | and 463 | .Fn vsyslogp_r 464 | return no value. 465 | .Pp 466 | The routines 467 | .Fn setlogmask 468 | and 469 | .Fn setlogmask_r 470 | always return the previous log mask level. 471 | .Sh EXAMPLES 472 | .Bd -literal -offset indent -compact 473 | syslog(LOG_ALERT, "who: internal error 23"); 474 | 475 | openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 476 | 477 | setlogmask(LOG_UPTO(LOG_ERR)); 478 | 479 | syslog(LOG_INFO, "Connection from host %d", CallingHost); 480 | 481 | syslog(LOG_INFO|LOG_LOCAL2, "foobar error: %m"); 482 | 483 | syslogp(LOG_INFO|LOG_LOCAL2, NULL, NULL, "foobar error: %m"); 484 | 485 | syslogp(LOG_INFO, "ID%d", "[meta language=\e"en-US\e"]", 486 | "event: %s", 42, EventDescription); 487 | .Ed 488 | .Pp 489 | For the multithread-safe functions: 490 | .Bd -literal -offset indent 491 | struct syslog_data sdata = SYSLOG_DATA_INIT; 492 | 493 | syslog_r(LOG_INFO|LOG_LOCAL2, &sdata, "foobar error: %m"); 494 | .Ed 495 | .Sh SEE ALSO 496 | .Xr logger 1 , 497 | .Xr syslogd 8 498 | .Rs 499 | .%R RFC 500 | .%N 3164 501 | .%D August 2001 502 | .%T The BSD syslog Protocol 503 | .Re 504 | .Rs 505 | .%R Internet-Draft 506 | .%N draft-ietf-syslog-protocol-23 507 | .%D September 2007 508 | .%T The syslog Protocol 509 | .Re 510 | .Sh HISTORY 511 | These non-multithread-safe functions appeared in 512 | .Bx 4.2 . 513 | The multithread-safe functions appeared in 514 | .Ox 3.1 515 | and then in 516 | .Nx 4.0 . 517 | The async-signal-safe functions appeared in 518 | .Nx 4.0 . 519 | The syslog-protocol functions appeared in 520 | .Nx 5.0 . 521 | .Sh CAVEATS 522 | It is important never to pass a string with user-supplied data as a 523 | format without using 524 | .Ql %s . 525 | An attacker can put format specifiers in the string to mangle your stack, 526 | leading to a possible security hole. 527 | This holds true even if you have built the string 528 | .Dq by hand 529 | using a function like 530 | .Fn snprintf , 531 | as the resulting string may still contain user-supplied conversion specifiers 532 | for later interpolation by 533 | .Fn syslog . 534 | .Pp 535 | Always be sure to use the proper secure idiom: 536 | .Bd -literal -offset indent 537 | syslog(priority, "%s", string); 538 | .Ed 539 | .Pp 540 | With 541 | .Fn syslogp 542 | the caller is responsible to use the right formatting for the message fields. 543 | A 544 | .Fa msgid 545 | must only contain up to 32 ASCII characters. 546 | A 547 | .Fa sdfmt 548 | has strict rules for parenthesis and character quoting. 549 | If the 550 | .Fa msgfmt 551 | contains UTF-8 characters, then it has to start with a Byte Order Mark. 552 | -------------------------------------------------------------------------------- /src/queue.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: queue.h,v 1.43 2015/12/28 19:38:40 millert Exp $ */ 2 | /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ 3 | 4 | /* 5 | * Copyright (c) 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 33 | */ 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | #ifndef _SYS_QUEUE_H_ 41 | #define _SYS_QUEUE_H_ 42 | 43 | /* 44 | * This file defines five types of data structures: singly-linked lists, 45 | * lists, simple queues, tail queues and XOR simple queues. 46 | * 47 | * 48 | * A singly-linked list is headed by a single forward pointer. The elements 49 | * are singly linked for minimum space and pointer manipulation overhead at 50 | * the expense of O(n) removal for arbitrary elements. New elements can be 51 | * added to the list after an existing element or at the head of the list. 52 | * Elements being removed from the head of the list should use the explicit 53 | * macro for this purpose for optimum efficiency. A singly-linked list may 54 | * only be traversed in the forward direction. Singly-linked lists are ideal 55 | * for applications with large datasets and few or no removals or for 56 | * implementing a LIFO queue. 57 | * 58 | * A list is headed by a single forward pointer (or an array of forward 59 | * pointers for a hash table header). The elements are doubly linked 60 | * so that an arbitrary element can be removed without a need to 61 | * traverse the list. New elements can be added to the list before 62 | * or after an existing element or at the head of the list. A list 63 | * may only be traversed in the forward direction. 64 | * 65 | * A simple queue is headed by a pair of pointers, one to the head of the 66 | * list and the other to the tail of the list. The elements are singly 67 | * linked to save space, so elements can only be removed from the 68 | * head of the list. New elements can be added to the list before or after 69 | * an existing element, at the head of the list, or at the end of the 70 | * list. A simple queue may only be traversed in the forward direction. 71 | * 72 | * A tail queue is headed by a pair of pointers, one to the head of the 73 | * list and the other to the tail of the list. The elements are doubly 74 | * linked so that an arbitrary element can be removed without a need to 75 | * traverse the list. New elements can be added to the list before or 76 | * after an existing element, at the head of the list, or at the end of 77 | * the list. A tail queue may be traversed in either direction. 78 | * 79 | * An XOR simple queue is used in the same way as a regular simple queue. 80 | * The difference is that the head structure also includes a "cookie" that 81 | * is XOR'd with the queue pointer (first, last or next) to generate the 82 | * real pointer value. 83 | * 84 | * For details on the use of these macros, see the queue(3) manual page. 85 | */ 86 | 87 | #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) 88 | #define _Q_INVALIDATE(a) (a) = ((void *)-1) 89 | #else 90 | #define _Q_INVALIDATE(a) 91 | #endif 92 | 93 | /* 94 | * Singly-linked List definitions. 95 | */ 96 | #define SLIST_HEAD(name, type) \ 97 | struct name { \ 98 | struct type *slh_first; /* first element */ \ 99 | } 100 | 101 | #define SLIST_HEAD_INITIALIZER(head) \ 102 | { NULL } 103 | 104 | #define SLIST_ENTRY(type) \ 105 | struct { \ 106 | struct type *sle_next; /* next element */ \ 107 | } 108 | 109 | /* 110 | * Singly-linked List access methods. 111 | */ 112 | #define SLIST_FIRST(head) ((head)->slh_first) 113 | #define SLIST_END(head) NULL 114 | #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) 115 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 116 | 117 | #define SLIST_FOREACH(var, head, field) \ 118 | for((var) = SLIST_FIRST(head); \ 119 | (var) != SLIST_END(head); \ 120 | (var) = SLIST_NEXT(var, field)) 121 | 122 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 123 | for ((var) = SLIST_FIRST(head); \ 124 | (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ 125 | (var) = (tvar)) 126 | 127 | /* 128 | * Singly-linked List functions. 129 | */ 130 | #define SLIST_INIT(head) { \ 131 | SLIST_FIRST(head) = SLIST_END(head); \ 132 | } 133 | 134 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 135 | (elm)->field.sle_next = (slistelm)->field.sle_next; \ 136 | (slistelm)->field.sle_next = (elm); \ 137 | } while (0) 138 | 139 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 140 | (elm)->field.sle_next = (head)->slh_first; \ 141 | (head)->slh_first = (elm); \ 142 | } while (0) 143 | 144 | #define SLIST_REMOVE_AFTER(elm, field) do { \ 145 | (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ 146 | } while (0) 147 | 148 | #define SLIST_REMOVE_HEAD(head, field) do { \ 149 | (head)->slh_first = (head)->slh_first->field.sle_next; \ 150 | } while (0) 151 | 152 | #define SLIST_REMOVE(head, elm, type, field) do { \ 153 | if ((head)->slh_first == (elm)) { \ 154 | SLIST_REMOVE_HEAD((head), field); \ 155 | } else { \ 156 | struct type *curelm = (head)->slh_first; \ 157 | \ 158 | while (curelm->field.sle_next != (elm)) \ 159 | curelm = curelm->field.sle_next; \ 160 | curelm->field.sle_next = \ 161 | curelm->field.sle_next->field.sle_next; \ 162 | } \ 163 | _Q_INVALIDATE((elm)->field.sle_next); \ 164 | } while (0) 165 | 166 | /* 167 | * List definitions. 168 | */ 169 | #define LIST_HEAD(name, type) \ 170 | struct name { \ 171 | struct type *lh_first; /* first element */ \ 172 | } 173 | 174 | #define LIST_HEAD_INITIALIZER(head) \ 175 | { NULL } 176 | 177 | #define LIST_ENTRY(type) \ 178 | struct { \ 179 | struct type *le_next; /* next element */ \ 180 | struct type **le_prev; /* address of previous next element */ \ 181 | } 182 | 183 | /* 184 | * List access methods. 185 | */ 186 | #define LIST_FIRST(head) ((head)->lh_first) 187 | #define LIST_END(head) NULL 188 | #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) 189 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 190 | 191 | #define LIST_FOREACH(var, head, field) \ 192 | for((var) = LIST_FIRST(head); \ 193 | (var)!= LIST_END(head); \ 194 | (var) = LIST_NEXT(var, field)) 195 | 196 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 197 | for ((var) = LIST_FIRST(head); \ 198 | (var) && ((tvar) = LIST_NEXT(var, field), 1); \ 199 | (var) = (tvar)) 200 | 201 | /* 202 | * List functions. 203 | */ 204 | #define LIST_INIT(head) do { \ 205 | LIST_FIRST(head) = LIST_END(head); \ 206 | } while (0) 207 | 208 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 209 | if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ 210 | (listelm)->field.le_next->field.le_prev = \ 211 | &(elm)->field.le_next; \ 212 | (listelm)->field.le_next = (elm); \ 213 | (elm)->field.le_prev = &(listelm)->field.le_next; \ 214 | } while (0) 215 | 216 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 217 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 218 | (elm)->field.le_next = (listelm); \ 219 | *(listelm)->field.le_prev = (elm); \ 220 | (listelm)->field.le_prev = &(elm)->field.le_next; \ 221 | } while (0) 222 | 223 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 224 | if (((elm)->field.le_next = (head)->lh_first) != NULL) \ 225 | (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ 226 | (head)->lh_first = (elm); \ 227 | (elm)->field.le_prev = &(head)->lh_first; \ 228 | } while (0) 229 | 230 | #define LIST_REMOVE(elm, field) do { \ 231 | if ((elm)->field.le_next != NULL) \ 232 | (elm)->field.le_next->field.le_prev = \ 233 | (elm)->field.le_prev; \ 234 | *(elm)->field.le_prev = (elm)->field.le_next; \ 235 | _Q_INVALIDATE((elm)->field.le_prev); \ 236 | _Q_INVALIDATE((elm)->field.le_next); \ 237 | } while (0) 238 | 239 | #define LIST_REPLACE(elm, elm2, field) do { \ 240 | if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ 241 | (elm2)->field.le_next->field.le_prev = \ 242 | &(elm2)->field.le_next; \ 243 | (elm2)->field.le_prev = (elm)->field.le_prev; \ 244 | *(elm2)->field.le_prev = (elm2); \ 245 | _Q_INVALIDATE((elm)->field.le_prev); \ 246 | _Q_INVALIDATE((elm)->field.le_next); \ 247 | } while (0) 248 | 249 | /* 250 | * Simple queue definitions. 251 | */ 252 | #define SIMPLEQ_HEAD(name, type) \ 253 | struct name { \ 254 | struct type *sqh_first; /* first element */ \ 255 | struct type **sqh_last; /* addr of last next element */ \ 256 | } 257 | 258 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ 259 | { NULL, &(head).sqh_first } 260 | 261 | #define SIMPLEQ_ENTRY(type) \ 262 | struct { \ 263 | struct type *sqe_next; /* next element */ \ 264 | } 265 | 266 | /* 267 | * Simple queue access methods. 268 | */ 269 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) 270 | #define SIMPLEQ_END(head) NULL 271 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) 272 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) 273 | 274 | #define SIMPLEQ_FOREACH(var, head, field) \ 275 | for((var) = SIMPLEQ_FIRST(head); \ 276 | (var) != SIMPLEQ_END(head); \ 277 | (var) = SIMPLEQ_NEXT(var, field)) 278 | 279 | #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 280 | for ((var) = SIMPLEQ_FIRST(head); \ 281 | (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ 282 | (var) = (tvar)) 283 | 284 | /* 285 | * Simple queue functions. 286 | */ 287 | #define SIMPLEQ_INIT(head) do { \ 288 | (head)->sqh_first = NULL; \ 289 | (head)->sqh_last = &(head)->sqh_first; \ 290 | } while (0) 291 | 292 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 293 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ 294 | (head)->sqh_last = &(elm)->field.sqe_next; \ 295 | (head)->sqh_first = (elm); \ 296 | } while (0) 297 | 298 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 299 | (elm)->field.sqe_next = NULL; \ 300 | *(head)->sqh_last = (elm); \ 301 | (head)->sqh_last = &(elm)->field.sqe_next; \ 302 | } while (0) 303 | 304 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 305 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ 306 | (head)->sqh_last = &(elm)->field.sqe_next; \ 307 | (listelm)->field.sqe_next = (elm); \ 308 | } while (0) 309 | 310 | #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ 311 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ 312 | (head)->sqh_last = &(head)->sqh_first; \ 313 | } while (0) 314 | 315 | #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 316 | if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ 317 | == NULL) \ 318 | (head)->sqh_last = &(elm)->field.sqe_next; \ 319 | } while (0) 320 | 321 | #define SIMPLEQ_CONCAT(head1, head2) do { \ 322 | if (!SIMPLEQ_EMPTY((head2))) { \ 323 | *(head1)->sqh_last = (head2)->sqh_first; \ 324 | (head1)->sqh_last = (head2)->sqh_last; \ 325 | SIMPLEQ_INIT((head2)); \ 326 | } \ 327 | } while (0) 328 | 329 | /* 330 | * XOR Simple queue definitions. 331 | */ 332 | #define XSIMPLEQ_HEAD(name, type) \ 333 | struct name { \ 334 | struct type *sqx_first; /* first element */ \ 335 | struct type **sqx_last; /* addr of last next element */ \ 336 | unsigned long sqx_cookie; \ 337 | } 338 | 339 | #define XSIMPLEQ_ENTRY(type) \ 340 | struct { \ 341 | struct type *sqx_next; /* next element */ \ 342 | } 343 | 344 | /* 345 | * XOR Simple queue access methods. 346 | */ 347 | #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ 348 | (unsigned long)(ptr))) 349 | #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) 350 | #define XSIMPLEQ_END(head) NULL 351 | #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) 352 | #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) 353 | 354 | 355 | #define XSIMPLEQ_FOREACH(var, head, field) \ 356 | for ((var) = XSIMPLEQ_FIRST(head); \ 357 | (var) != XSIMPLEQ_END(head); \ 358 | (var) = XSIMPLEQ_NEXT(head, var, field)) 359 | 360 | #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 361 | for ((var) = XSIMPLEQ_FIRST(head); \ 362 | (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ 363 | (var) = (tvar)) 364 | 365 | /* 366 | * XOR Simple queue functions. 367 | */ 368 | #define XSIMPLEQ_INIT(head) do { \ 369 | arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ 370 | (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ 371 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 372 | } while (0) 373 | 374 | #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 375 | if (((elm)->field.sqx_next = (head)->sqx_first) == \ 376 | XSIMPLEQ_XOR(head, NULL)) \ 377 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 378 | (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ 379 | } while (0) 380 | 381 | #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 382 | (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ 383 | *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ 384 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 385 | } while (0) 386 | 387 | #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 388 | if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ 389 | XSIMPLEQ_XOR(head, NULL)) \ 390 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 391 | (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ 392 | } while (0) 393 | 394 | #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ 395 | if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ 396 | (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ 397 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 398 | } while (0) 399 | 400 | #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 401 | if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ 402 | (elm)->field.sqx_next)->field.sqx_next) \ 403 | == XSIMPLEQ_XOR(head, NULL)) \ 404 | (head)->sqx_last = \ 405 | XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 406 | } while (0) 407 | 408 | 409 | /* 410 | * Tail queue definitions. 411 | */ 412 | #define TAILQ_HEAD(name, type) \ 413 | struct name { \ 414 | struct type *tqh_first; /* first element */ \ 415 | struct type **tqh_last; /* addr of last next element */ \ 416 | } 417 | 418 | #define TAILQ_HEAD_INITIALIZER(head) \ 419 | { NULL, &(head).tqh_first } 420 | 421 | #define TAILQ_ENTRY(type) \ 422 | struct { \ 423 | struct type *tqe_next; /* next element */ \ 424 | struct type **tqe_prev; /* address of previous next element */ \ 425 | } 426 | 427 | /* 428 | * Tail queue access methods. 429 | */ 430 | #define TAILQ_FIRST(head) ((head)->tqh_first) 431 | #define TAILQ_END(head) NULL 432 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 433 | #define TAILQ_LAST(head, headname) \ 434 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 435 | /* XXX */ 436 | #define TAILQ_PREV(elm, headname, field) \ 437 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 438 | #define TAILQ_EMPTY(head) \ 439 | (TAILQ_FIRST(head) == TAILQ_END(head)) 440 | 441 | #define TAILQ_FOREACH(var, head, field) \ 442 | for((var) = TAILQ_FIRST(head); \ 443 | (var) != TAILQ_END(head); \ 444 | (var) = TAILQ_NEXT(var, field)) 445 | 446 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 447 | for ((var) = TAILQ_FIRST(head); \ 448 | (var) != TAILQ_END(head) && \ 449 | ((tvar) = TAILQ_NEXT(var, field), 1); \ 450 | (var) = (tvar)) 451 | 452 | 453 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 454 | for((var) = TAILQ_LAST(head, headname); \ 455 | (var) != TAILQ_END(head); \ 456 | (var) = TAILQ_PREV(var, headname, field)) 457 | 458 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 459 | for ((var) = TAILQ_LAST(head, headname); \ 460 | (var) != TAILQ_END(head) && \ 461 | ((tvar) = TAILQ_PREV(var, headname, field), 1); \ 462 | (var) = (tvar)) 463 | 464 | /* 465 | * Tail queue functions. 466 | */ 467 | #define TAILQ_INIT(head) do { \ 468 | (head)->tqh_first = NULL; \ 469 | (head)->tqh_last = &(head)->tqh_first; \ 470 | } while (0) 471 | 472 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 473 | if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ 474 | (head)->tqh_first->field.tqe_prev = \ 475 | &(elm)->field.tqe_next; \ 476 | else \ 477 | (head)->tqh_last = &(elm)->field.tqe_next; \ 478 | (head)->tqh_first = (elm); \ 479 | (elm)->field.tqe_prev = &(head)->tqh_first; \ 480 | } while (0) 481 | 482 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 483 | (elm)->field.tqe_next = NULL; \ 484 | (elm)->field.tqe_prev = (head)->tqh_last; \ 485 | *(head)->tqh_last = (elm); \ 486 | (head)->tqh_last = &(elm)->field.tqe_next; \ 487 | } while (0) 488 | 489 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 490 | if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ 491 | (elm)->field.tqe_next->field.tqe_prev = \ 492 | &(elm)->field.tqe_next; \ 493 | else \ 494 | (head)->tqh_last = &(elm)->field.tqe_next; \ 495 | (listelm)->field.tqe_next = (elm); \ 496 | (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ 497 | } while (0) 498 | 499 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 500 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 501 | (elm)->field.tqe_next = (listelm); \ 502 | *(listelm)->field.tqe_prev = (elm); \ 503 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ 504 | } while (0) 505 | 506 | #define TAILQ_REMOVE(head, elm, field) do { \ 507 | if (((elm)->field.tqe_next) != NULL) \ 508 | (elm)->field.tqe_next->field.tqe_prev = \ 509 | (elm)->field.tqe_prev; \ 510 | else \ 511 | (head)->tqh_last = (elm)->field.tqe_prev; \ 512 | *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ 513 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 514 | _Q_INVALIDATE((elm)->field.tqe_next); \ 515 | } while (0) 516 | 517 | #define TAILQ_REPLACE(head, elm, elm2, field) do { \ 518 | if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ 519 | (elm2)->field.tqe_next->field.tqe_prev = \ 520 | &(elm2)->field.tqe_next; \ 521 | else \ 522 | (head)->tqh_last = &(elm2)->field.tqe_next; \ 523 | (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ 524 | *(elm2)->field.tqe_prev = (elm2); \ 525 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 526 | _Q_INVALIDATE((elm)->field.tqe_next); \ 527 | } while (0) 528 | 529 | #define TAILQ_CONCAT(head1, head2, field) do { \ 530 | if (!TAILQ_EMPTY(head2)) { \ 531 | *(head1)->tqh_last = (head2)->tqh_first; \ 532 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 533 | (head1)->tqh_last = (head2)->tqh_last; \ 534 | TAILQ_INIT((head2)); \ 535 | } \ 536 | } while (0) 537 | 538 | #endif /* !_SYS_QUEUE_H_ */ 539 | 540 | #ifdef __cplusplus 541 | } 542 | #endif 543 | --------------------------------------------------------------------------------