implement blackhole filters for logwatch, and stop applying filters after the first hit
authorTero Marttila <terom@fixme.fi>
Sun, 12 Apr 2009 23:27:15 +0300
changeset 138 a716c621cb90
parent 137 c607c357c486
child 139 55b9dcc2b73a
implement blackhole filters for logwatch, and stop applying filters after the first hit
config.lua
src/error.c
src/irc_chan.c
src/irc_net.c
src/module.c
src/modules/logwatch.c
src/modules/logwatch.h
src/modules/logwatch_filter.c
--- a/config.lua	Sun Apr 12 22:19:54 2009 +0300
+++ b/config.lua	Sun Apr 12 23:27:15 2009 +0300
@@ -4,28 +4,57 @@
 --
 
 -- match all lines and output them as-is
-local function logwatch_filter_all () 
-    return { pat=nil, fmt=nil }
+local function logwatch_filter_all (name)
+    return { name=name }
 end
 
 -- match using a regex pattern, but output the full line
-local function logwatch_filter_raw (pat)
-    return { pat=pat, fmt=nil }
+local function logwatch_filter_raw (name, pat)
+    return { name=name, pat=pat }
 end
 
 -- match using a regexp pattern, and output a formatted line
-local function logwatch_filter (pat, fmt)
-    return { pat=pat, fmt=fmt }
+local function logwatch_filter (name, pat, fmt)
+    return { name=name, pat=pat, fmt=fmt }
 end
 
+-- match using a regexp pattern, and do *not* output
+local function logwatch_filter_blackhole (name, pat)
+    return { name=name, pat=pat, channel_is_null=true }
+end
+
+logwatch_timestamp_pat = "\\w{3} [0-9 ]\\d \\d{2}:\\d{2}:\\d{2}"
+
 -- match auth.log sudo entries
-local function logwatch_filter_sudo ()
-    return logwatch_filter(
-        "(?P<hostname>\\S+)\\s+sudo:\\s*(?P<username>\\S+) : TTY=(?P<tty>\\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\\S+) ; COMMAND=(?P<command>.*)",
+local function logwatch_filter_sudo (name)
+    return logwatch_filter(name,
+        "^" .. logwatch_timestamp_pat .. " (?P<hostname>\\S+)\\s+sudo:\\s*(?P<username>\\S+) : TTY=(?P<tty>\\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\\S+) ; COMMAND=(?P<command>.*)$",
         "{username}:{tty} - {target_user}@{hostname}:{pwd} - {command:r}"
     )
 end
 
+-- filter out the prefixed timestamp from lines
+local function logwatch_filter_strip_timestamp (name)
+    return logwatch_filter(name,
+        "^" .. logwatch_timestamp_pat .. " (?P<line>.+)$",
+        "{line}"
+    )
+end
+
+-- filter out auth.log cron messages
+local function logwatch_filter_no_cron (name)
+    return logwatch_filter_blackhole(name,
+        "^" .. logwatch_timestamp_pat .. " \\S+\\s+(CRON|su)\\[\\d+\\]: pam_unix\\(\\w+:\\w+\\): session (opened|closed) for user \\w+( by \\(uid=\\d+\\))?$"
+    )
+end
+
+-- filter out auth.log 'su for nobody by root' messages
+local function logwatch_filter_no_su_nobody (name)
+    return logwatch_filter_blackhole(name,
+        "^" .. logwatch_timestamp_pat .. " \\S+\\s+su\\[\\d+\\]: (Successful su for nobody by root|\\+ \\?\\?\\? root:nobody)$"
+    )
+end
+
 --
 -- Procedural config
 --
@@ -71,8 +100,14 @@
             module:conf("source_fifo", fifo_path)
             source_name = fifo_path
             
-            for filter_name, filter in pairs(settings.filters) do
-                module:conf("filter", filter_name, source_name, filter.pat, filter.fmt, settings.channel)
+            for i, filter in ipairs(settings.filters) do
+                if filter.channel_is_null then
+                    channel = nil
+                else
+                    channel = settings.channel
+                end
+
+                module:conf("filter", filter.name, source_name, filter.pat, filter.fmt, channel)
             end
         end
     end
@@ -116,11 +151,13 @@
             channel     = "PVLNet/#test",
             
             filters     = {
-                ["test.foo"]    = logwatch_filter_raw("foo"),
-                ["test.sudo"]   = logwatch_filter_sudo(),
+                logwatch_filter_raw             ("test.foo", "foo"  ),
+                logwatch_filter_sudo            ("test.sudo"        ),
+                logwatch_filter_no_cron         ("test.no_cron"     ),
+                logwatch_filter_no_su_nobody    ("test.no_cron_su"  ),
+                logwatch_filter_strip_timestamp ("test.all"         )
             }
         },
     },
 }
 
-
--- a/src/error.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/error.c	Sun Apr 12 23:27:15 2009 +0300
@@ -86,6 +86,10 @@
     {   ERR_LUA_ERR,                        "lua: error handling error",                ERR_EXTRA_STR       },
     {   ERR_LUA_FILE,                       "lua: error loading file",                  ERR_EXTRA_STR       },
     {   _ERR_INVALID,                       NULL,                                       0                   }
+}, _pcre_error_desc[] = {
+    {   ERR_PCRE_COMPILE,                   "pcre_compile",                             ERR_EXTRA_STR       },
+    {   ERR_PCRE_EXEC,                      "pcre_exec",                                ERR_EXTRA_STR       },
+    {   _ERR_INVALID,                       NULL,                                       0                   }
 };
 
 /**
@@ -99,6 +103,7 @@
     _config_error_desc,
     _module_error_desc,
     _lua_error_desc,
+    _pcre_error_desc,
     NULL
 };
 
--- a/src/irc_chan.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/irc_chan.c	Sun Apr 12 23:27:15 2009 +0300
@@ -8,7 +8,7 @@
 
 const char* irc_chan_name (struct irc_chan *chan)
 {
-    return chan->info.channel;
+    return chan ? chan->info.channel : "NULL";
 }
 
 /**
--- a/src/irc_net.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/irc_net.c	Sun Apr 12 23:27:15 2009 +0300
@@ -7,7 +7,7 @@
 
 const char* irc_net_name (struct irc_net *net)
 {
-    return net->info.network;
+    return net ? net->info.network : NULL;
 }
 
 /**
--- a/src/module.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/module.c	Sun Apr 12 23:27:15 2009 +0300
@@ -102,7 +102,7 @@
 
 const char* module_name (struct module *module)
 {
-    return module->info.name;
+    return module ? module->info.name : NULL;
 }
 
 /**
--- a/src/modules/logwatch.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/modules/logwatch.c	Sun Apr 12 23:27:15 2009 +0300
@@ -50,11 +50,17 @@
 {
     const struct logwatch_filter *filter;
     struct error_info err;
+    int ret;
 
     // apply each filter
     TAILQ_FOREACH(filter, &ctx->filters, logwatch_filters) {
-        if (logwatch_filter_apply(filter, source, line, &err))
+        // apply it
+        if ((ret = logwatch_filter_apply(filter, source, line, &err)) < 0)
             log_warn("logwatch_filter_apply(%s, %s, %s): %s", filter->name, source->name, line, error_msg(&err));
+
+        // blackhole?
+        if (ret > 0)
+            break;
     }
 }
 
@@ -101,7 +107,7 @@
     struct logwatch_filter *filter;
     
     const struct logwatch_source *source = NULL;
-    struct logwatch_chan *chan;
+    struct logwatch_chan *chan = NULL;
     const char *name, *source_name, *pattern, *format;
     struct irc_chan *irc_chan;
 
@@ -123,7 +129,7 @@
        RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "unknown logwatch_source name");
     
     // lookup channel
-    if ((chan = logwatch_get_chan(ctx, irc_chan)) == NULL)
+    if (irc_chan && (chan = logwatch_get_chan(ctx, irc_chan)) == NULL)
         RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "logwatch_get_chan failed");
     
     log_info("add filter: name=%s, source=%s, pattern=%s, format=%s, chan=%s",
@@ -155,11 +161,11 @@
         "using the given format expression (or pass through directly), and finally send the output to the given channel. "
         "If a filter with the same name already exists, it will be replaced.",
 
-        CONFIG_PARAM(       "name",    CONFIG_STRING,      "filter name",                              false   ),
-        CONFIG_PARAM(       "source",  CONFIG_STRING,      "optional logwatch_source to use",          true    ),
-        CONFIG_PARAM(       "pattern", CONFIG_STRING,      "optional regular expression pattern",      true    ),
-        CONFIG_PARAM(       "format",  CONFIG_STRING,      "optional output format",                   true    ),
-        CONFIG_PARAM(       "chan",    CONFIG_IRC_CHAN,    "channel to send output to",                false   )
+        CONFIG_PARAM(       "name",    CONFIG_STRING,       "filter name",                              false   ),
+        CONFIG_PARAM(       "source",  CONFIG_STRING,       "optional logwatch_source to use",          true    ),
+        CONFIG_PARAM(       "pattern", CONFIG_STRING,       "optional regular expression pattern",      true    ),
+        CONFIG_PARAM(       "format",  CONFIG_STRING,       "optional output format",                   true    ),
+        CONFIG_PARAM(       "chan",    CONFIG_IRC_CHAN,     "channel to send output to, or NULL to blackhole",  true)
     )
 );
 
--- a/src/modules/logwatch.h	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/modules/logwatch.h	Sun Apr 12 23:27:15 2009 +0300
@@ -194,9 +194,11 @@
 void logwatch_filter_destroy (struct logwatch_filter *filter);
 
 /**
- * Pass the given line (from the given source) through the given filter
+ * Pass the given line (from the given source) through the given filter.
+ *
+ * Returns 0 if the next filter should be tried, >0 if no more filters should be processed, and -err_t on error.
  */
-err_t logwatch_filter_apply (const struct logwatch_filter *filter, const struct logwatch_source *source, const char *line, struct error_info *err);
+int logwatch_filter_apply (const struct logwatch_filter *filter, const struct logwatch_source *source, const char *line, struct error_info *err);
 
 /**
  * Remove any filters registered with the given source
--- a/src/modules/logwatch_filter.c	Sun Apr 12 22:19:54 2009 +0300
+++ b/src/modules/logwatch_filter.c	Sun Apr 12 23:27:15 2009 +0300
@@ -69,16 +69,17 @@
 
 void logwatch_filter_destroy (struct logwatch_filter *filter)
 {
-    // remove it if it's registered
     if (filter->ctx)
+        // unreigster
         TAILQ_REMOVE(&filter->ctx->filters, filter, logwatch_filters);
     
     // this if is probably not needed
     if (filter->regexp) 
         pcre_free(filter->regexp);
     
-    // release the logwatch_chan ref
-    logwatch_chan_put(filter->chan);
+    if (filter->chan)    
+        // release the logwatch_chan ref
+        logwatch_chan_put(filter->chan);
 
     // free misc
     free(filter->format);
@@ -151,7 +152,7 @@
 // length of output line prefix
 #define LOGWATCH_FILTER_OUT_PREFIX_LEN 16
 
-err_t logwatch_filter_apply (const struct logwatch_filter *filter, const struct logwatch_source *source, const char *line, struct error_info *err)
+int logwatch_filter_apply (const struct logwatch_filter *filter, const struct logwatch_source *source, const char *line, struct error_info *err)
 {
     int ovec[LOGWATCH_FILTER_OVEC_ITEMS], ret;
     char buf[LOGWATCH_FILTER_OUT_MAX + 1], *buf_ptr = buf;
@@ -161,23 +162,24 @@
 
     // discard based on source?
     if (filter->source && source != filter->source)
-        return SUCCESS;
+        return 0;
     
     // match against the regexp?
     if (filter->regexp && (ret = pcre_exec(filter->regexp, NULL, line, strlen(line), 0, 0, ovec, LOGWATCH_FILTER_OVEC_ITEMS)) <= 0) {
         if (ret == PCRE_ERROR_NOMATCH)
             // no match, ignore
-            return SUCCESS;
-
-        else if (ret == 0)
-            // too many match groups
-            return SET_ERROR(err, -1);
+            return 0;
 
         else
             // misc. error
-            RETURN_SET_ERROR_EXTRA(err, ERR_PCRE_EXEC, ret);
+            // XXX: might be 0 for too many groups
+            JUMP_SET_ERROR_EXTRA(err, ERR_PCRE_EXEC, ret);
     }
 
+    // blackhole?
+    if (!filter->chan)
+        return 1;
+
     // format?
     if (filter->format) {
         // setup the ctx
@@ -206,10 +208,10 @@
     if ((ERROR_CODE(err) = logwatch_chan_msg(filter->chan, "[%s] %s", filter->name, buf)))
         goto error;
 
-    // ok
-    return SUCCESS;
+    // ok, stop here
+    return 1;
 
 error:
-    return ERROR_CODE(err);    
+    return -ERROR_CODE(err);    
 }