From 72f2faef6e5e8964f8c03100bc1c1d43ff196be1 Mon Sep 17 00:00:00 2001 From: Steffen Nurpmeso Date: Thu, 20 Jan 2022 14:33:22 +0100 Subject: [PATCH 1/4] Add "notify" keyword --- man/syslog.conf.5 | 11 ++++++ src/syslogd.c | 90 ++++++++++++++++++++++++++++++++++++++++++++--- src/syslogd.h | 8 +++++ 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/man/syslog.conf.5 b/man/syslog.conf.5 index 41fb86e..b232fb5 100644 --- a/man/syslog.conf.5 +++ b/man/syslog.conf.5 @@ -105,6 +105,17 @@ cron or a separate log rotate daemon. Comments, lines starting with a hash mark ('#'), and empty lines are ignored. If an error occurs during parsing the whole line is ignored. .Pp +The special keyword +.Em notify +specifies the path to an executable program which will get called +whenever a log file has been rotated, with the name of the file, less +its rotation suffix +.Ql .0 , +as an argument. +For example: +.Ql notify /sbin/on-log-rotate.sh . +Any number of notifiers may be installed. +.Pp A special .Em include keyword can be used to include all files with names ending in '.conf' diff --git a/src/syslogd.c b/src/syslogd.c index cb6ad16..9318a29 100644 --- a/src/syslogd.c +++ b/src/syslogd.c @@ -147,6 +147,11 @@ static int KeepKernTime; /* Keep kernel timestamp, evern after initial read static off_t RotateSz = 0; /* Max file size (bytes) before rotating, disabled by default */ static int RotateCnt = 5; /* Max number (count) of log files to keep, set with -c */ +/* + * List of notifiers + */ +static SIMPLEQ_HEAD(notifiers, notifier) nothead = SIMPLEQ_HEAD_INITIALIZER(nothead); + /* * List of peers and sockets for binding. */ @@ -180,9 +185,11 @@ static void signal_init(void); static void boot_time_init(void); static void init(void); static int strtobytes(char *arg); -static int cfparse(FILE *fp, struct files *newf); +static int cfparse(FILE *fp, struct files *newf, struct notifiers *newn); int decode(char *name, struct _code *codetab); static void logit(char *, ...); +static void notifier_add(struct notifiers *newn, const char *program); +static void notifier_invoke(const char *logfile); void reload(int); static int validate(struct sockaddr *sa, const char *hname); static int waitdaemon(int); @@ -1604,6 +1611,9 @@ void logrotate(struct filed *f) ERR("Failed re-opening log file %s after rotation", f->f_un.f_fname); return; } + + if (!SIMPLEQ_EMPTY(¬head)) + notifier_invoke(f->f_un.f_fname); } ftruncate(f->f_file, 0); } @@ -2250,6 +2260,7 @@ void debug_switch(int signo) */ static void close_open_log_files(void) { + struct notifier *np = NULL, *npnext = NULL; struct filed *f = NULL, *next = NULL; SIMPLEQ_FOREACH_SAFE(f, &fhead, f_link, next) { @@ -2276,6 +2287,9 @@ static void close_open_log_files(void) free(f); } + + SIMPLEQ_FOREACH_SAFE(np, ¬head, n_link, npnext) + free(np); } void die(int signo) @@ -2455,6 +2469,7 @@ static void boot_time_init(void) static void init(void) { static int once = 1; + struct notifiers newn = SIMPLEQ_HEAD_INITIALIZER(newn); struct filed *f; struct files newf = SIMPLEQ_HEAD_INITIALIZER(newf); FILE *fp; @@ -2538,7 +2553,7 @@ static void init(void) } } - if (cfparse(fp, &newf)) { + if (cfparse(fp, &newf, &newn)) { fclose(fp); return; } @@ -2548,11 +2563,21 @@ static void init(void) * Close all open log files. */ close_open_log_files(); + fhead = newf; + nothead = newn; Initialized = 1; if (Debug) { + if (!SIMPLEQ_EMPTY(¬head)) { + struct notifier *np; + + SIMPLEQ_FOREACH(np, ¬head, n_link) + printf("notify %s\n", np->n_program); + printf("\n"); + } + SIMPLEQ_FOREACH(f, &fhead, f_link) { if (f->f_type == F_UNUSED) continue; @@ -2923,7 +2948,7 @@ static struct filed *cfline(char *line) /* * Parse .conf file and append to list */ -static int cfparse(FILE *fp, struct files *newf) +static int cfparse(FILE *fp, struct files *newf, struct notifiers *newn) { struct filed *f; char cbuf[BUFSIZ]; @@ -2988,13 +3013,18 @@ static int cfparse(FILE *fp, struct files *newf) } logit("Parsing %s ...", gl.gl_pathv[i]); - cfparse(fpi, newf); + cfparse(fpi, newf, newn); fclose(fpi); } globfree(&gl); continue; } + if (!strncmp(cbuf, "notify", 6)) { + notifier_add(newn, &cbuf[6]); + continue; + } + f = cfline(cbuf); if (!f) continue; @@ -3337,6 +3367,58 @@ static void logit(char *fmt, ...) fflush(stdout); } +static void notifier_add(struct notifiers *newn, const char *program) +{ + while (*program && isspace(*program)) + program++; + + /* Check whether it is accessible, regardless of TOCTOU */ + if (!access(program, X_OK)) { + struct notifier *np; + size_t len; + + len = strlen(program); + + np = calloc(1, sizeof(*np) + len +1); + if (np) { + /* xxx Actually wastes space -- vararray? */ + np->n_program = (char*)&np[1]; + memcpy(np->n_program, program, len); + SIMPLEQ_INSERT_TAIL(newn, np, n_link); + } else + ERR("Cannot allocate memory for a notify program"); + } else + logit("notify: non-existing, or not executable program\n"); +} + +static void notifier_invoke(const char *logfile) +{ + char *argv[3]; + struct notifier *np; + + logit("notify: rotated %s, invoking hooks\n", logfile); + + SIMPLEQ_FOREACH(np, ¬head, n_link) { + switch (fork()) { + case -1: + ERR("Cannot start notifier %s", np->n_program); + break; + case 0: + /* No specific I/O setup, just use what we had */ + argv[0] = np->n_program; + argv[1] = (char*)logfile; /* logical unconst */ + argv[2] = NULL; + execv(argv[0], argv); + _exit(1); + default: + /* Do not care beside that, no special process group + * etc.; it will eventually be reaped via reapchild() */ + logit("notify: forked for %s\n", np->n_program); + break; + } + } +} + /* * The following function is resposible for handling a SIGHUP signal. Since * we are now doing mallocs/free as part of init we had better not being diff --git a/src/syslogd.h b/src/syslogd.h index e9247d8..934afd1 100644 --- a/src/syslogd.h +++ b/src/syslogd.h @@ -305,6 +305,14 @@ struct filed { int f_rotatesz; }; +/* + * Log rotation notifiers + */ +struct notifier { + SIMPLEQ_ENTRY(notifier) n_link; + char *n_program; +}; + void flog(int pri, char *fmt, ...); #endif /* SYSKLOGD_SYSLOGD_H_ */ From bc103013cba61fe8e6805cf7e3c9e0918544dffa Mon Sep 17 00:00:00 2001 From: Steffen Nurpmeso Date: Thu, 20 Jan 2022 14:33:45 +0100 Subject: [PATCH 2/4] Test for "Add "notify" keyword" --- test/Makefile.am | 3 ++- test/notify.sh | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 test/notify.sh diff --git a/test/Makefile.am b/test/Makefile.am index f966b2b..5282297 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST = lib.sh opts.sh -EXTRA_DIST += api.sh local.sh unicode.sh remote.sh fwd.sh mark.sh +EXTRA_DIST += api.sh local.sh unicode.sh remote.sh fwd.sh mark.sh notify.sh CLEANFILES = *~ *.trs *.log TEST_EXTENSIONS = .sh TESTS_ENVIRONMENT= unshare -mrun @@ -17,5 +17,6 @@ TESTS += remote.sh TESTS += api.sh TESTS += fwd.sh TESTS += mark.sh +TESTS += notify.sh programs: $(check_PROGRAMS) diff --git a/test/notify.sh b/test/notify.sh new file mode 100755 index 0000000..7670259 --- /dev/null +++ b/test/notify.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -x + +if [ x"${srcdir}" = x ]; then + srcdir=. +fi +. ${srcdir}/lib.sh + +NOT1=${DIR}/${NM}-1.sh +NOT1STAMP=${DIR}/${NM}-1.stamp +NOT2=${DIR}/${NM}-2.sh +NOT2STAMP=${DIR}/${NM}-2.stamp + +printf '#!/bin/sh -\necho script 1: $* > '${NOT1STAMP}'\n' > ${NOT1} +printf '#!/bin/sh -\necho script 2: $* > '${NOT2STAMP}'\n' > ${NOT2} +chmod 0755 ${NOT1} ${NOT2} + +cat < ${CONF} +notify ${NOT1} +# Match all log messages, store in RC5424 format and rotate every 1 KiB +*.* -${LOG} ;rotate=1k:2,RFC5424 +notify ${NOT2} +EOF + +../src/syslogd -m1 -b :${PORT2} -d -sF -f ${CONF} -p ${SOCK2} -p ${ALTSOCK} -P ${PID2} >${LOG2} & +sleep 3 +cat ${PID2} >> "$DIR/PIDs" + +if grep 'notify '${NOT1} ${LOG2} && grep 'notify '${NOT2} ${LOG2}; then + : +else + exit $? +fi + +if [ -x ../src/logger ]; then + : +else + exit 0 +fi + +kill -USR1 `cat ${PID2}` + +MSG=01234567890123456789012345678901234567890123456789 +MSG=$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG +../src/logger -u ${SOCK2} ${MSG} +../src/logger -u ${SOCK2} 1${MSG} +../src/logger -u ${SOCK2} 2${MSG} + +kill -9 `cat ${PID2}` + +sleep 1 # XXX synchronization of async process? +if [ -f ${LOG}.0 ] && + grep 'script 1' ${NOT1STAMP} && + grep 'script 2' ${NOT2STAMP}; then + : +else + exit 1 +fi From 7038e51a0fba9140f16047424aa492aeec0253dd Mon Sep 17 00:00:00 2001 From: Steffen Nurpmeso Date: Sat, 12 Mar 2022 16:46:46 +0100 Subject: [PATCH 3/4] "notify": address Joachim Wiberg's comments (pull/45) --- src/syslogd.c | 57 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/syslogd.c b/src/syslogd.c index 9318a29..6827432 100644 --- a/src/syslogd.c +++ b/src/syslogd.c @@ -190,6 +190,7 @@ int decode(char *name, struct _code *codetab); static void logit(char *, ...); static void notifier_add(struct notifiers *newn, const char *program); static void notifier_invoke(const char *logfile); +static void notifier_free_all(void); void reload(int); static int validate(struct sockaddr *sa, const char *hname); static int waitdaemon(int); @@ -2260,7 +2261,6 @@ void debug_switch(int signo) */ static void close_open_log_files(void) { - struct notifier *np = NULL, *npnext = NULL; struct filed *f = NULL, *next = NULL; SIMPLEQ_FOREACH_SAFE(f, &fhead, f_link, next) { @@ -2287,9 +2287,6 @@ static void close_open_log_files(void) free(f); } - - SIMPLEQ_FOREACH_SAFE(np, ¬head, n_link, npnext) - free(np); } void die(int signo) @@ -2565,6 +2562,12 @@ static void init(void) close_open_log_files(); fhead = newf; + + /* + * Free all notifiers + */ + notifier_free_all(); + nothead = newn; Initialized = 1; @@ -3370,23 +3373,24 @@ static void logit(char *fmt, ...) static void notifier_add(struct notifiers *newn, const char *program) { while (*program && isspace(*program)) - program++; + ++program; /* Check whether it is accessible, regardless of TOCTOU */ if (!access(program, X_OK)) { struct notifier *np; - size_t len; - len = strlen(program); - - np = calloc(1, sizeof(*np) + len +1); - if (np) { - /* xxx Actually wastes space -- vararray? */ - np->n_program = (char*)&np[1]; - memcpy(np->n_program, program, len); - SIMPLEQ_INSERT_TAIL(newn, np, n_link); - } else + np = calloc(1, sizeof(*np)); + if (!np) { ERR("Cannot allocate memory for a notify program"); + return; + } + np->n_program = strdup(program); + if (!np->n_program) { + free (np); + ERR("Cannot allocate memory for a notify program"); + return; + } + SIMPLEQ_INSERT_TAIL(newn, np, n_link); } else logit("notify: non-existing, or not executable program\n"); } @@ -3394,31 +3398,42 @@ static void notifier_add(struct notifiers *newn, const char *program) static void notifier_invoke(const char *logfile) { char *argv[3]; + int childpid; struct notifier *np; logit("notify: rotated %s, invoking hooks\n", logfile); SIMPLEQ_FOREACH(np, ¬head, n_link) { - switch (fork()) { + childpid = fork(); + + switch (childpid) { case -1: ERR("Cannot start notifier %s", np->n_program); break; case 0: - /* No specific I/O setup, just use what we had */ argv[0] = np->n_program; - argv[1] = (char*)logfile; /* logical unconst */ + argv[1] = (char*)logfile; argv[2] = NULL; execv(argv[0], argv); _exit(1); default: - /* Do not care beside that, no special process group - * etc.; it will eventually be reaped via reapchild() */ - logit("notify: forked for %s\n", np->n_program); + logit("notify: forked child pid %d for %s\n", + childpid, np->n_program); break; } } } +static void notifier_free_all(void) +{ + struct notifier *np, *npnext; + + SIMPLEQ_FOREACH_SAFE(np, ¬head, n_link, npnext) { + free(np->n_program); + free(np); + } +} + /* * The following function is resposible for handling a SIGHUP signal. Since * we are now doing mallocs/free as part of init we had better not being From f7abd0d10a976d42e22fb890442dfacc368cee63 Mon Sep 17 00:00:00 2001 From: Steffen Nurpmeso Date: Sat, 12 Mar 2022 17:21:43 +0100 Subject: [PATCH 4/4] test/notify.sh: address Joachim Wiberg's comments (pull/45) --- test/notify.sh | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/test/notify.sh b/test/notify.sh index 7670259..5c6b554 100755 --- a/test/notify.sh +++ b/test/notify.sh @@ -6,6 +6,8 @@ if [ x"${srcdir}" = x ]; then fi . ${srcdir}/lib.sh +[ -x ../src/logger ] || SKIP 'logger missing' + NOT1=${DIR}/${NM}-1.sh NOT1STAMP=${DIR}/${NM}-1.stamp NOT2=${DIR}/${NM}-2.sh @@ -15,44 +17,25 @@ printf '#!/bin/sh -\necho script 1: $* > '${NOT1STAMP}'\n' > ${NOT1} printf '#!/bin/sh -\necho script 2: $* > '${NOT2STAMP}'\n' > ${NOT2} chmod 0755 ${NOT1} ${NOT2} -cat < ${CONF} -notify ${NOT1} +cat < ${CONFD}/notifier.conf +notify ${NOT1} # Match all log messages, store in RC5424 format and rotate every 1 KiB *.* -${LOG} ;rotate=1k:2,RFC5424 notify ${NOT2} EOF -../src/syslogd -m1 -b :${PORT2} -d -sF -f ${CONF} -p ${SOCK2} -p ${ALTSOCK} -P ${PID2} >${LOG2} & -sleep 3 -cat ${PID2} >> "$DIR/PIDs" - -if grep 'notify '${NOT1} ${LOG2} && grep 'notify '${NOT2} ${LOG2}; then - : -else - exit $? -fi - -if [ -x ../src/logger ]; then - : -else - exit 0 -fi - -kill -USR1 `cat ${PID2}` +setup MSG=01234567890123456789012345678901234567890123456789 MSG=$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG -../src/logger -u ${SOCK2} ${MSG} -../src/logger -u ${SOCK2} 1${MSG} -../src/logger -u ${SOCK2} 2${MSG} +../src/logger -u ${SOCK} ${MSG} +../src/logger -u ${SOCK} 1${MSG} +../src/logger -u ${SOCK} 2${MSG} -kill -9 `cat ${PID2}` - -sleep 1 # XXX synchronization of async process? if [ -f ${LOG}.0 ] && grep 'script 1' ${NOT1STAMP} && grep 'script 2' ${NOT2STAMP}; then - : + OK else - exit 1 + FAIL 'Notifier did not run.' fi