From c2b4a3070f2063ab0fd2aa14d5dd247f8566c1e0 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 10 Mar 2006 00:12:58 +0000 Subject: Basic configuration parsing, and regular expression compiling. (untested) --- configure.in | 14 +- src/Makefile.am | 1 + src/bsnmp-regex.c | 412 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/regex-tree.def | 16 ++- 4 files changed, 403 insertions(+), 40 deletions(-) diff --git a/configure.in b/configure.in index fb24345..b4d83d2 100644 --- a/configure.in +++ b/configure.in @@ -5,6 +5,9 @@ AM_INIT_AUTOMAKE(bsnmp-regex, 0.1) AC_CONFIG_SRCDIR([src/bsnmp-regex.c]) AM_CONFIG_HEADER([config.h]) +LDFLAGS="$LDFLAGS -L/usr/local/lib" +CFLAGS="$CFLAGS -I/usr/local/include" + # Checks for programs. AC_PROG_CC AC_PROG_INSTALL @@ -21,13 +24,16 @@ if test "$enable_debug" = "yes"; then echo "enabling debug compile mode" fi -# Some checks for BSNMP libraries +# Some checks for libraries AC_CHECK_LIB(bsnmp, snmp_close, , - [echo "Couldn't find the bsnmp module library"; exit 1]) - + [echo "Couldn't find the bsnmp library"; exit 1]) +AC_CHECK_LIB(pcre, pcre_compile, , + [echo "Couldn't find the pcre library"; exit 1]) + # Checks for header files. AC_HEADER_STDC -dnl AC_CHECK_HEADERS() +AC_CHECK_HEADERS("pcre.h", , + [echo "Couldn't find pcre headers"; exit 1]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/src/Makefile.am b/src/Makefile.am index 9758f1a..330114f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ +INCLUDES = -DCONF_PREFIX=\"$(sysconfdir)\" lib_LTLIBRARIES = libsnmp_regex.la libsnmp_regex_la_LDFLAGS = libsnmp_regex_la_SOURCES = regex_tree.c regex_tree.h regex_oid.h \ diff --git a/src/bsnmp-regex.c b/src/bsnmp-regex.c index dca188a..a2696df 100644 --- a/src/bsnmp-regex.c +++ b/src/bsnmp-regex.c @@ -5,12 +5,17 @@ #include #include #include +#include +#include #include +#include #include "regex_tree.h" #include "regex_oid.h" +#define DEFAULT_CONFIG CONF_PREFIX "/rrdbot" + /* our module handle */ static struct lmodule *module; @@ -20,11 +25,24 @@ static const struct asn_oid oid_regex = OIDX_regexData; /* the Object Resource registration index */ static u_int reg_index; +/* Various match types */ +enum { + TYPE_UNKNOWN = 0, + TYPE_COUNTER, + TYPE_INTEGER +}; + struct data_entry { uint32_t index; TAILQ_ENTRY(data_entry) link; + int type; char *descr; + + pcre *regex; + pcre_extra *extra; + char *result; + uint64_t last_update; uint64_t value; }; @@ -35,48 +53,334 @@ TAILQ_HEAD(data_entry_list, data_entry); static struct data_entry_list entries = TAILQ_HEAD_INITIALIZER(entries); static uint32_t entry_count = 0; -/* the initialisation function */ +/* configuration */ +static const char *config_file = NULL; +static char *config_memory = NULL; + +/* ----------------------------------------------------------------------------- + * HELPERS + */ + +static void +emsg(const char *format, ...) +{ + va_list va; + va_start(va, format); + vsyslog(LOG_ERR, format, va); + va_end(va); +} + +static void +strcln (char* data, char ch) +{ + char* p; + for (p = data; *data; data++, p++) { + while (*data == ch) + data++; + *p = *data; + } + /* null terminate */ + *p = 0; +} + +static void +stretrim (char* data) +{ + char* t = data + strlen (data); + while (t > data && isspace (*(t - 1))) { + t--; + *t = 0; + } +} + +static char* +strbtrim (const char* data) +{ + while (*data && isspace (*data)) + ++data; + return (char*)data; +} + +static char* +strtrim (char* data) +{ + data = (char*)strbtrim (data); + stretrim (data); + return data; +} + +/* ----------------------------------------------------------------------------- + * CONFIG PARSING + */ + +static void +config_free (struct data_entry *data) +{ + if (data->regex) + free (data->regex); + if (data->extra) + free (data->extra); + free (data); +} + +static void +config_free_all () +{ + struct data_entry *data; + while ((data = TAILQ_FIRST(&entries)) != NULL) { + TAILQ_REMOVE (&entries, data, link); + config_free (data); + } +} + static int -module_init(struct lmodule *mod, int argc, char *argv[]) +config_entry (struct data_entry *data, char *name, char type, char *regex, + char *result, char *flags, int line) { - module = mod; + const char *errptr; + int erroffset; + int options = 0; + + ASSERT (regex); + ASSERT (flags); + ASSERT (data); + ASSERT (type); + ASSERT (name); + + /* Figure out the type first */ + switch (type) { + case 'c': + data->type = TYPE_COUNTER; + break; + case 'i': + data->type = TYPE_INTEGER; + break; + case '\0': + return -2; + default: + emsg ("[line %d] invalid or unknown entry type: %c", line, type); + return -1; + } - if (argc != 0) { - syslog(LOG_ERR, "bad number of arguments for %s", __func__); - return (EINVAL); + /* Parse any options we have */ + for (; *flags; flags++) { + switch (flags[0]) { + case 'i': + options |= PCRE_CASELESS; + break; + default: + emsg ("[line %d] invalid flag: %c", line, flags[0]); + return -1; + } } + /* The name */ + data->descr = name; + + /* Parse the regex */ + data->regex = pcre_compile (regex, options, &errptr, &erroffset, NULL); + if (!data->regex) { + emsg ("[line %d] invalid regular expression: %s", line, errptr); + return -1; + } + + /* Optimize the regex, ignore errors */ + data->extra = pcre_study (data->regex, 0, &errptr); + + /* Replacement data */ + if (data->type != TYPE_COUNTER && !result) { + emsg ("[line %d] no match text specified in entry", line); + return -1; + } + data->result = result; + return 0; } -/* Module is started */ -static void -module_start(void) +static int +config_line (char *name, char *value, int line) { - reg_index = or_register(&oid_regex, "The MIB for regex data.", module); + struct data_entry *data; + char *regex = NULL; + char *result = NULL; + char *flags = NULL; + char type; + char delimiter; + char *t; + int r; + + /* config_parse trims this for us */ + ASSERT (!isspace(value[0])); + + /* The type of entry */ + type = value[0]; + ++value; + + /* Next thing is the delimiter */ + if (!*value) + return -2; + delimiter = value[0]; + ++value; + + /* Look for the next delimiter */ + t = strchr (value, delimiter); + if (!t) + return -2; + *t = 0; + regex = value; + value = t + 1; + + /* And the result */ + t = strchr (value, delimiter); + if (t) { + *t = 0; + t++; + result = value; + } + + /* The flags */ + flags = t ? t : value; + + /* Now populate an entry */ + data = (struct data_entry*)calloc(1, sizeof (*data)); + if (!data) { + emsg ("out of memory"); + return -1; + } + + /* Now make an entry out of it all */ + r = config_entry (data, name, type, regex, result, flags, line); + + if (r < 0) { + free (data); + return r; + } + + /* Put it in our table */ + data->index = entry_count++; + INSERT_OBJECT_INT (data, &entries); + + return 0; } -/* Called, when the module is to be unloaded after it was successfully loaded */ static int -module_fini(void) +config_read () { - or_unregister(reg_index); + FILE *f = NULL; + long len; + + if (!config_file) + config_file = DEFAULT_CONFIG; + + f = fopen (config_file, "r"); + if (f == NULL) { + emsg ("couldn't open config file: %s", config_file); + return -1; + } + + /* Figure out size */ + if (fseek (f, 0, SEEK_END) == -1 || (len = ftell (f)) == -1 || fseek (f, 0, SEEK_SET) == -1) { + emsg ("couldn't seek config file: %s", config_file); + return -1; + } + + ASSERT (!config_memory); + + if ((config_memory = (char*)malloc (len + 2)) == NULL) { + emsg ("out of memory"); + return -1; + } + + /* And read in one block */ + if(fread(config_memory, 1, len, f) != len) { + emsg ("couldn't read config file: %s", config_file); + free (config_memory); + config_memory = NULL; + return -1; + } + + fclose (f); + + /* Null terminate the data */ + config_memory[len] = '\n'; + config_memory[len + 1] = 0; + return 0; } -const struct snmp_module config = { - .comment = "This module implements SNMP listing of data from regular expressions", - .init = module_init, - .start = module_start, - .fini = module_fini, - .tree = regex_ctree, - .tree_size = regex_CTREE_SIZE, -}; +static int +config_parse () +{ + char* next; + char* p; + char* t; + int line = 0; + int r; + + config_free_all (); + + /* Reads raw config file into config_memory */ + if (config_read () < 0) + return -1; + + ASSERT (config_memory); + + /* Remove nasty dos line endings */ + strcln(config_memory, '\r'); + + next = config_memory; + while ((t = strchr (next, '\n')) != NULL) { + + *t = 0; + p = next; + next = t + 1; + t = strbtrim (p); + line++; + + /* blank lines, comments */ + if (!*t || *t == '#') + continue; + + t = strchr (t, ':'); + if (t != NULL) { + emsg ("invalid config line: %s", p); + return -1; + } + + *t = 0; + t++; + + /* Pass of the name and value */ + r = config_line (strtrim (p), strtrim (t), line); + if (r < 0) { + + /* If -2 was returned, no error message was printed */ + if (r == -2) + emsg ("[line %d] invalid configuration file", line); + + return -1; + } + } + + return 0; +} + + +/* ----------------------------------------------------------------------------- + * CALLBACKS + */ -/* System variables - read-only scalars only. */ int -op_data (struct snmp_context *ctx, struct snmp_value *value, - u_int sub, u_int iidx, enum snmp_op op) +op_regexconfig (struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx, enum snmp_op op) +{ + fprintf(stderr, "op_regexconfig\n"); + return SNMP_ERR_NOT_WRITEABLE; +} + +int +op_regex (struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; @@ -87,13 +391,17 @@ op_data (struct snmp_context *ctx, struct snmp_value *value, case SNMP_OP_SET: return SNMP_ERR_NOT_WRITEABLE; + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + return SNMP_ERR_NOERROR; + default: ASSERT(0); break; } switch (which) { - case LEAF_dataCount: + case LEAF_regexCount: value->v.integer = entry_count; break; @@ -106,7 +414,7 @@ op_data (struct snmp_context *ctx, struct snmp_value *value, } int -op_dataentry (struct snmp_context *ctx, struct snmp_value *value, +op_regexentry (struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; @@ -141,18 +449,18 @@ op_dataentry (struct snmp_context *ctx, struct snmp_value *value, } switch (which) { - case LEAF_dataIndex: + case LEAF_regexIndex: value->v.integer = data->index; break; - case LEAF_dataDescr: + case LEAF_regexDescr: return (string_get (value, data->descr, -1)); - case LEAF_dataLast: + case LEAF_regexLast: value->v.uint32 = data->last_update; break; - case LEAF_dataValue: + case LEAF_regexValue: value->v.uint32 = data->value; break; @@ -163,3 +471,47 @@ op_dataentry (struct snmp_context *ctx, struct snmp_value *value, return SNMP_ERR_NOERROR; } + +/* ----------------------------------------------------------------------------- + * MODULE + */ + +/* the initialisation function */ +static int +module_init (struct lmodule *mod, int argc, char *argv[]) +{ + module = mod; + + if (argc != 0) { + syslog(LOG_ERR, "bad number of arguments for %s", __func__); + return (EINVAL); + } + + return 0; +} + +/* Module is started */ +static void +module_start (void) +{ + reg_index = or_register(&oid_regex, "The MIB for regex data.", module); +} + +/* Called, when the module is to be unloaded after it was successfully loaded */ +static int +module_fini (void) +{ + or_unregister(reg_index); + return 0; +} + +const struct snmp_module config = { + .comment = "This module implements SNMP listing of data from regular expressions", + .init = module_init, + .start = module_start, + .fini = module_fini, + .tree = regex_ctree, + .tree_size = regex_CTREE_SIZE, +}; + + diff --git a/src/regex-tree.def b/src/regex-tree.def index 8a631b9..23e0508 100644 --- a/src/regex-tree.def +++ b/src/regex-tree.def @@ -2,12 +2,16 @@ (4 private (1 enterprises (10277 regexData - (1 dataCount INTEGER op_data GET) - (2 dataEntry : INTEGER op_dataentry - (1 dataIndex INTEGER GET) - (2 dataDescr OCTETSTRING GET) - (3 dataLast TIMETICKS GET) - (4 dataValue INTEGER GET) + + # Valid only during configuration + (0 regexConfig OCTETSTRING op_regexconfig GET SET) + + (1 regexCount INTEGER op_regex GET) + (2 regexEntry : INTEGER op_regexentry + (1 regexIndex INTEGER GET) + (2 regexDescr OCTETSTRING GET) + (3 regexLast TIMETICKS GET) + (4 regexValue INTEGER GET) ) ) ) -- cgit v1.2.3