# HG changeset patch # User Tero Marttila # Date 1239146730 -10800 # Node ID 4682ebbc564472a2401746d652df497ba9907289 # Parent 576bab0a1c5aec9827075ebe30fca7a67c39ba12 implement logwatch_conf_filter such that it compiles and loads, but not yet tested diff -r 576bab0a1c5a -r 4682ebbc5644 cmake/Modules/FindPCRE.cmake --- /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) diff -r 576bab0a1c5a -r 4682ebbc5644 src/CMakeLists.txt --- 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 diff -r 576bab0a1c5a -r 4682ebbc5644 src/config.c --- 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; +} + diff -r 576bab0a1c5a -r 4682ebbc5644 src/config.h --- 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 diff -r 576bab0a1c5a -r 4682ebbc5644 src/error.c --- 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 diff -r 576bab0a1c5a -r 4682ebbc5644 src/error.h --- 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: + /** General errors */ _ERR_GENERAL = 0xffff00, ERR_CMD_OPT, diff -r 576bab0a1c5a -r 4682ebbc5644 src/modules/irc_log.c --- 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, "[= [...]]", "set database connection info, see libpq docs" ), - CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_channel, "", "log the given channel" ), - - CONFIG_OPT_END -}; + CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_channel, "", "log the given channel" ) +); /** * Deinitialize, logging CLOSE events for all channels, and removing any hooks we've added diff -r 576bab0a1c5a -r 4682ebbc5644 src/modules/logwatch.c --- 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, "", "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, "", "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. diff -r 576bab0a1c5a -r 4682ebbc5644 src/modules/logwatch.h --- 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); + diff -r 576bab0a1c5a -r 4682ebbc5644 src/modules/logwatch_filter.c --- /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 +#include + +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); +}