#include "logwatch.h"
#include "../str.h"
#include "../log.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
struct logwatch_filter* logwatch_filter_lookup (struct logwatch *ctx, const char *name)
{
struct logwatch_filter *filter;
// inspect each filter
TAILQ_FOREACH(filter, &ctx->filters, logwatch_filters)
if (strcmp(filter->name, name) == 0)
return filter;
// not found
return NULL;
}
struct logwatch_filter* logwatch_filter (struct logwatch *ctx, const char *name, const struct logwatch_source *source,
const char *pattern, const char *format, struct logwatch_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)) == 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);
// mark it as registered
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)
// unreigster
TAILQ_REMOVE(&filter->ctx->filters, filter, logwatch_filters);
// this if is probably not needed
if (filter->regexp)
pcre_free(filter->regexp);
if (filter->chan)
// release the logwatch_chan ref
logwatch_chan_put(filter->chan);
// free misc
free(filter->format);
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
*/
struct logwatch_filter_str_format_ctx {
/** The regexp we are processing */
pcre *regexp;
/** The subject string */
const char *subject;
/** The match group vector */
int *groups;
/** Number of match items in groups */
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;
int num;
// look it up
if (!ctx->regexp || (num = pcre_get_stringnumber(ctx->regexp, name)) < 0)
// no such capture group
return ERR_STR_FMT_NAME;
// sanity check
assert((size_t) num < ctx->group_count);
// look up the start/end offsets
int start = ctx->groups[num * 2 + 0];
int end = ctx->groups[num * 2 + 1];
// return a pointer to the subject
*value = ctx->subject + start;
*value_len = end - start;
// ok
return SUCCESS;
}
// number of pcre ovec elemnents required
#define LOGWATCH_FILTER_OVEC_ITEMS ((LOGWATCH_FILTER_GROUPS_MAX + 1) * 3)
// length of output line prefix
#define LOGWATCH_FILTER_OUT_PREFIX_LEN 16
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;
size_t buf_size = sizeof(buf);
// XXX: what to do for truncated data?
// discard based on source?
if (filter->source && source != filter->source)
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 0;
else
// misc. error
// 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
struct logwatch_filter_str_format_ctx ctx = {
.regexp = filter->regexp,
.subject = line,
.groups = ovec,
.group_count = ret
};
// operate
if ((ERROR_CODE(err) = str_format(buf_ptr, buf_size, filter->format, logwatch_filter_str_format_cb, &ctx)))
goto error;
} else {
// quote the entire line
// XXX: overflow
buf_ptr += str_advance(NULL, &buf_size, str_quote(buf_ptr, buf_size, line, -1));
}
// log
log_info("<%s>:%s -> %s:%s", source->name, line, irc_chan_name(filter->chan->irc_chan), buf);
// send it
if ((ERROR_CODE(err) = logwatch_chan_msg(filter->chan, "[%s] %s", filter->name, buf)))
goto error;
// ok, stop here
return 1;
error:
return -ERROR_CODE(err);
}