Index: bpflogd/Makefile =================================================================== RCS file: bpflogd/Makefile diff -N bpflogd/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bpflogd/Makefile 24 Apr 2025 03:56:54 -0000 @@ -0,0 +1,13 @@ + +PROG= bpflogd +SRCS= bpflogd.c +SRCS+= log.c +MAN= bpflogd.8 + +LDADD=-lpcap -levent +DPADD=${LIBPCAP} ${LIBEVENT} + +DEBUG=-g +WARNINGS=yes + +.include Index: bpflogd/bpflogd.8 =================================================================== RCS file: bpflogd/bpflogd.8 diff -N bpflogd/bpflogd.8 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bpflogd/bpflogd.8 24 Apr 2025 03:56:54 -0000 @@ -0,0 +1,129 @@ +.\" $OpenBSD: pflogd.8,v 1.51 2019/08/30 17:51:47 jmc Exp $ +.\" +.\" Copyright (c) 2001 Can Erkin Acar. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: August 30 2019 $ +.Dt BPFLOGD 8 +.Os +.Sh NAME +.Nm bpflogd +.Nd Berkely Packet Filter logging daemon +.Sh SYNOPSIS +.Nm bpflogd +.Op Fl dpP +.Op Fl F Ar filterfile +.Op Fl s Ar snaplen +.Op Fl w Ar waitms +.Op Fl y Ar datalinktype +.Fl f Ar filename +.Fl i Ar interface +.Op Ar expression +.Sh DESCRIPTION +.Nm +is a daemon which captures packets using +.Xr bpf 4 +and writes the packets to a logfile +in +.Xr tcpdump 8 +binary format. +These logs can be reviewed later using the +.Fl r +option of +.Xr tcpdump 8 . +.Pp +.Nm +closes and then re-opens the log file when it receives +.Dv SIGHUP , +permitting +.Xr newsyslog 8 +to rotate logfiles automatically. +.Pp +If the log file contains data when starting or a +.Dv SIGHUP , +the PCAP header is checked before new logs are appended to the existing file. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Debugging mode. +.Nm +does not daemonise and logs to the terminal. +.It Fl f Ar filename +Log output filename. +The file must already exist, and be readable and writable by the +_pflogd user. +.It Fl F Ar filterfile +Specify a file containing a filter expression as per +.Xr pcap-filter 5 . +.It Fl i Ar interface +Specifies the interface to capture packets on using +.Xr bpf 4 . +This can be specified multiple times to capture packets from multiple +interfaces, but all the interfaces must support the same datalink type. +.It Fl p +Do not put the interfaces into promiscuous mode. +This is the default. +.It Fl P +Put the interfaces into promiscuous mode. +.It Fl s Ar snaplen +Capture at most the first +.Ar snaplen +bytes of data from each packet. +By default +.Nm +captures whole packets. +.It Fl w Ar waitms +Specify the maximum amount of time in milliseconds between when a +packet is captured and when it will be written to the log file. +The default +.Ar waitms +value is 2000 milliseconds. +.It Fl y Ar datalinktype +Specify the datalink type when capturing packets. +If this is not specified then the default datalink type on the first +interface is used. +.It Ar expression +Specify a filter expression for matching packets as per +.Xr pcap-filter 5 . +.El +.Pp +A filter expression may only be specified by a file with +.Ar -F +or as arguments on the command line, specifying both is unsupported. +If a filter is not provided then all packets are captured. +.Sh SEE ALSO +.Xr pcap_open_live 3 , +.Xr pcap-filter 5 , +.Xr newsyslog 8 , +.Xr tcpdump 8 +.Sh HISTORY +The +.Nm +command appeared in +.Ox 7.8 . +.\" .Sh AUTHORS +.\" .Nm +.\" was written by +.\" .An David Gwynne Aq Mt dlg@uq.edu.au . Index: bpflogd/bpflogd.c =================================================================== RCS file: bpflogd/bpflogd.c diff -N bpflogd/bpflogd.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bpflogd/bpflogd.c 24 Apr 2025 03:56:54 -0000 @@ -0,0 +1,686 @@ +/* */ + +/* + * Copyright (c) 2025 The University of Queensland + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "log.h" + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +int rdaemon(int); + +#define BPFLOGD_USER "_pflogd" + +#define _DEV_BPF "/dev/bpf" + +struct bpflogd; + +struct bpfif { + struct bpflogd *bif_bd; + const char *bif_name; + int bif_bpf; + + struct event bif_ev; + + TAILQ_ENTRY(bpfif) bif_entry; +}; + +TAILQ_HEAD(bpfif_list, bpfif); + +struct bpflogd { + const char *bd_user; + const char *bd_fname; + int bd_fd; + unsigned int bd_snaplen; + int bd_dlt; + const char *bd_dlt_name; + + struct bpfif_list bd_bif_list; + + int bd_buflen; + void *bd_buf; + + struct event bd_sighup; +}; + +static int bpf_maxbufsize(void); + +static void bpflogd_hup(int, short, void *); + +static void bpfif_open(struct bpfif *); +static void bpfif_read(int, short, void *); + +static int bpflog_open(struct bpflogd *); + +__dead static void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dpP] [-F expression] [-s snaplen] " + "[-w delay] [-y datalinktype]" "\n" + "\t" "-f filename -i interface [expression]\n", __progname); + + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct bpflogd _bd = { + .bd_user = BPFLOGD_USER, + .bd_fname = NULL, + .bd_fd = -1, + .bd_buflen = bpf_maxbufsize(), + .bd_snaplen = BPF_MAXBUFSIZE, + .bd_dlt = -1, + .bd_bif_list = TAILQ_HEAD_INITIALIZER(_bd.bd_bif_list), + }; + struct bpflogd *bd = &_bd; + struct bpfif *bif, *bif0; + struct bpf_version bv; + char *filter = NULL; + struct bpf_program bf; + struct bpf_insn insns[] = { { BPF_RET, 0, 0, -1 } }; + int devnull = -1; + int debug = 0; + int promisc = 0; + int waitms = 1000; + struct timeval waittv; + struct passwd *pw; + + int ch; + const char *errstr; + + while ((ch = getopt(argc, argv, "df:F:i:pPs:u:w:y:")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'f': + bd->bd_fname = optarg; + break; + case 'F': + filter = optarg; + break; + case 'i': + TAILQ_FOREACH(bif, &bd->bd_bif_list, bif_entry) { + if (strcmp(bif->bif_name, optarg) == 0) { + errx(1, "interface %s already exists", + optarg); + } + } + + bif = malloc(sizeof(*bif)); + if (bif == NULL) + err(1, "bpf interface alloc"); + bif->bif_bd = bd; + bif->bif_name = optarg; + TAILQ_INSERT_TAIL(&bd->bd_bif_list, bif, bif_entry); + break; + case 'p': + promisc = 0; + break; + case 'P': + promisc = 1; + break; + case 's': + bd->bd_snaplen = strtonum(optarg, 60, BPF_MAXBUFSIZE, + &errstr); + if (errstr != NULL) + errx(1, "snaplen: %s", errstr); + break; + case 'u': + bd->bd_user = optarg; + break; + case 'w': + waitms = strtonum(optarg, 10, 300000, &errstr); + if (errstr != NULL) + errx(1, "wait ms: %s", errstr); + break; + case 'y': + bd->bd_dlt = pcap_datalink_name_to_val(optarg); + if (bd->bd_dlt == -1) { + errx(1, "%s: unknown datalink type name", + optarg); + } + bd->bd_dlt_name = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (bd->bd_fname == NULL) { + warnx("output file not specified"); + usage(); + } + + bif0 = TAILQ_FIRST(&bd->bd_bif_list); + if (bif0 == NULL) { + warnx("no interfaces specified"); + usage(); + } + + if (filter != NULL && argc > 0) { + warnx("use either -F or extra arguments, not both"); + usage(); + } + + signal(SIGPIPE, SIG_IGN); + + if (geteuid()) + errx(1, "need root privileges"); + + pw = getpwnam(bd->bd_user); + if (pw == NULL) + errx(1, "%s: unknown user", bd->bd_user); + + bd->bd_buf = malloc(bd->bd_buflen); + if (bd->bd_buf == NULL) + err(1, "bpf read buffer"); + + bpfif_open(bif0); /* err on failure */ + + if (ioctl(bif0->bif_bpf, BIOCVERSION, &bv) == -1) + err(1, "%s: get filter language version", bif0->bif_name); + + if (bv.bv_major != BPF_MAJOR_VERSION) { + errx(1, "bpf major %u, expected %u", + bv.bv_major, BPF_MAJOR_VERSION); + } + if (bv.bv_minor < BPF_MINOR_VERSION) { + errx(1, "bpf minor %u, expected >= %u", + bv.bv_minor, BPF_MINOR_VERSION); + } + + if (bd->bd_dlt != -1) { + if (ioctl(bif0->bif_bpf, BIOCSDLT, &bd->bd_dlt) == -1) { + err(1, "%s: unsupported datalink type %s", + bif0->bif_name, bd->bd_dlt_name); + } + } else { + if (ioctl(bif0->bif_bpf, BIOCGDLT, &bd->bd_dlt) == -1) + err(1, "%s: get datalink type", bif0->bif_name); + + bd->bd_dlt_name = pcap_datalink_val_to_name(bd->bd_dlt); + if (bd->bd_dlt_name == NULL) { + errx(1, "%s: datalink type %d is unknown to libpcap", + bif0->bif_name, bd->bd_dlt); + } + } + + if (filter != NULL || argc > 0) { + pcap_t *ph; + char *expr = NULL; + int i; + + if (filter != NULL) { + int fd; + ssize_t rv; + + fd = open(filter, O_RDONLY); + if (fd == -1) + err(1, "%s", filter); + +#define BPFLOG_FILTER_MAX 8192 + + expr = malloc(BPFLOG_FILTER_MAX); + if (expr == NULL) + err(1, NULL); + + rv = read(fd, expr, BPFLOG_FILTER_MAX); + if (rv == -1) + err(1, "%s read", filter); + if (rv == 0) + errx(1, "%s is empty", filter); + if (rv >= BPFLOG_FILTER_MAX - 1) + errx(1, "%s is too long", filter); + + close(fd); + } else if (argc == 1) + expr = argv[0]; + else { + size_t alen = strlen(argv[0]); + size_t len = alen; + + expr = malloc(len + 1); + if (expr == NULL) + err(1, "bpf expression buffer"); + + memcpy(expr, argv[0], alen); + + for (i = 1; i < argc; i++) { + size_t nlen; + + alen = strlen(argv[i]); + if (alen == 0) + continue; + + nlen = len + 1 + alen; + + expr = realloc(expr, nlen + 1); + if (expr == NULL) + err(1, "bpf expression buffer"); + + expr[len] = ' '; + memcpy(expr + len + 1, argv[i], alen); + + len = nlen; + } + expr[len] = '\0'; + } + + ph = pcap_open_dead(bd->bd_dlt, bd->bd_snaplen); + if (ph == NULL) + err(1, "pcap_open_dead"); + + if (pcap_compile(ph, &bf, expr, 1, PCAP_NETMASK_UNKNOWN) == -1) + errx(1, "%s", pcap_geterr(ph)); + + pcap_close(ph); + + if (argc != 1) + free(expr); + } else { + insns[0].k = bd->bd_snaplen; + bf.bf_insns = insns; + bf.bf_len = nitems(insns); + } + + bif = bif0; + while ((bif = TAILQ_NEXT(bif, bif_entry)) != NULL) { + bpfif_open(bif); /* err on failure */ + + if (ioctl(bif->bif_bpf, BIOCSDLT, &bd->bd_dlt) == -1) { + err(1, "%s: unsupported datalink type %s", + bif->bif_name, bd->bd_dlt_name); + } + } + + waittv.tv_sec = waitms / 1000; + waittv.tv_usec = (waitms % 1000) * 1000; + + TAILQ_FOREACH(bif, &bd->bd_bif_list, bif_entry) { + if (ioctl(bif->bif_bpf, BIOCSETF, &bf) == -1) + err(1, "%s: set filter", bif0->bif_name); + + if (promisc) { + if (ioctl(bif->bif_bpf, BIOCPROMISC, NULL) == -1) + err(1, "%s: enable promisc", bif0->bif_name); + } + + if (ioctl(bif->bif_bpf, BIOCSWTIMEOUT, &waittv) == -1) + err(1, "%s: wait ms %d", bif0->bif_name, waitms); + } + + if (bf.bf_insns != insns) + pcap_freecode(&bf); + + if (!debug) { + devnull = open(_PATH_DEVNULL, O_RDWR); + if (devnull == -1) + err(1, "%s", _PATH_DEVNULL); + } + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + errx(1, "can't drop privileges"); + + endpwent(); + + if (unveil(bd->bd_fname, "rwc") == -1) + err(1, "unveil %s", bd->bd_fname); + + bd->bd_fd = bpflog_open(bd); + if (bd->bd_fd == -1) { + /* error has already been printed */ + exit(1); + } + + if (!debug) { + extern char *__progname; + + if (rdaemon(devnull) == -1) + err(1, "unable to daemonize"); + + logger_syslog(__progname); + } + + event_init(); + + signal_set(&bd->bd_sighup, SIGHUP, bpflogd_hup, bd); + signal_add(&bd->bd_sighup, NULL); + + TAILQ_FOREACH(bif, &bd->bd_bif_list, bif_entry) { + event_set(&bif->bif_ev, bif->bif_bpf, EV_READ | EV_PERSIST, + bpfif_read, bif); + event_add(&bif->bif_ev, NULL); + } + + event_dispatch(); + + return (0); +} + +static int +bpf_maxbufsize(void) +{ + int mib[] = { CTL_NET, PF_BPF, NET_BPF_MAXBUFSIZE }; + int maxbuf; + size_t maxbufsize = sizeof(maxbuf); + + if (sysctl(mib, nitems(mib), &maxbuf, &maxbufsize, NULL, 0) == -1) + return (-1); + + if (maxbuf > 1 << 20) + maxbuf = 1 << 20; + + return (maxbuf); +} + +static void +bpflogd_hup(int nil, short events, void *arg) +{ + struct bpflogd *bd = arg; + struct bpfif *bif; + int fd; + + fd = bpflog_open(bd); + + TAILQ_FOREACH(bif, &bd->bd_bif_list, bif_entry) + bpfif_read(bif->bif_bpf, 0, bif); + + close(bd->bd_fd); + + if (fd == -1) + lerrx(1, "exiting"); + + bd->bd_fd = fd; + + linfo("%s turned over", bd->bd_fname); +} + +static int +bpflog_open(struct bpflogd *bd) +{ + const struct pcap_file_header pfh = { + .magic = 0xa1b2c3d4, + .version_major = PCAP_VERSION_MAJOR, + .version_minor = PCAP_VERSION_MINOR, + .thiszone = 0, /* we work in UTC */ + .sigfigs = 0, + .snaplen = BPF_MAXBUFSIZE, + .linktype = bd->bd_dlt, + }; + struct pcap_file_header epfh; + struct stat st; + ssize_t rv; + int fd; + + fd = open(bd->bd_fname, O_RDWR|O_APPEND); + if (fd == -1) { + lwarn("%s", bd->bd_fname); + return (-1); + } + + if (fstat(fd, &st) == -1) { + lwarn("%s stat", bd->bd_fname); + goto close; + } + + if (st.st_size == 0) { + rv = write(fd, &pfh, sizeof(pfh)); + if (rv == -1) { + lwarn("%s pcap file header write", bd->bd_fname); + goto close; + } + if ((size_t)rv < sizeof(pfh)) { + lwarnx("%s pcap file header short write", + bd->bd_fname); + goto close; + } + + return (fd); + } + + rv = pread(fd, &epfh, sizeof(epfh), 0); + if (rv == -1) { + lwarn("%s pcap file header read", bd->bd_fname); + goto close; + } + if ((size_t)rv < sizeof(epfh)) { + lwarn("%s pcap file header is short", bd->bd_fname); + goto close; + } + + if (epfh.magic != pfh.magic) { + lwarnx("%s pcap file header magic is wrong", + bd->bd_fname); + goto close; + } + if (epfh.version_major != pfh.version_major || + epfh.version_minor < pfh.version_minor) { + lwarnx("%s pcap file header version is unsupported", + bd->bd_fname); + goto close; + } + if (epfh.thiszone != pfh.thiszone) { + lwarnx("%s pcap file timezone is different", bd->bd_fname); + goto close; + } + if (epfh.snaplen < bd->bd_snaplen) { + lwarnx("%s pcap file snaplen is too short", bd->bd_fname); + goto close; + } + if (epfh.linktype != pfh.linktype) { + lwarnx("%s pcap file linktype is different", bd->bd_fname); + goto close; + } + + return (fd); + +close: + close(fd); + return (-1); +} + +static void +bpfif_open(struct bpfif *bif) +{ + struct bpflogd *bd = bif->bif_bd; + struct ifreq ifr; + + bif->bif_bpf = open(_DEV_BPF, O_RDWR | O_NONBLOCK); + if (bif->bif_bpf == -1) + err(1, "%s: open %s", bif->bif_name, _DEV_BPF); + + memset(&ifr, 0, sizeof(ifr)); + if (strlcpy(ifr.ifr_name, bif->bif_name, sizeof(ifr.ifr_name)) >= + sizeof(ifr.ifr_name)) + errx(1, "%s: interface name is too long", bif->bif_name); + + if (ioctl(bif->bif_bpf, BIOCSBLEN, &bd->bd_buflen) == -1) { + err(1, "%s: set buffer length %d", bif->bif_name, + bd->bd_buflen); + } + + if (ioctl(bif->bif_bpf, BIOCSETIF, &ifr) == -1) + err(1, "%s: set bpf interface", bif->bif_name); +} + +#define PCAP_PKTS 64 + +static void +bpfif_read(int fd, short events, void *arg) +{ + struct bpfif *bif = arg; + struct bpflogd *bd = bif->bif_bd; + ssize_t rv; + size_t len, bpflen, caplen; + uint8_t *buf; + + struct pcap_pkthdr pps[PCAP_PKTS], *pp; + struct iovec iovs[PCAP_PKTS * 2], *iov = iovs; + size_t np = 0; + + rv = read(fd, bd->bd_buf, bd->bd_buflen); + switch (rv) { + case -1: + switch (errno) { + case EINTR: + case EAGAIN: + break; + default: + lerr(1, "%s bpf read", bif->bif_name); + /* NOTREACHED */ + } + return; + case 0: + /* bpf buffer is empty */ + return; + default: + break; + } + + buf = bd->bd_buf; + len = rv; + + for (;;) { + const struct bpf_hdr *bh; + + /* the kernel lied to us. */ + if (len < sizeof(*bh)) + lerrx(1, "%s: short bpf header", bif->bif_name); + + bh = (const struct bpf_hdr *)buf; + bpflen = bh->bh_hdrlen + bh->bh_caplen; + + /* + * If the bpf header plus data doesn't fit in what's + * left of the buffer, we've got a problem... + */ + if (bpflen > len) + lerrx(1, "%s: short bpf read", bif->bif_name); + + if (np >= PCAP_PKTS) { + rv = writev(bd->bd_fd, iovs, iov - iovs); + if (rv == -1) + lwarn("%s write", bd->bd_fname); + iov = iovs; + np = 0; + } + + caplen = bh->bh_caplen; + if (caplen > BPF_MAXBUFSIZE) + caplen = BPF_MAXBUFSIZE; + + pp = &pps[np++]; + + pp->ts = bh->bh_tstamp; + pp->caplen = caplen; + pp->len = bh->bh_datalen; + + iov->iov_base = pp; + iov->iov_len = sizeof(*pp); + iov++; + iov->iov_base = buf + bh->bh_hdrlen; + iov->iov_len = caplen; + iov++; + + bpflen = BPF_WORDALIGN(bpflen); + if (len <= bpflen) { + /* everything is consumed */ + break; + } + + /* Move the lop to the next packet */ + buf += bpflen; + len -= bpflen; + } + + if (np > 0) { + rv = writev(bd->bd_fd, iovs, iov - iovs); + if (rv == -1) + lwarn("%s write", bd->bd_fname); + } + + fsync(bd->bd_fd); +} + +/* daemon(3) clone, intended to be used in a "r"estricted environment */ +int +rdaemon(int devnull) +{ + if (devnull == -1) { + errno = EBADF; + return (-1); + } + if (fcntl(devnull, F_GETFL) == -1) + return (-1); + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + (void)dup2(devnull, STDIN_FILENO); + (void)dup2(devnull, STDOUT_FILENO); + (void)dup2(devnull, STDERR_FILENO); + if (devnull > 2) + (void)close(devnull); + + return (0); +} Index: bpflogd/log.c =================================================================== RCS file: bpflogd/log.c diff -N bpflogd/log.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bpflogd/log.c 24 Apr 2025 03:56:54 -0000 @@ -0,0 +1,150 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static const struct __logger conslogger = { + err, + errx, + warn, + warnx, + warnx, /* info */ + warnx /* debug */ +}; + +__dead static void syslog_err(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead static void syslog_errx(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +static void syslog_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_vstrerror(int, int, const char *, va_list) + __attribute__((__format__ (printf, 3, 0))); + +static const struct __logger syslogger = { + syslog_err, + syslog_errx, + syslog_warn, + syslog_warnx, + syslog_info, + syslog_debug +}; + +const struct __logger *__logger = &conslogger; + +void +logger_syslog(const char *progname) +{ + openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + tzset(); + + __logger = &syslogger; +} + +static void +syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) == -1) { + syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); + exit(1); + } + + syslog(priority, "%s: %s", s, strerror(e)); + + free(s); +} + +static void +syslog_err(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_errx(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_INFO, fmt, ap); + va_end(ap); +} + +static void +syslog_debug(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); +} Index: bpflogd/log.h =================================================================== RCS file: bpflogd/log.h diff -N bpflogd/log.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bpflogd/log.h 24 Apr 2025 03:56:54 -0000 @@ -0,0 +1,47 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +struct __logger { + __dead void (*err)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + __dead void (*errx)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + void (*warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*warnx)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +}; + +extern const struct __logger *__logger; + +#define lerr(_e, _f...) __logger->err((_e), _f) +#define lerrx(_e, _f...) __logger->errx((_e), _f) +#define lwarn(_f...) __logger->warn(_f) +#define lwarnx(_f...) __logger->warnx(_f) +#define linfo(_f...) __logger->info(_f) +#define ldebug(_f...) __logger->debug(_f) + +void logger_syslog(const char *); + +#endif /* _LOG_H_ */