summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.in14
-rw-r--r--src/Makefile.am1
-rw-r--r--src/bsnmp-regex.c412
-rw-r--r--src/regex-tree.def16
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 <sys/select.h>
#include <syslog.h>
#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
#include <bsnmp/snmpmod.h>
+#include <pcre.h>
#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)
)
)
)