Skip to content

Commit 2ae1a49

Browse files
committed
Add git dfprog/++host/::filter stop-processing block prefix
Inspired by OpenBSD syslogd, introduce a double-prefix syntax for block headers that stops rule evaluation once a matching rule fires, preventing a message from being logged twice. !!prog -- like !prog but stops on match ++host -- like +host but stops on match ::filter -- like :filter but stops on match (sysklogd extension) Use !*, +*, or :* as usual to reset and resume normal evaluation for subsequent blocks. The STOP_FLAG (0x040) is set on all filed entries created within a double-prefix block. The logmsg() dispatch loop breaks out of the SIMPLEQ_FOREACH as soon as it processes a rule carrying that flag, so no further rules see the message. Example -- route nftables firewall logs exclusively to their own file: ::msg, contains, "[nftables" kern.* -/var/log/nftables.log :* *.warn;authpriv.none -/var/log/syslog Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent da40146 commit 2ae1a49

3 files changed

Lines changed: 104 additions & 3 deletions

File tree

man/syslog.conf.5

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,14 @@ single backslash ('\\') character.
6363
.Pp
6464
.Bd -literal -offset indent
6565
PROGRAM := !IDENT[,IDENT]
66+
|= !!IDENT[,IDENT]
6667
|= !+IDENT[,IDENT]
6768
|= !-IDENT[,IDENT]
6869
HOSTNAME := +IDENT[,IDENT]
70+
|= ++IDENT[,IDENT]
6971
|= -IDENT[,IDENT]
7072
PROPERTY := :PROP, [PREFIX]OPERATOR, "VALUE"
73+
|= ::PROP, [PREFIX]OPERATOR, "VALUE"
7174
PROP := hostname
7275
|= msg
7376
|= msgid
@@ -176,6 +179,16 @@ You can reset the program specification at any time using the
176179
syntax. The program specification is also reset for each included .conf
177180
file.
178181
.Pp
182+
The
183+
.Ql !!prog
184+
form works like
185+
.Ql !prog
186+
but causes evaluation to stop after a matching rule in this block fires,
187+
ensuring no further rules are applied to that message.
188+
Use
189+
.Ql !*
190+
to cancel the effect and resume normal evaluation for subsequent blocks.
191+
.Pp
179192
A
180193
.Em hostname filter
181194
specification of the form
@@ -197,14 +210,34 @@ the local hostname will be used. Similar to program specifications,
197210
multiple comma-separated values may be specified for hostname
198211
specifications.
199212
.Pp
213+
The
214+
.Ql ++hostname
215+
form works like
216+
.Ql +hostname
217+
but causes evaluation to stop after a matching rule in this block fires,
218+
ensuring no further rules are applied to that message.
219+
Use
220+
.Ql +*
221+
to cancel the effect and resume normal evaluation for subsequent blocks.
222+
.Pp
200223
A
201224
.Em property-based filter
202225
specification is a line beginning with
203226
.Ql #:
204227
or
205228
.Ql :
206229
and the following blocks will be applied only when filter value matches
207-
given filter properties value. See
230+
given filter properties value.
231+
The
232+
.Ql ::
233+
form works like
234+
.Ql :
235+
but causes evaluation to stop after a matching rule in this block fires,
236+
ensuring no further rules are applied to that message.
237+
Use
238+
.Ql :*
239+
to cancel the effect and resume normal evaluation for subsequent blocks.
240+
See
208241
.Sx PROPERTY-BASED FILTERS
209242
section for more details. For examples, see
210243
.Sx EXAMPLES
@@ -844,6 +877,37 @@ These examples show off the substring and regexp matching capabilities.
844877
:hostname, icase_ereregex, "^server-(dcA|podB|cdn)-rack1[0-9]{2}\..*"
845878
*.* /var/log/racks10-19.log
846879
.Ed
880+
.Ss Stop Processing
881+
In this example, firewall messages tagged with
882+
.Ql [nftables
883+
are captured in a dedicated log file and then processing stops,
884+
preventing the same messages from also appearing in
885+
.Pa /var/log/syslog .
886+
Without the
887+
.Ql ::
888+
prefix on the property filter, both files would receive the message.
889+
.Bd -literal -offset indent
890+
# Capture firewall messages exclusively in their own log file.
891+
# The :: prefix stops further rule evaluation once a match fires.
892+
::msg, contains, "[nftables"
893+
kern.* /var/log/nftables.log
894+
895+
# Reset the property filter, then log general kernel/warn messages.
896+
# Firewall messages never reach this rule.
897+
:*
898+
*.warn;\e
899+
authpriv.none;cron.none;mail.none;news.none /var/log/syslog
900+
.Ed
901+
.Pp
902+
The same behaviour is available for program and hostname blocks.
903+
Here, all messages from
904+
.Nm spamd
905+
are routed to their own log and nowhere else:
906+
.Bd -literal -offset indent
907+
!!spamd
908+
daemon.info /var/log/spamd
909+
!*
910+
.Ed
847911
.Ss Critical
848912
This stores all messages of priority
849913
.Ql crit

src/syslogd.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2526,6 +2526,9 @@ static void logmsg(struct buf_msg *buffer)
25262526
#endif
25272527
fprintlog_first(f, buffer);
25282528
}
2529+
2530+
if (f->f_flags & STOP_FLAG)
2531+
break;
25292532
}
25302533

25312534
sigprocmask(SIG_UNBLOCK, &mask, NULL);
@@ -4146,9 +4149,9 @@ static void init(void)
41464149
}
41474150

41484151
if (f->f_program)
4149-
printf(" (%s)", f->f_program);
4152+
printf(" (%s%s)", (f->f_flags & STOP_FLAG) ? "!!" : "", f->f_program);
41504153
if (f->f_host)
4151-
printf(" [%s]", f->f_host);
4154+
printf(" [%s%s]", (f->f_flags & STOP_FLAG) ? "++" : "", f->f_host);
41524155

41534156
if (f->f_flags & RFC5424)
41544157
printf("\t;RFC5424");
@@ -4891,6 +4894,7 @@ static int cfparse(FILE *fp, struct files *newf)
48914894
char host[LINE_MAX] = "*";
48924895
char prog[LINE_MAX] = "*";
48934896
char cbuf[LINE_MAX];
4897+
int stop_block = 0;
48944898
struct filed *f;
48954899
char *cline;
48964900
char *p;
@@ -4923,11 +4927,20 @@ static int cfparse(FILE *fp, struct files *newf)
49234927
if (*p == '+' || *p == '-') {
49244928
host[i++] = *p++;
49254929

4930+
/* ++ means final block (like OpenBSD), - never final */
4931+
if (*p == '+') {
4932+
stop_block = 1;
4933+
p++;
4934+
} else {
4935+
stop_block = 0;
4936+
}
4937+
49264938
while (isblank(*p))
49274939
p++;
49284940

49294941
if (*p == '*') {
49304942
(void)strlcpy(host, "*", sizeof(host));
4943+
stop_block = 0;
49314944
continue;
49324945
}
49334946

@@ -4949,11 +4962,21 @@ static int cfparse(FILE *fp, struct files *newf)
49494962

49504963
if (*p == '!') {
49514964
p++;
4965+
4966+
/* !! means final block, matching OpenBSD syslogd */
4967+
if (*p == '!') {
4968+
stop_block = 1;
4969+
p++;
4970+
} else {
4971+
stop_block = 0;
4972+
}
4973+
49524974
while (isblank(*p))
49534975
p++;
49544976

49554977
if (*p == '\0' || *p == '*') {
49564978
(void)strlcpy(prog, "*", sizeof(prog));
4979+
stop_block = 0;
49574980
continue;
49584981
}
49594982

@@ -4968,10 +4991,20 @@ static int cfparse(FILE *fp, struct files *newf)
49684991

49694992
if (*p == ':') {
49704993
p++;
4994+
4995+
/* :: means final block, analogous to !! for programs */
4996+
if (*p == ':') {
4997+
stop_block = 1;
4998+
p++;
4999+
} else {
5000+
stop_block = 0;
5001+
}
5002+
49715003
while (isblank(*p))
49725004
p++;
49735005
if (!*p || *p == '*') {
49745006
strlcpy(pfilter, "*", sizeof(pfilter));
5007+
stop_block = 0;
49755008
continue;
49765009
}
49775010
strlcpy(pfilter, p, sizeof(pfilter));
@@ -5034,6 +5067,9 @@ static int cfparse(FILE *fp, struct files *newf)
50345067
if (!f)
50355068
continue;
50365069

5070+
if (stop_block)
5071+
f->f_flags |= STOP_FLAG;
5072+
50375073
SIMPLEQ_INSERT_TAIL(newf, f, f_link);
50385074
}
50395075

src/syslogd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@
191191
#define MARK 0x008 /* this message is a mark */
192192
#define RFC3164 0x010 /* format log message according to RFC 3164 */
193193
#define RFC5424 0x020 /* format log message according to RFC 5424 */
194+
#define STOP_FLAG 0x040 /* stop processing further rules after match */
194195

195196
/* Syslog timestamp formats. */
196197
#define BSDFMT_DATELEN 0

0 commit comments

Comments
 (0)