implement logwatch_source names, and logwatch_chan
authorTero Marttila <terom@fixme.fi>
Sun, 12 Apr 2009 17:18:06 +0300
changeset 132 f2ece471fb07
parent 131 df949b399491
child 133 e2d0c0c23b39
implement logwatch_source names, and logwatch_chan
TODO
src/error.h
src/irc_cmd.h
src/modules/logwatch.c
src/modules/logwatch.h
src/modules/logwatch_filter.c
src/modules/logwatch_source.c
src/str.c
--- a/TODO	Sat Apr 11 06:03:49 2009 +0300
+++ b/TODO	Sun Apr 12 17:18:06 2009 +0300
@@ -9,11 +9,15 @@
  * fix use of line_proto_send
  * more expansive tests
 
+irc_cmd/chain:
+ * handle irc_cmd_remove from inside irc_cmd_invoke
+
 irc_net:
  * reconnect, maybe cycling servers?
 
 config:
- * An even more advanced structured value parser that can then handle config values for more things like the command line --options
+ * user-defined types
+ * return values
 
 irc_log:
  * recode to valid UTF8
--- a/src/error.h	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/error.h	Sun Apr 12 17:18:06 2009 +0300
@@ -120,6 +120,7 @@
     _ERR_GENERAL    = 0xffff00,
     ERR_CMD_OPT,
     ERR_UNKNOWN,
+    ERR_DUP_NAME,
 
 };
 
--- a/src/irc_cmd.h	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/irc_cmd.h	Sun Apr 12 17:18:06 2009 +0300
@@ -5,6 +5,8 @@
  * @file
  *
  * Command handlers callback lists for use with irc_line's
+ *
+ * XXX: irc_cmd_remove called from iniside irc_cmd_invoke?
  */
 
 #include "irc_line.h"
--- a/src/modules/logwatch.c	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/modules/logwatch.c	Sun Apr 12 17:18:06 2009 +0300
@@ -3,6 +3,8 @@
 #include "../config.h"
 #include "../log.h"
 
+#include <assert.h>
+
 static err_t logwatch_init (struct nexus *nexus, void **ctx_ptr, struct error_info *err)
 {
     struct logwatch *ctx;
@@ -25,25 +27,90 @@
     return SUCCESS;
 }
 
-struct logwatch_chan* logwatch_add_chan (struct logwatch *ctx, struct irc_chan *chan, struct error_info *err)
+/**
+ * Look for an existing logwatch_chan entry, returning it if it exists
+ */
+static struct logwatch_chan* logwatch_chan_lookup (struct logwatch *ctx, struct irc_chan *irc_chan)
 {
-    struct logwatch_chan *lw_chan;
+    struct logwatch_chan *chan;
 
+    // look for it
+    TAILQ_FOREACH(chan, &ctx->channels, logwatch_channels)
+        if (chan->irc_chan == irc_chan)
+            return chan;
+
+    // not found
+    return NULL;
+}
+
+/**
+ * Create a new irc_chan with an initial refcount of one
+ */
+static struct logwatch_chan* logwatch_chan_create (struct logwatch *ctx, struct irc_chan *irc_chan)
+{
+    struct logwatch_chan *chan;
+   
     // alloc
-    if ((lw_chan = calloc(1, sizeof(*lw_chan))) == NULL)
-        JUMP_SET_ERROR(err, ERR_CALLOC);
+    if ((chan = calloc(1, sizeof(*chan))) == NULL)
+        return NULL;
 
     // store
-    lw_chan->chan = chan;
+    chan->ctx = ctx;
+    chan->irc_chan = irc_chan;
+    chan->refcount = 1;
 
-    // add to list
-    TAILQ_INSERT_TAIL(&ctx->channels, lw_chan, logwatch_channels);
+    // add
+    TAILQ_INSERT_TAIL(&ctx->channels, chan, logwatch_channels);
 
     // ok
-    return lw_chan;
+    return chan;
+}
 
-error:
-    return NULL;    
+/**
+ * Destroy a logwatch_chan
+ */
+static void logwatch_chan_destroy (struct logwatch_chan *chan)
+{
+    // XXX: check refcount
+    assert(chan->refcount == 0);
+
+    // remove
+    TAILQ_REMOVE(&chan->ctx->channels, chan, logwatch_channels);
+
+    // release
+    free(chan);
+}
+
+struct logwatch_chan* logwatch_get_chan (struct logwatch *ctx, struct irc_chan *irc_chan)
+{
+    struct logwatch_chan *chan;
+    
+    // existing?
+    if ((chan = logwatch_chan_lookup(ctx, irc_chan))) {
+        // get ref
+        chan->refcount++;
+
+    } else {
+        // create a new one with a ref
+        // XXX: errors...
+        chan = logwatch_chan_create(ctx, irc_chan);
+    }
+
+    // ok
+    return chan;
+}
+
+void logwatch_put_chan (struct logwatch_chan *chan)
+{
+    // valid refcount
+    assert(chan->refcount);
+
+    // still alive?
+    if (--chan->refcount)
+        return;
+
+    // destroy
+    logwatch_chan_destroy(chan);
 }
 
 void logwatch_on_line (struct logwatch *ctx, const struct logwatch_source *source, const char *line)
@@ -54,17 +121,28 @@
     // apply each filter
     TAILQ_FOREACH(filter, &ctx->filters, logwatch_filters) {
         if (logwatch_filter_apply(filter, source, line, &err))
-            log_warn("logwatch_filter_apply(%s, %p, %s): %s", filter->name, source, line, error_msg(&err));
+            log_warn("logwatch_filter_apply(%s, %s, %s): %s", filter->name, source->name, line, error_msg(&err));
     }
 }
 
+void logwatch_on_error (struct logwatch *ctx, const struct logwatch_source *source, const struct error_info *err)
+{
+    struct logwatch_chan *chan;
+    struct logwatch_filter *filter;
+    
+    // notify each relevant channel
+}
+
 /**
  * Add a logwatch_source with a FIFO at the given path
  */
-static err_t logwatch_conf_source_fifo (void *_ctx, char *path, struct error_info *err)
+static err_t logwatch_conf_source_fifo (void *_ctx, const struct config_option *option, const struct config_value values[], struct error_info *err)
 {
     struct logwatch *ctx = _ctx;
     
+    // parse arguments
+    const char *path = config_get_string(option, values, "path");
+    
     // open it
     if (logwatch_open_fifo(ctx, path, err) == NULL)
        return ERROR_CODE(err);
@@ -81,16 +159,25 @@
     struct logwatch *ctx = _ctx;
     struct logwatch_filter *filter;
     
-    const struct logwatch_source *source;
-    const char *name, *pattern, *format;
-    struct irc_chan *chan;
+    const struct logwatch_source *source = NULL;
+    struct logwatch_chan *chan;
+    const char *name, *source_name, *pattern, *format;
+    struct irc_chan *irc_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"     );
+    name        = config_get_string     (option, values, "name"     );
+    source_name = config_get_string     (option, values, "source"   );
+    pattern     = config_get_string     (option, values, "pattern"  );
+    format      = config_get_string     (option, values, "format"   );
+    irc_chan    = config_get_irc_chan   (option, values, "chan"     );
+
+    // lookup name
+    if (source_name && (source = logwatch_source_lookup(ctx, source_name)) == NULL)
+       RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "unknown logwatch_source name");
+    
+    // lookup the channel
+    if ((chan = logwatch_get_chan(ctx, irc_chan)) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "logwatch_get_chan failed");
 
     // invoke
     if ((filter = logwatch_filter(ctx, name, source, pattern, format, chan, err)) == NULL)
@@ -107,42 +194,40 @@
  * Configuration options
  */
 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(         "source_fifo",  logwatch_conf_source_fifo,
+        "read input lines from the FIFO at the given path",
+
+        CONFIG_PARAM(       "path",     CONFIG_STRING,      "filesystem path to FIFO",                  false   )
+    ),
 
     CONFIG_OPT(         "filter",       logwatch_conf_filter, 
         "filter lines from source (or all), using the given regular expression (or all lines), and format output "
         "using the given format expression (or pass through directly), and finally send the output 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(       "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   )
     )
 );
 
-/**
- * Deinitialize by freeing all filters, closing all sources, and releasing all channels.
- *
- * XXX: unused
- * /
-static err_t logwatch_unload (void *_ctx, struct module *module)
-{
-    struct logwatch *ctx = _ctx;
-
-    // XXX: implement
-
-    return SUCCESS;
-} */
-
 static void logwatch_destroy (void *_ctx)
 {
     struct logwatch *ctx = _ctx;
+    struct logwatch_filter *filter;
     struct logwatch_source *source;
 
+    // flush the filters list
+    while ((filter = TAILQ_FIRST(&ctx->filters)))
+        logwatch_filter_destroy(filter);
+
     // flush the sources list
     while ((source = TAILQ_FIRST(&ctx->sources)))
         logwatch_source_destroy(source);
+
+    // release the context
+    free(ctx);
 }
 
 /**
@@ -151,7 +236,7 @@
 struct module_desc logwatch_module = {
     .init               = logwatch_init,
     .config_options     = logwatch_config_options,
-    .unload             = NULL, // logwatch_unload,
+    .unload             = NULL,
     .destroy            = logwatch_destroy
 };
 
--- a/src/modules/logwatch.h	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/modules/logwatch.h	Sun Apr 12 17:18:06 2009 +0300
@@ -20,6 +20,9 @@
     /** The logwatch context we are registered to */
     struct logwatch *ctx;
 
+    /** Our name */
+    char *name;
+
     /** The input FIFO */
     struct line_proto *lp;
 
@@ -33,6 +36,23 @@
 #define LOGWATCH_SOURCE_LINE_MAX 1024
 
 /**
+ * Logwatch's per-channel state
+ */
+struct logwatch_chan {
+    /** The logwatch context we are registered to */
+    struct logwatch *ctx;
+
+    /** The actual irc_chan */
+    struct irc_chan *irc_chan;
+
+    /** Number of references */
+    size_t refcount;
+
+    /** Our entry in logwatch::channels */
+    TAILQ_ENTRY(logwatch_chan) logwatch_channels;
+};
+
+/**
  * A filter specifies what lines to match, and how to then format the output.
  */
 struct logwatch_filter {
@@ -58,7 +78,7 @@
     /**
      * The channel to send output to 
      */
-    struct irc_chan *chan;
+    struct logwatch_chan *chan;
 
     /** Our entry in logwatch::filters */
     TAILQ_ENTRY(logwatch_filter) logwatch_filters;
@@ -75,19 +95,6 @@
 #define LOGWATCH_FILTER_OUT_MAX 450
 
 /**
- * A channel for outputting logwatch output
- *
- * XXX; do we need this?
- */
-struct logwatch_chan {
-    /** The channel... */
-    struct irc_chan *chan;
-
-    /** Our entry in logwatch::channels */
-    TAILQ_ENTRY(logwatch_chan) logwatch_channels;
-};
-
-/**
  * The logwatch module state
  */
 struct logwatch {
@@ -100,7 +107,7 @@
     /** List of filters */
     TAILQ_HEAD(logwatch_filter_list, logwatch_filter) filters;
 
-    /** List of output channels */
+    /** List of chanenls */
     TAILQ_HEAD(logwatch_chan_list, logwatch_chan) channels;
 };
 
@@ -108,7 +115,7 @@
  * Add a new FIFO logwatch_source
  *
  * @param ctx the logwatch state
- * @param path the path to the fifo
+ * @param path the path to the fifo, also used as the name
  * @param err returned error info
  * @return the new logwatch_source, or NULL on error
  */
@@ -117,22 +124,35 @@
 /**
  * Destroy a source, removing it from the list of sources.
  *
- * XXX: remove all affected filters as well?
+ * XXX: remove all affected filters as well
  */
 void logwatch_source_destroy (struct logwatch_source *source);
 
 /**
- * Add a new output channel
+ * Lookup a logwatch_source by name
+ */
+const struct logwatch_source* logwatch_source_lookup (struct logwatch *ctx, const char *name);
+
+/**
+ * Find an existing logwatch_chan for the given channel, or create a new one. Returns a real reference that should be
+ * returned using logwatch_put_chan().
  *
- * XXX; do we need this?
+ * @returns NULL on ERR_CALLOC
  */
-struct logwatch_chan* logwatch_add_chan (struct logwatch *ctx, struct irc_chan *chan, struct error_info *err);
+struct logwatch_chan* logwatch_get_chan (struct logwatch *ctx, struct irc_chan *irc_chan);
+
+/**
+ * Returns a reference aquired using logwatch_get_chan().
+ */
+void logwatch_put_chan (struct logwatch_chan *chan);
 
 /**
  * Add a new filter.
+ *
+ * The given logwatch_chan should be a new reference.
  */
 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);
+        const char *pattern, const char *format, struct logwatch_chan *chan, struct error_info *err);
 
 /**
  * Destroy a logwatch_filter, unregistering it
@@ -145,7 +165,16 @@
 err_t logwatch_filter_apply (const struct logwatch_filter *filter, const struct logwatch_source *source, const char *line, struct error_info *err);
 
 /**
- * Handle a line from the given source by applying it to all filters
+ * Remove any filters registered with the given source
+ */
+void logwatch_filter_remove (struct logwatch *ctx, const struct logwatch_source *source);
+
+/**
+ * Handle a line from the given source by applying it to all filters.
  */
 void logwatch_on_line (struct logwatch *ctx, const struct logwatch_source *source, const char *line);
 
+/**
+ * Handle a disasterous error on a logwatch_source by notifying any irc_chan's that it is linked to via our filters.
+ */
+void logwatch_on_error (struct logwatch *ctx, const struct logwatch_source *source, const struct error_info *err);
--- a/src/modules/logwatch_filter.c	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/modules/logwatch_filter.c	Sun Apr 12 17:18:06 2009 +0300
@@ -8,7 +8,7 @@
 #include <assert.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)
+        const char *pattern, const char *format, struct logwatch_chan *chan, struct error_info *err)
 {
     struct logwatch_filter *filter;
     const char *err_msg;
@@ -39,6 +39,8 @@
  
     // add it
     TAILQ_INSERT_TAIL(&ctx->filters, filter, logwatch_filters);
+
+    // mark it as registered
     filter->ctx = ctx;
 
     // ok
@@ -54,16 +56,37 @@
 
 void logwatch_filter_destroy (struct logwatch_filter *filter)
 {
+    // remove it if it's registered
     if (filter->ctx)
-        // remove it
         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_put_chan(filter->chan);
 
+    // free misc
     free(filter->format);
-    if (filter->regexp) pcre_free(filter->regexp);
     free(filter->name);
     free(filter);
 }
 
+void logwatch_filter_remove (struct logwatch *ctx, const struct logwatch_source *source)
+{
+    struct logwatch_filter *filter, *next;
+    
+    // inspect each filter
+    for (filter = TAILQ_FIRST(&ctx->filters); filter; filter = next) {
+        next = TAILQ_NEXT(filter, logwatch_filters);
+    
+        // destroy matching
+        if (filter->source == source)
+            logwatch_filter_destroy(filter);
+    }
+}
+
 /**
  * str_format context for logwatch_filter_apply operation
  */
@@ -81,6 +104,9 @@
     size_t group_count;
 };
 
+/**
+ * Our callback for str_format params used by logwatch_filter_apply()
+ */
 static err_t logwatch_filter_str_format_cb (const char *name, const char **value, ssize_t *value_len, void *arg)
 {
     struct logwatch_filter_str_format_ctx *ctx = arg;
@@ -161,10 +187,10 @@
         buf_ptr += str_advance(NULL, &buf_size, str_quote(buf_ptr, buf_size, line, -1));
     }
 
-    log_info("%s:%p -> %s:%s", line, source, irc_chan_name(filter->chan), buf);
+    log_info("<%s>:%s -> %s:%s", source->name, line, irc_chan_name(filter->chan->irc_chan), buf);
 
     // then send it as a notice
-    if ((ERROR_CODE(err) = irc_chan_NOTICE(filter->chan, buf)))
+    if ((ERROR_CODE(err) = irc_chan_NOTICE(filter->chan->irc_chan, buf)))
         goto error;
 
     // ok
--- a/src/modules/logwatch_source.c	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/modules/logwatch_source.c	Sun Apr 12 17:18:06 2009 +0300
@@ -2,6 +2,7 @@
 #include "../log.h"
 
 #include <stdlib.h>
+#include <string.h>
 
 void logwatch_source_on_line (char *line, void *arg)
 {
@@ -19,6 +20,9 @@
     
     // XXX: complain more loudly
     log_error("%s", error_msg(err));
+
+    // drop it
+    logwatch_source_destroy(source);
 }
 
 static const struct line_proto_callbacks lp_callbacks = {
@@ -26,14 +30,35 @@
     .on_error   = logwatch_source_on_error,
 };
 
+const struct logwatch_source* logwatch_source_lookup (struct logwatch *ctx, const char *name)
+{
+    const struct logwatch_source *source;
+
+    // compare
+    TAILQ_FOREACH(source, &ctx->sources, logwatch_sources)
+        if (strcmp(source->name, name) == 0)
+            return source;
+
+    // not found
+    return NULL;
+}
+
 /**
  * Initialize with the given sock
  */
-err_t logwatch_source_init (struct logwatch_source *source, struct logwatch *ctx, struct sock_stream *stream, struct error_info *err)
+err_t logwatch_source_init (struct logwatch_source *source, struct logwatch *ctx, const char *name, struct sock_stream *stream, struct error_info *err)
 {
+    // duplicate name?
+    if (logwatch_source_lookup(ctx, name))
+        return SET_ERROR(err, ERR_DUP_NAME);
+
     // store
     source->ctx = ctx;
     
+    // the name
+    if ((source->name = strdup(name)) == NULL)
+        return SET_ERROR(err, ERR_STRDUP);
+    
     // create the lp to wrap the sock
     if (line_proto_create(&source->lp, stream, LOGWATCH_SOURCE_LINE_MAX, &lp_callbacks, source, err))
         goto error;
@@ -62,7 +87,7 @@
         goto error;
         
     // init
-    if (logwatch_source_init(source, ctx, stream, err))
+    if (logwatch_source_init(source, ctx, path, stream, err))
         goto error;
 
     // ok
@@ -88,5 +113,10 @@
     // remove from the list
     TAILQ_REMOVE(&source->ctx->sources, source, logwatch_sources);
 
+    // remove any affected filters
+    logwatch_filter_remove(source->ctx, source);
+    
+    // release
+    free(source->name);
     free(source);
 }
--- a/src/str.c	Sat Apr 11 06:03:49 2009 +0300
+++ b/src/str.c	Sun Apr 12 17:18:06 2009 +0300
@@ -245,7 +245,6 @@
                 *name_ptr = *flags_ptr = '\0';
                 
                 break;
-            
 
             case '}':
                 // syntax
@@ -280,13 +279,13 @@
                     if (!flags_size)
                         return ERR_STR_FMT_FLAGS_LEN;
                
-
                 } else if (in_param) {
                     // add to param name
                     name_ptr += str_advance(NULL, &name_size, str_append_char(name_ptr, name_size, *format));
 
                     if (!name_size)
                         return ERR_STR_FMT_NAME_LEN;
+
                 } else {
                     // add to output
                     buf += str_advance(NULL, &buf_size, str_append_char(buf, buf_size, *format));