Page MenuHomeFreeBSD

D23468.id67661.diff
No OneTemporary

D23468.id67661.diff

Index: usr.sbin/syslogd/syslog.conf.5
===================================================================
--- usr.sbin/syslogd/syslog.conf.5
+++ usr.sbin/syslogd/syslog.conf.5
@@ -44,9 +44,10 @@
program.
It consists of
blocks of lines separated by
-.Em program
-and
+.Em program ,
.Em hostname
+or
+.Em property-based filter
specifications (separations appear alone on their lines),
with each line containing two fields: the
.Em selector
@@ -154,14 +155,16 @@
library routine.
.Pp
Each block of lines is separated from the previous block by a
-.Em program
-or
+.Em program ,
.Em hostname
+or
+.Em property-based filter
specification.
A block will only log messages corresponding to the most recent
-.Em program
-and
+.Em program ,
.Em hostname
+and
+.Em property-based filter
specifications given.
Thus, with a block which selects
.Ql ppp
@@ -236,11 +239,24 @@
values may be specified for hostname specifications.
.Pp
A
-.Em program
+.Em property-based filter
+specification is a line beginning with
+.Ql #:
or
+.Ql \&:
+and the following blocks will be applied only when filter value
+matches given filter propertie's value. See
+.Sx PROPERTY-BASED FILTERS
+section for more details.
+.Pp
+A
+.Em program ,
.Em hostname
-specification may be reset by giving the program or hostname as
-.Ql * .
+or
+.Em property-based filter
+specification may be reset by giving
+.Ql *
+as an argument.
.Pp
See
.Xr syslog 3
@@ -434,6 +450,78 @@
is removed and
.Ql #
is treated as an ordinary character.
+.Sh PROPERTY-BASED FILTERS
+.Em program ,
+.Em hostname
+specifications performs exact match filtering against explicit field only.
+.Em Property-based filters
+feature substring and regular expressions (see
+.Xr re_format 7 )
+matching against various message attributes.
+Filter specification starts with
+.Ql #:
+or
+.Ql \&:
+followed by three comma-separated fields
+.Em property , operator , \&"value\&" .
+Value must be double-quoted. A double quote and backslash must be escaped by
+a blackslash.
+.Pp
+Following
+.Em properties
+are supported as test value:
+.Pp
+.Bl -bullet -compact
+.It
+.Ql msg
+- body of the message received.
+.It
+.Ql programname
+- program name sent the message
+.It
+.Ql hostname
+- hostname of message's originator
+.It
+.Ql source
+- an alias for hostname
+.El
+.Pp
+Operator specifies a comparison function between
+.Em propertie's
+ value against filter's value.
+Possible operators:
+.Pp
+.Bl -bullet -compact
+.It
+.Ql contains
+- true if filter value is found as a substring of
+.Em property
+.It
+.Ql isequal
+- true if filter value is equal to
+.Em property
+.It
+.Ql startswith
+- true if property starts with filter value
+.It
+.Ql regex
+- true if property matches basic regular expression defined in filter value
+.It
+.Ql ereregex
+- true if property matches extended regular expression defined in filter value
+.El
+.Pp
+Operator may be prefixed by
+.Pp
+.Bl -bullet -compact
+.It
+.Ql \&!
+- to invert compare logic
+.It
+.Ql icase_
+- to make comparison function case insensitive
+.El
+.Pp
.Sh IMPLEMENTATION NOTES
The
.Dq kern
@@ -503,6 +591,21 @@
# Log ipfw messages without syncing after every message.
!ipfw
*.* -/var/log/ipfw
+
+# Log ipfw messages with "Deny" in the message body.
+:msg, contains, ".*Deny.*"
+*.* /var/log/ipfw.deny
+
+# Reset program name filtering
+!*
+
+# Log messages from bird or bird6 into one file
+:processname, regex, "^bird6?$"
+*.* /var/log/bird-all.log
+
+# Log messages from servers in racks 10-19 in multiple locations, case insensitive
+:hostname, icase_ereregex, "^server-(dcA|podB|cdn)-rack1[0-9]{2}\\..*"
+*.* /var/log/racks10..19.log
.Ed
.Sh SEE ALSO
.Xr syslog 3 ,
Index: usr.sbin/syslogd/syslogd.c
===================================================================
--- usr.sbin/syslogd/syslogd.c
+++ usr.sbin/syslogd/syslogd.c
@@ -113,6 +113,7 @@
#include <sysexits.h>
#include <unistd.h>
#include <utmpx.h>
+#include <regex.h>
#include "pathnames.h"
#include "ttymsg.h"
@@ -175,6 +176,37 @@
#define MARK 0x008 /* this message is a mark */
#define ISKERNEL 0x010 /* kernel generated message */
+/*
+ * This structure hold property-based filter
+ */
+
+struct prop_filter {
+ uint8_t prop_type;
+#define PROP_TYPE_NOOP 0
+#define PROP_TYPE_MSG 1
+#define PROP_TYPE_HOSTNAME 2
+#define PROP_TYPE_PROGNAME 3
+
+ uint8_t cmp_type;
+#define PROP_CMP_CONTAINS 1
+#define PROP_CMP_EQUAL 2
+#define PROP_CMP_STARTS 3
+#define PROP_CMP_REGEX 4
+
+ uint16_t cmp_flags;
+#define PROP_FLAG_EXCLUDE (1 << 0)
+#define PROP_FLAG_ICASE (1 << 1)
+
+ union {
+ char *p_strval;
+ regex_t *p_re;
+ } pflt_uniptr;
+#define pflt_strval pflt_uniptr.p_strval
+#define pflt_re pflt_uniptr.p_re
+
+ size_t pflt_strlen;
+};
+
/*
* This structure represents the files that will have log
* copies printed.
@@ -194,6 +226,7 @@
#define PRI_EQ 0x2
#define PRI_GT 0x4
char *f_program; /* program this applies to */
+ struct prop_filter *f_prop_filter; /* property-based filter */
union {
char f_uname[MAXUNAMES][MAXLOGNAME];
struct {
@@ -337,7 +370,8 @@
static int addfile(struct filed *);
static int addpeer(struct peer *);
static int addsock(struct sockaddr *, socklen_t, struct socklist *);
-static struct filed *cfline(const char *, const char *, const char *);
+static struct filed *cfline(const char *, const char *, const char *,
+ const char *);
static const char *cvthname(struct sockaddr *);
static void deadq_enter(pid_t, const char *);
static int deadq_remove(struct deadq_entry *);
@@ -1026,6 +1060,71 @@
return !exclude;
}
+/*
+ * Match a set of message properties against a filter.
+ * Return a non-0 value if the message must be ignored
+ * based on the filter.
+ */
+static int
+evaluate_prop_filter(const struct prop_filter *filter, const char *value,
+ size_t valuelen)
+{
+ const char *s = NULL;
+ const int exclude = ((filter->cmp_flags & PROP_FLAG_EXCLUDE) > 0);
+
+ if (value == NULL)
+ return (-1);
+
+ if (filter->cmp_type == PROP_CMP_REGEX) {
+ if (regexec(filter->pflt_re, value, 0, NULL, 0) == 0)
+ return (exclude);
+ else
+ return (!exclude);
+ }
+
+ if (valuelen < 0)
+ valuelen = strlen(value);
+
+ /* a shortcut for equal with different length is always false */
+ if (filter->cmp_type == PROP_CMP_EQUAL
+ && valuelen != filter->pflt_strlen)
+ return (!exclude);
+
+ if (filter->cmp_flags & PROP_FLAG_ICASE)
+ s = strcasestr (value, filter->pflt_strval);
+ else
+ s = strstr (value, filter->pflt_strval);
+
+ /*
+ * PROP_CMP_CONTAINS true if s
+ * PROP_CMP_STARTS true if s && s == value
+ * PROP_CMP_EQUAL true if s && s == value &&
+ * valuelen == filter->pflt_strlen
+ * (and length match is checked
+ * already)
+ */
+
+ switch (filter->cmp_type) {
+ case PROP_CMP_STARTS:
+ case PROP_CMP_EQUAL:
+ if (s != value)
+ return (!exclude);
+ /* FALLTHROUGH */
+ case PROP_CMP_CONTAINS:
+ if (s)
+ return (exclude);
+ else
+ return (!exclude);
+ break;
+ default:
+ /* unknown cmp_type */
+ break;
+ }
+
+ /* should not be reachable, discard the message */
+ return (-1);
+}
+
/*
* Log a message to the appropriate log files, users, etc. based on
* the priority.
@@ -1034,7 +1133,7 @@
logmsg(int pri, const char *msg, const char *from, int flags)
{
struct filed *f;
- int i, fac, msglen, prilev;
+ int i, fac, msglen, prilev, filterret;
const char *timestamp;
char prog[NAME_MAX+1];
char buf[MAXLINE+1];
@@ -1129,6 +1228,28 @@
if (skip_message(prog, f->f_program, 1))
continue;
+ if (f->f_prop_filter
+ && f->f_prop_filter->prop_type != PROP_TYPE_NOOP) {
+ switch (f->f_prop_filter->prop_type) {
+ case PROP_TYPE_MSG:
+ filterret = evaluate_prop_filter(
+ f->f_prop_filter, msg, msglen);
+ break;
+ case PROP_TYPE_HOSTNAME:
+ filterret = evaluate_prop_filter(
+ f->f_prop_filter, from, -1);
+ break;
+ case PROP_TYPE_PROGNAME:
+ filterret = evaluate_prop_filter(
+ f->f_prop_filter, prog, -1);
+ break;
+ default:
+ filterret = -1;
+ }
+ if (filterret)
+ continue;
+ }
+
/* skip message to console if it has already been printed */
if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
continue;
@@ -1706,6 +1827,7 @@
char host[MAXHOSTNAMELEN];
char prog[LINE_MAX];
char file[MAXPATHLEN];
+ char pfilter[LINE_MAX];
char *p, *tmp;
int i, nents;
size_t include_len;
@@ -1716,6 +1838,7 @@
include_len = sizeof(include_str) -1;
(void)strlcpy(host, "*", sizeof(host));
(void)strlcpy(prog, "*", sizeof(prog));
+ (void)strlcpy(pfilter, "*", sizeof(pfilter));
while (fgets(cline, sizeof(cline), cf) != NULL) {
/*
* check for end-of-section, comments, strip off trailing
@@ -1764,7 +1887,7 @@
}
if (*p == '#') {
p++;
- if (*p != '!' && *p != '+' && *p != '-')
+ if (*p == '\0' || strchr("!+-:", *p) == NULL)
continue;
}
if (*p == '+' || *p == '-') {
@@ -1801,6 +1924,17 @@
prog[i] = 0;
continue;
}
+ if (*p == ':') {
+ p++;
+ while (isspace(*p))
+ p++;
+ if ((!*p) || (*p == '*')) {
+ (void)strlcpy(pfilter, "*", sizeof(pfilter));
+ continue;
+ }
+ (void)strlcpy(pfilter, p, sizeof(pfilter));
+ continue;
+ }
for (p = cline + 1; *p != '\0'; p++) {
if (*p != '#')
continue;
@@ -1814,7 +1948,7 @@
}
for (i = strlen(cline) - 1; i >= 0 && isspace(cline[i]); i--)
cline[i] = '\0';
- f = cfline(cline, prog, host);
+ f = cfline(cline, prog, host, pfilter);
if (f != NULL)
addfile(f);
}
@@ -1905,16 +2039,30 @@
STAILQ_REMOVE_HEAD(&fhead, next);
free(f->f_program);
free(f->f_host);
+ if (f->f_prop_filter) {
+ switch (f->f_prop_filter->cmp_type) {
+ case PROP_CMP_REGEX:
+ regfree(f->f_prop_filter->pflt_re);
+ free(f->f_prop_filter->pflt_re);
+ break;
+ case PROP_CMP_CONTAINS:
+ case PROP_CMP_EQUAL:
+ case PROP_CMP_STARTS:
+ free(f->f_prop_filter->pflt_strval);
+ break;
+ }
+ free(f->f_prop_filter);
+ }
free(f);
}
/* open the configuration file */
if ((cf = fopen(ConfFile, "r")) == NULL) {
dprintf("cannot open %s\n", ConfFile);
- f = cfline("*.ERR\t/dev/console", "*", "*");
+ f = cfline("*.ERR\t/dev/console", "*", "*", "*");
if (f != NULL)
addfile(f);
- f = cfline("*.PANIC\t*", "*", "*");
+ f = cfline("*.PANIC\t*", "*", "*", "*");
if (f != NULL)
addfile(f);
Initialized = 1;
@@ -2010,11 +2158,163 @@
}
}
+#define CONST_STRNCASECMP(a, b) strncasecmp((a), (b), (sizeof(b)))
+#define CONST_STRNCASECMPPREF(a, b) strncasecmp((a), (b), (sizeof(b) - 1))
+/*
+ * Compile property-based filter.
+ * Returns 0 on success, -1 otherwise.
+ */
+static int
+prop_filter_compile(struct prop_filter *pfilter, const char *filterstr)
+{
+ char *filter, *filterp, *filter_endpos, *p;
+ char **ap, *argv[2];
+ int re_flags = REG_NOSUB;
+ int escaped;
+
+ filterp = filter = strdup(filterstr);
+ if (filter == NULL) {
+ logerror("strdup");
+ return (-1);
+ }
+
+ bzero(pfilter, sizeof(struct prop_filter));
+ bzero(argv, sizeof(argv));
+
+ /*
+ * Split filter into 3 parts: property name, cmp type
+ * and value to compare against which stays in filter.
+ */
+ for (ap = argv; (*ap = strsep(&filter, ", \t\n")) != NULL;) {
+ if (**ap != '\0')
+ if (++ap >= &argv[2])
+ break;
+ }
+
+ if (argv[0] == NULL || argv[1] == NULL) {
+ logerror("filter parse error");
+ free(filterp);
+ return (-1);
+ }
+
+ /* fill in prop_type */
+ if (CONST_STRNCASECMP(argv[0], "msg") == 0)
+ pfilter->prop_type = PROP_TYPE_MSG;
+ else if(CONST_STRNCASECMP(argv[0], "hostname") == 0)
+ pfilter->prop_type = PROP_TYPE_HOSTNAME;
+ else if(CONST_STRNCASECMP(argv[0], "source") == 0)
+ pfilter->prop_type = PROP_TYPE_HOSTNAME;
+ else if(CONST_STRNCASECMP(argv[0], "programname") == 0)
+ pfilter->prop_type = PROP_TYPE_PROGNAME;
+ else {
+ logerror("unknown property");
+ free(filterp);
+ return (-1);
+ }
+
+ /* full in cmp_flags */
+ if (CONST_STRNCASECMPPREF(argv[1], "!") == 0) {
+ pfilter->cmp_flags |= PROP_FLAG_EXCLUDE;
+ argv[1]++;
+ }
+ if (CONST_STRNCASECMPPREF(argv[1], "icase_") == 0) {
+ pfilter->cmp_flags |= PROP_FLAG_ICASE;
+ argv[1] += sizeof("icase_") - 1;
+ }
+
+ /* fill in cmp_type */
+ if (CONST_STRNCASECMP(argv[1], "contains") == 0)
+ pfilter->cmp_type = PROP_CMP_CONTAINS;
+ else if (CONST_STRNCASECMP(argv[1], "isequal") == 0)
+ pfilter->cmp_type = PROP_CMP_EQUAL;
+ else if (CONST_STRNCASECMP(argv[1], "startswith") == 0)
+ pfilter->cmp_type = PROP_CMP_STARTS;
+ else if (CONST_STRNCASECMP(argv[1], "regex") == 0)
+ pfilter->cmp_type = PROP_CMP_REGEX;
+ else if (CONST_STRNCASECMP(argv[1], "ereregex") == 0) {
+ pfilter->cmp_type = PROP_CMP_REGEX;
+ re_flags |= REG_EXTENDED;
+ } else {
+ logerror("unknown cmp function");
+ free(filterp);
+ return (-1);
+ }
+
+ /*
+ * Handle filter value
+ */
+
+ /* remove leading whitespace and check for " next character */
+ filter += strspn(filter, ", \t\n");
+ if (*filter != '"' || strlen(filter) < 3) {
+ logerror("property value parse error");
+ free(filterp);
+ return (-1);
+ }
+ filter++;
+
+ /* process possible '"' escaping */
+ escaped = 0;
+ filter_endpos = filter;
+ for (p = filter; *p != '\0'; p++) {
+ if (*p == '\\' && !escaped) {
+ escaped = 1;
+ /* do not shift filter_endpos */
+ continue;
+ }
+ if (*p == '"' && !escaped) {
+ p++;
+ break;
+ }
+ /* we've seen some esc symbols, need to compress the line */
+ if (filter_endpos != p)
+ *filter_endpos = *p;
+
+ filter_endpos++;
+ escaped = 0;
+ }
+
+ *filter_endpos = '\0';
+
+ /* do we have anything but whitespace left? */
+ if (*p != '\0' && strspn(p, " \t\n") != strlen(p)) {
+ logerror("property value parse error");
+ free(filterp);
+ return (-1);
+ }
+
+ if (pfilter->cmp_type == PROP_CMP_REGEX) {
+ pfilter->pflt_re = calloc(1, sizeof(*pfilter->pflt_re));
+ if (pfilter->pflt_re == NULL) {
+ logerror("RE calloc() error");
+ free(filterp);
+ free(pfilter->pflt_re);
+ return (-1);
+ }
+ if (pfilter->cmp_flags & PROP_FLAG_ICASE)
+ re_flags |= REG_ICASE;
+ if (regcomp(pfilter->pflt_re, filter, re_flags) != 0) {
+ logerror("RE compilation error");
+ free(filterp);
+ free(pfilter->pflt_re);
+ return (-1);
+ }
+ } else {
+ pfilter->pflt_strval = strdup(filter);
+ pfilter->pflt_strlen = strlen(filter);
+ }
+
+ free(filterp);
+ return (0);
+
+}
+
/*
* Crack a configuration file line
*/
static struct filed *
-cfline(const char *line, const char *prog, const char *host)
+cfline(const char *line, const char *prog, const char *host,
+ const char *pfilter)
{
struct filed *f;
struct addrinfo hints, *res;
@@ -2023,7 +2323,8 @@
char *bp;
char buf[MAXLINE], ebuf[100];
- dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
+ dprintf("cfline(\"%s\", f, \"%s\", \"%s\", \"%s\")\n", line, prog,
+ host, pfilter);
f = calloc(1, sizeof(*f));
if (f == NULL) {
@@ -2063,6 +2364,22 @@
}
}
+ if (pfilter) {
+ f->f_prop_filter = calloc(1, sizeof(*(f->f_prop_filter)));
+ if (f->f_prop_filter == NULL) {
+ logerror("pfilter calloc");
+ exit(1);
+ }
+ if (*pfilter == '*')
+ f->f_prop_filter->prop_type = PROP_TYPE_NOOP;
+ else {
+ if (prop_filter_compile(f->f_prop_filter, pfilter)) {
+ logerror("filter compile error");
+ exit(1);
+ }
+ }
+ }
+
/* scan through the list of selectors */
for (p = line; *p && *p != '\t' && *p != ' ';) {
int pri_done;

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 21, 9:17 PM (7 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25806269
Default Alt Text
D23468.id67661.diff (15 KB)

Event Timeline