implement logwatch_conf_filter such that it compiles and loads, but not yet tested
authorTero Marttila <terom@fixme.fi>
Wed, 08 Apr 2009 02:25:30 +0300
changeset 121 4682ebbc5644
parent 120 576bab0a1c5a
child 122 52ffbdb6bba1
implement logwatch_conf_filter such that it compiles and loads, but not yet tested
cmake/Modules/FindPCRE.cmake
src/CMakeLists.txt
src/config.c
src/config.h
src/error.c
src/error.h
src/modules/irc_log.c
src/modules/logwatch.c
src/modules/logwatch.h
src/modules/logwatch_filter.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/Modules/FindPCRE.cmake	Wed Apr 08 02:25:30 2009 +0300
@@ -0,0 +1,23 @@
+# Find PCRE
+# Once done, this will define:
+#
+#   PCRE_FOUND
+#   PCRE_INCLUDE_DIRS
+#   PCRE_LIBRARIES
+
+include (LibFindMacros)
+
+# include dir
+find_path (PCRE_INCLUDE_DIR
+    NAMES "pcre.h"
+)
+
+# library
+find_library (PCRE_LIBRARY
+    NAMES "pcre"
+)
+
+# set the external vars
+set (PCRE_PROCESS_INCLUDES PCRE_INCLUDE_DIR)
+set (PCRE_PROCESS_LIBS PCRE_LIBRARY)
+libfind_process (PCRE)
--- a/src/CMakeLists.txt	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/CMakeLists.txt	Wed Apr 08 02:25:30 2009 +0300
@@ -4,9 +4,10 @@
 find_package (LibPQ REQUIRED)
 find_package (Evsql REQUIRED)
 find_package (Lua51 REQUIRED)
+find_package (PCRE REQUIRED)
 
 # add our include path
-include_directories (${LibEvent_INCLUDE_DIRS} ${GnuTLS_INCLUDE_DIRS} ${Evsql_INCLUDE_DIRS} ${Lua51_INCLUDE_DIRS})
+include_directories (${LibEvent_INCLUDE_DIRS} ${GnuTLS_INCLUDE_DIRS} ${Evsql_INCLUDE_DIRS} ${Lua51_INCLUDE_DIRS} ${PCRE_INCLUDE_DIRS})
 
 # define our source code modules
 set (CORE_SOURCES error.c log.c)
@@ -18,7 +19,7 @@
 set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES} signals.c module.c config.c)
 set (TEST_SOURCES test.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES})
 set (IRC_LOG_SOURCES modules/irc_log.c)
-set (LOGWATCH_SOURCES modules/logwatch.c modules/logwatch_source.c)
+set (LOGWATCH_SOURCES modules/logwatch.c modules/logwatch_source.c modules/logwatch_filter.c)
 
 # define our libraries
 set (MODULE_LIBRARIES "dl")
@@ -39,6 +40,7 @@
 target_link_libraries (nexus ${NEXUS_LIBRARIES})
 target_link_libraries (test ${NEXUS_LIBRARIES})
 target_link_libraries (irc_log ${Evsql_LIBRARIES})
+target_link_libraries (logwatch ${PCRE_LIBRARIES})
 
 # global target properties
 set_target_properties (nexus test irc_log logwatch PROPERTIES
--- a/src/config.c	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/config.c	Wed Apr 08 02:25:30 2009 +0300
@@ -128,7 +128,7 @@
     }
 
     // the option's handler?
-    if (option->func && option->func(ctx, values, err))
+    if (option->func && option->func(ctx, option, values, err))
         goto error;
 
     // ok
@@ -170,7 +170,7 @@
     return config_apply(options, ctx, name, conf_value, err);
 }
 
-err_t config_apply_raw (const struct config_option *options, struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err)
+err_t config_apply_raw (const struct config_option options[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err)
 {
     const struct config_option *option;
     struct config_value value[2] = {
@@ -190,3 +190,46 @@
     return config_apply_opt(option, ctx, value, err);
 }
 
+const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name)
+{
+    const struct config_param *param;
+    const struct config_value *value;
+
+    // handle each param
+    for (param = option->params, value = values; param->name && param->type && value->type; param++, value++) {
+        // matches?
+        if (strcmp(param->name, name) == 0) {
+            // not NULL?
+            if (value->type != CONFIG_NULL)
+                return value;
+
+            else
+                return NULL;
+        }
+    }
+
+    // not found
+    return NULL;
+}
+
+const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name)
+{
+    const struct config_value *value;
+
+    return (value = config_get_value(option, values, name)) ? value->string : NULL;
+}
+
+struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name)
+{
+    const struct config_value *value;
+
+    return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL;
+}
+
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const char *user_type)
+{
+    const struct config_value *value;
+
+    return ((value = config_get_value(option, values, name)) && strcmp(value->user.type, user_type) == 0) ? value->user.ptr : NULL;
+}
+
--- a/src/config.h	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/config.h	Wed Apr 08 02:25:30 2009 +0300
@@ -15,9 +15,15 @@
 enum config_type {
     CONFIG_INVALID,
 
+    /** No value at all */
+    CONFIG_NULL,
+
     /** A plain NUL-terminated string */
     CONFIG_STRING,
 
+    /** A user-defined type, further identified by a string */
+    CONFIG_USER,
+
     /** An IRC channel */
     CONFIG_IRC_CHAN,
 };
@@ -36,6 +42,15 @@
 
         /** Value for CONFIG_IRC_CHAN */
         struct irc_chan *irc_chan;
+        
+        /** Value for CONFIG_USER */
+        struct {
+            /** The specific user type */
+            const char *type;
+
+            /** A pointer to the user type */
+            void *ptr;
+        } user;
     };
 };
 
@@ -49,6 +64,9 @@
     /** The type */
     enum config_type type;
 
+    /** The specific type for CONFIG_USER */
+    const char *user_type;
+
     /** Description */
     const char *description;
 
@@ -60,6 +78,9 @@
 
     /** Use handler function? */
     bool is_handler;
+
+    /** Optional value? */
+    bool optional;
 };
 
 /**
@@ -83,38 +104,56 @@
     const struct config_param params[CONFIG_PARAMS_MAX];
     
     /** The handler function */
-    err_t (*func) (void *ctx, const struct config_value values[], struct error_info *err);
+    err_t (*func) (void *ctx, const struct config_option *option, const struct config_value values[], struct error_info *err);
 
     /** Help text */
     const char *help;
 };
 
-#define CONFIG_PARAM(name, type, desc) \
-    {   name, type, desc, { NULL }, false }
+#define CONFIG_PARAM(name, type, desc, optional) \
+    {   name, type, NULL, desc, { NULL }, false, optional }
+
+#define CONFIG_PARAM_OPTIONAL(name, type, desc) \
+    CONFIG_PARAM(name, type, desc, true)
+
+#define CONFIG_PARAM_USER(name, user_type, desc, optional) \
+    {   name, CONFIG_USER, user_type, desc, { NULL }, false, optional }
+
+#define CONFIG_PARAM_USER_OPTIONAL(name, user_type, desc) \
+    CONFIG_PARAM_USER(name, user_type, desc, true) 
 
 #define CONFIG_PARAM_HANDLER(name, type, desc, _func_name_, func) \
-    {   name, type, desc, {._func_name_ = func }, true }
+    {   name, type, NULL, desc, {._func_name_ = func }, true, false }
 
-#define CONFIG_OPT(name, params, func, help) \
-    {   name, params, func, help }
+#define CONFIG_PARAM_END \
+    {   NULL, 0, NULL, NULL, { NULL }, false, false }
+
+#define CONFIG_OPT(name, func, help, ...) \
+    {   name, { __VA_ARGS__, CONFIG_PARAM_END }, func, help }
 
 #define CONFIG_OPT_STRING(name, func, desc, help) \
-    {   name, { CONFIG_PARAM_HANDLER(name, CONFIG_STRING, desc, string, func) }, NULL, help }
+    CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_STRING, desc, string, func))
 
 #define CONFIG_OPT_IRC_CHAN(name, func, desc, help) \
-    {   name, { CONFIG_PARAM_HANDLER(name, CONFIG_IRC_CHAN, desc, irc_chan, func) }, NULL, help }
+    CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_IRC_CHAN, desc, irc_chan, func))
 
 #define CONFIG_OPT_END \
     {   NULL,   {}, NULL, NULL }
 
+#define CONFIG_OPTIONS(...) \
+    { __VA_ARGS__, CONFIG_OPT_END }
+
 #define CONFIG_VALUE(type, _name_, value) \
-    {   (type), { ._name_ = (value) } }
+    {   (type), { ._name_ = value } }
 
 #define CONFIG_VALUE_STRING(str_value) \
-    CONFIG_VALUE(CONFIG_STRING, string, str_value)
+    CONFIG_VALUE(CONFIG_STRING, string, (str_value))
 
 #define CONFIG_VALUE_IRC_CHAN(irc_chan_value) \
-    CONFIG_VALUE(CONFIG_IRC_CHAN, irc_chan, irc_chan_value)
+    CONFIG_VALUE(CONFIG_IRC_CHAN, irc_chan, (irc_chan_value))
+
+#define CONFIG_VALUE_USER(user_type, user_value) \
+    CONFIG_VALUE(CONFIG_USER, user, { user_type, user_value })
 
 #define CONFIG_VALUE_END \
     {   0, { NULL } }
@@ -206,6 +245,15 @@
  *
  * @see config_parse
  */
-err_t config_apply_raw (const struct config_option *options, struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err);
+err_t config_apply_raw (const struct config_option option[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err);
+
+/**
+ * Lookup a config_value by name from the given list of config_values for the given config_option
+ */
+const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name);
+
+const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name);
+struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name);
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const char *user_type);
 
 #endif
--- a/src/error.c	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/error.c	Wed Apr 08 02:25:30 2009 +0300
@@ -59,14 +59,11 @@
     {   _ERR_INVALID,                       NULL,                                       0                   }
 
 }, _config_error_desc[] = {
-    // extra: the (unknown) config field name
-    {   ERR_CONFIG_NAME,                    "no matching config name",                  ERR_EXTRA_STR       },
-
-    // XXX: extra: the mismatching types
-    {   ERR_CONFIG_TYPE,                    "wrong config type",                        ERR_EXTRA_NONE      },
-    
-    // extra: the name of the field with missing value
+    {   ERR_CONFIG_NAME,                    "unknown config option",                    ERR_EXTRA_STR       },
+    {   ERR_CONFIG_TYPE,                    "invalid config type",                      ERR_EXTRA_NONE      },
     {   ERR_CONFIG_REQUIRED,                "missing required value",                   ERR_EXTRA_STR       },
+    {   ERR_CONFIG_VALUE,                   "invalid value",                            ERR_EXTRA_STR       },
+    {   ERR_CONFIG_PARAMS,                  "invalid number of paramters",              ERR_EXTRA_NONE      },
 
     {   _ERR_INVALID,                       NULL,                                       0                   }
 
@@ -97,6 +94,7 @@
     _sock_error_desc,
     _sock_gnutls_error_desc,
     _irc_error_desc,
+    _config_error_desc,
     _module_error_desc,
     _lua_error_desc,
     NULL
--- a/src/error.h	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/error.h	Wed Apr 08 02:25:30 2009 +0300
@@ -108,6 +108,10 @@
     _ERR_IRC_CHAN   = 0x000d00,
     ERR_IRC_CHAN_STATE,
 
+    /** pcre errors */
+    _ERR_PCRE       = 0x000e00,
+    ERR_PCRE_COMPILE,       ///< pcre_compile: <error_msg>
+
     /** General errors */
     _ERR_GENERAL    = 0xffff00,
     ERR_CMD_OPT,
--- a/src/modules/irc_log.c	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/modules/irc_log.c	Wed Apr 08 02:25:30 2009 +0300
@@ -462,12 +462,10 @@
 /**
  * Our configuration options
  */
-struct config_option irc_log_config_options[] = {
+struct config_option irc_log_config_options[] = CONFIG_OPTIONS(
     CONFIG_OPT_STRING(      "db_info",  &irc_log_conf_db_info,  "[<key>=<value> [...]]",    "set database connection info, see libpq docs"  ),
-    CONFIG_OPT_IRC_CHAN(    "channel",  &irc_log_conf_channel,  "<channel>",                "log the given channel"                         ),
-
-    CONFIG_OPT_END
-};
+    CONFIG_OPT_IRC_CHAN(    "channel",  &irc_log_conf_channel,  "<channel>",                "log the given channel"                         )
+);
 
 /**
  * Deinitialize, logging CLOSE events for all channels, and removing any hooks we've added
--- a/src/modules/logwatch.c	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/modules/logwatch.c	Wed Apr 08 02:25:30 2009 +0300
@@ -24,6 +24,27 @@
     return SUCCESS;
 }
 
+struct logwatch_chan* logwatch_add_chan (struct logwatch *ctx, struct irc_chan *chan, struct error_info *err)
+{
+    struct logwatch_chan *lw_chan;
+
+    // alloc
+    if ((lw_chan = calloc(1, sizeof(*lw_chan))) == NULL)
+        JUMP_SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lw_chan->chan = chan;
+
+    // add to list
+    TAILQ_INSERT_TAIL(&ctx->channels, lw_chan, logwatch_channels);
+
+    // ok
+    return lw_chan;
+
+error:
+    return NULL;    
+}
+
 /**
  * Add a logwatch_source with a FIFO at the given path
  */
@@ -40,13 +61,53 @@
 }
 
 /**
+ * Add a logwatch_filter for the given bits
+ */
+static err_t logwatch_conf_filter (void *_ctx, const struct config_option *option, const struct config_value values[], struct error_info *err)
+{
+    struct logwatch *ctx = _ctx;
+    struct logwatch_filter *filter;
+    
+    const struct logwatch_source *source;
+    const char *name, *pattern, *format;
+    struct irc_chan *chan;
+
+    // parse arguments
+    name    = config_get_string     (option, values, "name"     );
+    source  = config_get_user       (option, values, "source",  "logwatch_source"   );
+    pattern = config_get_string     (option, values, "pattern"  );
+    format  = config_get_string     (option, values, "format"   );
+    chan    = config_get_irc_chan   (option, values, "chan"     );
+    
+    // invoke
+    if ((filter = logwatch_filter(ctx, name, source, pattern, format, chan, err)) == NULL)
+        goto error;
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);    
+}
+
+/**
  * Configuration options
  */
-struct config_option logwatch_config_options[] = {
-    CONFIG_OPT_STRING(      "source_fifo",  &logwatch_conf_source_fifo, "<path>",           "read lines from the fifo at the given path"    ),
+struct config_option logwatch_config_options[] = CONFIG_OPTIONS(
+    CONFIG_OPT_STRING(  "source_fifo",  logwatch_conf_source_fifo, "<path>",           "read lines from the fifo at the given path"    ),
 
-    CONFIG_OPT_END
-};
+    CONFIG_OPT(         "filter",       logwatch_conf_filter, 
+        "filter lines (from source, if given), using the given regular expression pattern (if given), "
+        "format output using the given format expression (if given), and finally send the filtered/formatted lines "
+        "to the given channel",
+
+        CONFIG_PARAM(       "name",    CONFIG_STRING,      "filter name",                              false   ),
+        CONFIG_PARAM_USER(  "source",  "logwatch_source",  "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   )
+    )
+);
 
 /**
  * Deinitialize by freeing all filters, closing all sources, and releasing all channels.
--- a/src/modules/logwatch.h	Wed Apr 08 01:28:46 2009 +0300
+++ b/src/modules/logwatch.h	Wed Apr 08 02:25:30 2009 +0300
@@ -17,7 +17,7 @@
  * XXX: currently this is just hardcoded to be a FIFO
  */
 struct logwatch_source {
-    /** The logwatch context */
+    /** The logwatch context we are registered to */
     struct logwatch *ctx;
 
     /** The input FIFO */
@@ -36,20 +36,29 @@
  * A filter specifies what lines to match, and how to then format the output.
  */
 struct logwatch_filter {
+    /** The logwatch context we are registered to */
+    struct logwatch *ctx;
+
     /** The name of the filter */
     char *name;
 
     /** The source to restrict this to, if any */
-    const struct logwatch_sourcee *source;
+    const struct logwatch_source *source;
 
-    /** The compiled regular expression to match */
+    /** 
+     * The compiled regular expression to match, or NULL to match all lines.
+     */
     pcre *regexp;
 
-    /** The output format string */
+    /** 
+     * The output format string, or NULL to send the line itself as-is
+     */
     char *format;
 
-    /** The channel to send output to */
-    struct logwatch_chan *chan;
+    /**
+     * The channel to send output to 
+     */
+    struct irc_chan *chan;
 
     /** Our entry in logwatch::filters */
     TAILQ_ENTRY(logwatch_filter) logwatch_filters;
@@ -57,6 +66,8 @@
 
 /**
  * A channel for outputting logwatch output
+ *
+ * XXX; do we need this?
  */
 struct logwatch_chan {
     /** The channel... */
@@ -102,12 +113,19 @@
 
 /**
  * Add a new output channel
+ *
+ * XXX; do we need this?
  */
-struct logwatch_chan* logwatch_add_chan (struct logwatch *ctx, struct irc_chan *chan);
+struct logwatch_chan* logwatch_add_chan (struct logwatch *ctx, struct irc_chan *chan, struct error_info *err);
 
 /**
  * Add a new filter
  */
-struct logwatch_filter logwatch_add_filter (struct logwatch *ctx, const struct logwatch_source *source,
-        const char *regexp, const char *fmt, struct logwatch_chan *chan);
+struct logwatch_filter* logwatch_filter (struct logwatch *ctx, const char *name, const struct logwatch_source *source,
+        const char *pattern, const char *fmt, struct irc_chan *chan, struct error_info *err);
 
+/**
+ * Destroy a logwatch_filter, unregistering it
+ */
+void logwatch_filter_destroy (struct logwatch_filter *filter);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/logwatch_filter.c	Wed Apr 08 02:25:30 2009 +0300
@@ -0,0 +1,61 @@
+#include "logwatch.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct logwatch_filter* logwatch_filter (struct logwatch *ctx, const char *name, const struct logwatch_source *source,
+        const char *pattern, const char *format, struct irc_chan *chan, struct error_info *err)
+{
+    struct logwatch_filter *filter;
+    const char *err_msg;
+    int err_offset;
+
+    // alloc
+    if ((filter = calloc(1, sizeof(*filter))) == NULL)
+        JUMP_SET_ERROR(err, ERR_CALLOC);
+
+    // optional source
+    filter->source = source;
+
+    // name?
+    if (name && (filter->name = strdup(name)) == NULL)
+        JUMP_SET_ERROR(err, ERR_STRDUP);
+    
+    // compile the regexp
+    // XXX: any flags?
+    if (pattern && (filter->regexp = pcre_compile(pattern, 0, &err_msg, &err_offset, NULL)))
+        JUMP_SET_ERROR_STR(err, ERR_PCRE_COMPILE, err_msg);
+
+    // format?
+    if (format && (filter->format = strdup(format)) == NULL)
+        JUMP_SET_ERROR(err, ERR_STRDUP);
+   
+    // output channel
+    filter->chan = chan;
+ 
+    // add it
+    TAILQ_INSERT_TAIL(&ctx->filters, filter, logwatch_filters);
+    filter->ctx = ctx;
+
+    // ok
+    return filter;
+
+error:
+    // cleanup
+    if (filter)
+        logwatch_filter_destroy(filter);
+    
+    return NULL;
+}
+
+void logwatch_filter_destroy (struct logwatch_filter *filter)
+{
+    if (filter->ctx)
+        // remove it
+        TAILQ_REMOVE(&filter->ctx->filters, filter, logwatch_filters);
+
+    free(filter->format);
+    if (filter->regexp) pcre_free(filter->regexp);
+    free(filter->name);
+    free(filter);
+}