From 92a4fb3318709b0daed4701d6710abc485107460 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 5 May 2021 05:06:41 +0200 Subject: [PATCH] Fix #29: prevent repeating kernel messages when syslogd is restarted This patch fixes the problem with kernel messages being repeated when syslogd is restarted at runtime. This is achieved by caching the last seqno read from /dev/kmsg to /run/syslogd.cache. The latter is usually a ram disk these days so it should be a fairly quick op. Excessive updates are prevented by only caching after handling all callbacks in the socket_poll() loop, and only updating the cache if there has been any new kernel messages since last update. Signed-off-by: Joachim Wiberg --- ChangeLog.md | 1 + src/syslogd.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/syslogd.h | 4 ++++ 3 files changed, 63 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 4812db3..e9ff49d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,6 +14,7 @@ Bug fix release. the bundled logger, the `syslog()` API in the C library is used, which may not necessarily set the timestamp. When sysklogd infers timestamp it was offset by the number of years since 1969, i.e., `boot_time` +- Issue #29: kernel messages repeated if syslogd is restarted at runtime - Issue #31: time calculation issue on 32-bit systems - Issue #32: remote kernel messages being mapped to uucp instead of user diff --git a/src/syslogd.c b/src/syslogd.c index 5741e99..25d4216 100644 --- a/src/syslogd.c +++ b/src/syslogd.c @@ -60,6 +60,7 @@ static char sccsid[] __attribute__((unused)) = #include #include #include +#include #include #include #include @@ -122,6 +123,7 @@ struct filed consfile; static int Debug; /* debug flag */ static int Foreground = 0; /* don't fork - don't run in daemon mode */ static time_t boot_time; /* Offset for printsys() */ +static uint64_t sys_seqno = 0; /* Last seen kernel log message */ static int resolve = 1; /* resolve hostname */ static char LocalHostName[MAXHOSTNAMELEN + 1]; /* our hostname */ static char *LocalDomain; /* our local domain name */ @@ -195,6 +197,54 @@ static int addpeer(struct peer *pe0) return 0; } +static void sys_seqno_load(void) +{ + char buf[32], *str; + FILE *fp; + + fp = fopen(_PATH_CACHE, "r"); + if (!fp) + return; + + while ((str = fgets(buf, sizeof(buf), fp))) { + uint64_t val; + char *end; + + if (str[strlen(str) - 1] == '\n') + str[strlen(str) - 1] = 0; + + errno = 0; + val = strtoull(str, &end, 10); + if (val == 0 && end == str) + break; /* str was not a number */ + else if (val == ULLONG_MAX && errno) + break; /* the value of str does not fit in unsigned long long */ + else if (*end) + break; /* str began with a number but has junk left over at the end */ + + sys_seqno = val; + } + fclose(fp); +} + +static void sys_seqno_save(void) +{ + static uint64_t prev = 0; + FILE *fp; + + if (prev == sys_seqno) + return; /* no changes since last save */ + + fp = fopen(_PATH_CACHE, "w"); + if (!fp) + return; /* best effort, ignore any errors */ + + fprintf(fp, "%" PRIu64 "\n", sys_seqno); + fclose(fp); + + prev = sys_seqno; +} + int usage(int code) { printf("Usage:\n" @@ -389,6 +439,7 @@ int main(int argc, char *argv[]) * /dev/kmsg and fall back to _PROC_KLOG, which on GLIBC * systems is /proc/kmsg, and /dev/klog on *BSD. */ + sys_seqno_load(); if (opensys("/dev/kmsg")) { if (opensys(_PATH_KLOG)) warn("Kernel logging disabled, failed opening %s", _PATH_KLOG); @@ -453,6 +504,8 @@ int main(int argc, char *argv[]) if (rc < 0 && errno != EINTR) ERR("select()"); + + sys_seqno_save(); } } @@ -1134,6 +1187,11 @@ void printsys(char *msg) while (isdigit(*p)) seqno = 10 * seqno + (*p++ - '0'); + /* Check if logged already (we've been restarted) */ + if (sys_seqno > 0 && seqno <= sys_seqno) + return; + sys_seqno = seqno; + p++; /* skip ',' */ /* timestamp */ diff --git a/src/syslogd.h b/src/syslogd.h index d68e59d..a9a1f6a 100644 --- a/src/syslogd.h +++ b/src/syslogd.h @@ -85,6 +85,10 @@ #define _PATH_LOGPID RUNSTATEDIR "/syslogd.pid" #endif +#ifndef _PATH_CACHE +#define _PATH_CACHE RUNSTATEDIR "/syslogd.cache" +#endif + #ifndef _PATH_DEV #define _PATH_DEV "/dev/" #endif