summaryrefslogtreecommitdiff
path: root/module
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-03-17 17:27:28 +0000
committerStef Walter <stef@memberwebs.com>2006-03-17 17:27:28 +0000
commit0fe9ee8d6a212e5b676f327b727c9d404edc7781 (patch)
tree04d82f477cb92f86db1d38f8e92c4597091ca2c0 /module
parent30702442c8a4b0ce65a7ce05166004de6325911d (diff)
Move src/ to module/ subdir
Diffstat (limited to 'module')
-rw-r--r--module/Makefile.am16
-rw-r--r--module/bsnmp-regex.c1025
-rw-r--r--module/regex-tree.def20
-rw-r--r--module/usuals.h32
4 files changed, 1093 insertions, 0 deletions
diff --git a/module/Makefile.am b/module/Makefile.am
new file mode 100644
index 0000000..6f10233
--- /dev/null
+++ b/module/Makefile.am
@@ -0,0 +1,16 @@
+
+INCLUDES = -DCONF_PREFIX=\"$(sysconfdir)\"
+
+moduledir = $(prefix)/lib
+module_LTLIBRARIES = snmp_regex.la
+
+snmp_regex_la_LDFLAGS = -module
+snmp_regex_la_SOURCES = regex_tree.c regex_tree.h regex_oid.h \
+ bsnmp-regex.c
+
+regex_tree.c: regex-tree.def
+ gensnmptree -p regex_ < regex-tree.def
+ gensnmptree -e regexData > regex_oid.h < regex-tree.def
+
+CLEANFILES = regex_tree.* \
+ regex_oid.h
diff --git a/module/bsnmp-regex.c b/module/bsnmp-regex.c
new file mode 100644
index 0000000..98a94a9
--- /dev/null
+++ b/module/bsnmp-regex.c
@@ -0,0 +1,1025 @@
+
+#include "usuals.h"
+
+#include <sys/queue.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <bsnmp/snmpmod.h>
+
+#ifdef WITH_PCRE
+#include <pcre.h>
+#else
+#include <regex.h>
+#endif
+
+#include "regex_tree.h"
+#include "regex_oid.h"
+
+#define DEFAULT_CONFIG CONF_PREFIX "/rrdbot"
+#define DEFAULT_FIFO "/var/run/snmp-regex.fifo"
+
+/* our module handle */
+static struct lmodule *module;
+
+/* OIDs */
+static const struct asn_oid oid_regex = OIDX_regexData;
+
+/* the Object Resource registration index */
+static u_int reg_index = 0;
+
+/* 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;
+
+#ifdef WITH_PCRE
+ pcre *regex;
+ pcre_extra *extra;
+#else
+ regex_t regex;
+#endif
+ char *result;
+
+ uint64_t last_update;
+ uint64_t value;
+};
+
+TAILQ_HEAD(data_entry_list, data_entry);
+
+/* list of peers */
+static struct data_entry_list entries = TAILQ_HEAD_INITIALIZER(entries);
+static uint32_t entry_count = 0;
+
+
+/* configuration */
+static u_char *regex_config = NULL;
+static char *config_memory = NULL;
+
+/* The FIFO log */
+static u_char *regex_fifo = NULL;
+static int fifo_fd = -1;
+static void *fifo_sel = NULL;
+
+/* Buffer for parsing logs */
+#define LINE_LENGTH 1024
+static char line_buffer[LINE_LENGTH];
+
+/* -----------------------------------------------------------------------------
+ * 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;
+}
+
+static uint64_t
+getcurrticks ()
+{
+ struct timeval tp;
+ uint64_t t;
+
+ /* Update tick count */
+ if (gettimeofday (&tp, NULL) < 0) {
+ emsg ("couldn't get current time: %s", strerror (errno));
+ return 0;
+ }
+
+ t = (((uint64_t)tp.tv_sec) * 100) + (((uint64_t)tp.tv_usec) / 10000);
+ return t;
+}
+
+#ifndef WITH_PCRE
+
+static const char*
+regex_msg (int code)
+{
+ ASSERT (code != 0);
+
+ switch (code) {
+ case REG_NOMATCH:
+ return "The regexec() function failed to match";
+ case REG_BADPAT:
+ return "invalid regular expression";
+ case REG_ECOLLATE:
+ return "invalid collating element";
+ case REG_ECTYPE:
+ return "invalid character class";
+ case REG_EESCAPE:
+ return "'\' applied to unescapable character";
+ case REG_ESUBREG:
+ return "invalid backreference number";
+ case REG_EBRACK:
+ return "brackets '[ ]' not balanced";
+ case REG_EPAREN:
+ return "parentheses '( )' not balanced";
+ case REG_EBRACE:
+ return "braces '{ }' not balanced";
+ case REG_BADBR:
+ return "invalid repetition count(s) in '{ }'";
+ case REG_ERANGE:
+ return "invalid character range in '[ ]'";
+ case REG_ESPACE:
+ return "ran out of memory";
+ case REG_BADRPT:
+ return "'?', '*', or '+' operand invalid";
+ case REG_EMPTY:
+ return "empty (sub)expression";
+ case REG_ILLSEQ:
+ return "illegal byte sequence (bad multibyte character)";
+
+ case REG_ASSERT:
+ case REG_INVARG:
+ default:
+ return "internal or unknown error";
+ }
+}
+
+#endif /* WITH_PCRE */
+
+/* -----------------------------------------------------------------------------
+ * MATCHING
+ */
+
+static int
+process_match (struct data_entry *data, char *result)
+{
+ char *t;
+ int i;
+
+ switch(data->type) {
+ case TYPE_COUNTER:
+ data->value++;
+ break;
+ case TYPE_INTEGER:
+ if (!result) {
+ emsg ("match, but no result data for '%s'", data->descr);
+ return -1;
+ }
+
+ i = strtol (result, &t, 10);
+ if (*t) {
+ emsg ("invalid integer for '%s': %s", data->descr, result);
+ return -1;
+ }
+
+ data->value = i;
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ data->last_update = getcurrticks ();
+
+ /*
+ fprintf(stderr, "updated '%s' to value '%lld' at '%lld'\n",
+ data->descr, data->value, data->last_update);
+ */
+
+ return 0;
+}
+
+static char*
+process_result (struct data_entry *data, char *line, int len,
+#ifdef WITH_PCRE
+ int *ovector, int substrings)
+#else
+ regmatch_t *pm)
+#endif
+{
+ char *result;
+ char *p, *t;
+ int rlen, l;
+ int idx;
+
+ /* Some nice little optimizations */
+ if (!data->result)
+ return NULL;
+ if (strchr (data->result, '\\') == NULL)
+ return strdup (data->result);
+
+ /* Figure out the string length */
+ rlen = strlen (data->result) + 1;
+ for (p = data->result; *p; p++) {
+ if(p[0] == '\\') {
+ p++;
+ if(p[0] == '\\' || !isdigit(p[0]))
+ continue;
+ idx = p[0] - '0';
+ ASSERT (idx >= 0 && idx <= 9);
+#ifdef WITH_PCRE
+ if (idx < substrings) {
+ ASSERT (ovector[(idx * 2) + 1] >= ovector[idx * 2]);
+ ASSERT (ovector[(idx * 2) + 1] < len);
+ ASSERT (ovector[idx * 2] < len);
+ rlen += (ovector[(idx * 2) + 1]) - (ovector[idx * 2]);
+ rlen += 1;
+ }
+#else /* WITH_PCRE */
+ if (pm[idx].rm_so != -1 && pm[idx].rm_eo != -1) {
+ ASSERT (pm[idx].rm_eo >= pm[idx].rm_so);
+ ASSERT (pm[idx].rm_eo < len);
+ ASSERT (pm[idx].rm_so < len);
+ rlen += (pm[idx].rm_eo - pm[idx].rm_so);
+ rlen += 1;
+ }
+#endif /* WITH_PCRE */
+ }
+ }
+
+ result = (char*)calloc(rlen, 1);
+ if (!result) {
+ emsg ("out of memory");
+ return NULL;
+ }
+
+ for (p = data->result, t = result; *p; p++) {
+ if (p[0] == '\\') {
+ p++;
+ if (isdigit(p[0])) {
+ idx = p[0] - '0';
+#ifdef WITH_PCRE
+ if (idx < substrings) {
+ l = (ovector[(idx * 2) + 1]) - (ovector[idx * 2]);
+ memcpy (t, line + (ovector[idx * 2]), l);
+ t += l;
+ }
+#else /* WITH_PCRE */
+ if (pm[idx].rm_so != -1 && pm[idx].rm_eo != -1) {
+ l = pm[idx].rm_eo - pm[idx].rm_so;
+ memcpy (t, line + pm[idx].rm_so, l);
+ t += l;
+ }
+#endif /* WITH _PCRE */
+
+ continue;
+ }
+ }
+
+ *t++ = *p;
+ }
+
+ *t = 0;
+ return result;
+}
+
+static void
+process_log (char *line, int len)
+{
+ struct data_entry *data;
+ char *result;
+ int r;
+
+#ifdef WITH_PCRE
+ int ovector[30];
+#else
+ regmatch_t pm[10];
+#endif
+
+ for (data = TAILQ_FIRST (&entries); data;
+ data = TAILQ_NEXT (data, link)) {
+
+ result = NULL;
+
+#ifdef WITH_PCRE
+ memset (ovector, 0, sizeof (ovector));
+ r = pcre_exec (data->regex, data->extra, line, len, 0,
+ PCRE_NOTEMPTY | PCRE_NO_UTF8_CHECK, ovector, 30);
+
+ if (r == PCRE_ERROR_NOMATCH)
+ continue;
+ else if (r == PCRE_ERROR_NOMEMORY) {
+ emsg ("out of memory");
+ return;
+ } else if (r < 0) {
+ emsg ("internal error in matching code: %d", r);
+ return;
+ }
+
+ result = process_result (data, line, len, ovector, r);
+
+#else
+ r = regexec (&(data->regex), line, 10, pm, 0);
+ if (r == REG_NOMATCH)
+ continue;
+ else if (r != 0) {
+ emsg ("internal error in matching code: %d", r);
+ return;
+ }
+
+ result = process_result (data, line, len, pm);
+#endif
+
+ process_match (data, result);
+ free (result);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * LOG READING
+ */
+
+static void
+close_fifo ()
+{
+ if (fifo_sel) {
+ fd_deselect (fifo_sel);
+ fifo_sel = NULL;
+ }
+
+ if (fifo_fd != -1) {
+ close (fifo_fd);
+ fifo_fd = -1;
+ }
+
+ memset (line_buffer, 0, sizeof (line_buffer));
+}
+
+static void
+receive_log (int fd, void *data)
+{
+ char *t;
+ int len;
+ int r, l;
+
+ ASSERT (fd == fifo_fd);
+
+ len = strlen (line_buffer);
+ ASSERT (len < LINE_LENGTH);
+
+ do {
+ r = read (fd, line_buffer + len, (LINE_LENGTH - 1) - len);
+
+ if (r < 0 && errno != EAGAIN) {
+ emsg ("couldn't read from fifo: %s: %s", regex_fifo, strerror (errno));
+ return;
+
+ /* Got data, null terminate */
+ } else if (r > 0) {
+ len += r;
+ ASSERT (len < LINE_LENGTH);
+ line_buffer[len] = 0;
+ }
+
+ /* Break really long lines */
+ if (len >= LINE_LENGTH - 1)
+ line_buffer[len - 1] = '\n';
+
+ for (;;) {
+ t = strchr (line_buffer, '\n');
+ if (t == NULL)
+ break;
+
+ /* Break line (also DOS line) */
+ *t = 0;
+ if (line_buffer != t && *(t - 1) == '\r')
+ *(t - 1) = 0;
+ l = (t + 1) - line_buffer;
+
+ /* Send it off */
+ process_log (line_buffer, l);
+
+ /* Move data to front of buffer */
+ ASSERT (l <= len);
+ memmove (line_buffer, t + 1, (len - l) + 1);
+ len -= l;
+ }
+
+ } while (r > 0);
+}
+
+static int
+open_fifo ()
+{
+ struct stat sb;
+
+ close_fifo ();
+
+ ASSERT (regex_fifo && regex_fifo[0]);
+
+ if (stat (regex_fifo, &sb) == 0) {
+ /* Complain if it's not a FIFO */
+ if (!S_ISFIFO (sb.st_mode)) {
+ emsg ("file or directory already exists: %s", regex_fifo);
+ return -1;
+ }
+
+ } else if (errno == ENOENT) {
+ /* Try and create it */
+ if (mkfifo (regex_fifo, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ emsg ("couldn't create fifo: %s: %s", regex_fifo, strerror (errno));
+ return -1;
+ }
+
+ } else {
+ emsg ("couldn't access fifo: %s: %s", regex_fifo, strerror (errno));
+ return -1;
+ }
+
+ fifo_fd = open (regex_fifo, O_RDONLY | O_NONBLOCK);
+ if (fifo_fd < 0) {
+ emsg ("couldn't open fifo: %s: %s", regex_fifo, strerror (errno));
+ return -1;
+ }
+
+ fifo_sel = fd_select (fifo_fd, receive_log, NULL, module);
+ if (!fifo_sel) {
+ emsg ("couldn't listen on fifo: %s: %s", regex_fifo, strerror (errno));
+ return -1;
+ }
+
+ memset (line_buffer, 0, sizeof (line_buffer));
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * CONFIG PARSING
+ */
+
+static void
+config_free (struct data_entry *data)
+{
+#ifdef WITH_PCRE
+ if (data->regex)
+ free (data->regex);
+ if (data->extra)
+ free (data->extra);
+#else
+ regfree (&(data->regex));
+#endif
+
+ 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
+config_entry (struct data_entry *data, char *name, char type, char *regex,
+ char *result, char *flags, int line)
+{
+#ifdef WITH_PCRE
+ const char *errptr;
+ int erroffset;
+ int options = 0;
+#else
+ int options = REG_EXTENDED;
+ int r;
+#endif
+
+ 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;
+ }
+
+ /* Parse any options we have */
+ for (; *flags; flags++) {
+ switch (flags[0]) {
+ case 'i':
+#ifdef WITH_PCRE
+ options |= PCRE_CASELESS;
+#else
+ options |= REG_ICASE;
+#endif
+ break;
+ default:
+ emsg ("[line %d] invalid flag: %c", line, flags[0]);
+ return -1;
+ }
+ }
+
+ /* The name */
+ data->descr = name;
+
+ /* Parse the regex */
+#ifdef WITH_PCRE
+ 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);
+#else
+ r = regcomp (&(data->regex), regex, options);
+ if (r != 0) {
+ emsg ("[line %d] invalid regular expression: %s", line, regex_msg (r));
+ return -1;
+ }
+#endif
+
+ /* 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;
+}
+
+static int
+config_line (char *name, char *value, int line)
+{
+ 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;
+}
+
+static int
+config_read ()
+{
+ FILE *f = NULL;
+ long len;
+
+ ASSERT (regex_config && regex_config[0]);
+
+ f = fopen (regex_config, "r");
+ if (f == NULL) {
+ emsg ("couldn't open config file: %s", regex_config);
+ 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", regex_config);
+ 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", regex_config);
+ 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;
+}
+
+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
+ */
+
+int
+op_regexconfig (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];
+ int r = SNMP_ERR_NOERROR;
+
+ switch (which) {
+ case LEAF_regexConfig:
+
+ if (op == SNMP_OP_GET)
+ return string_get (value, regex_config, -1);
+
+ /* Remainder only at initialization */
+ if (community != COMM_INITIALIZE)
+ return SNMP_ERR_NOT_WRITEABLE;
+
+ switch (op) {
+ case SNMP_OP_SET:
+ if ((r = string_save (value, ctx, -1, &regex_config)) == SNMP_ERR_NOERROR) {
+ if (!regex_config[0])
+ r = SNMP_ERR_WRONG_VALUE;
+ else if (config_parse () < 0)
+ r = SNMP_ERR_GENERR;
+ }
+ if (r != SNMP_ERR_NOERROR)
+ string_rollback (ctx, &regex_config);
+ break;
+ case SNMP_OP_COMMIT:
+ string_commit (ctx);
+ break;
+ case SNMP_OP_ROLLBACK:
+ string_rollback (ctx, &regex_config);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return r;
+
+
+ case LEAF_regexFifo:
+
+ if (op == SNMP_OP_GET)
+ return string_get (value, regex_fifo, -1);
+
+ /* Remainder only at initialization */
+ if (community != COMM_INITIALIZE)
+ return SNMP_ERR_NOT_WRITEABLE;
+
+ switch (op) {
+ case SNMP_OP_SET:
+ if ((r = string_save (value, ctx, -1, &regex_fifo)) == SNMP_ERR_NOERROR) {
+ if (!regex_fifo[0])
+ r = SNMP_ERR_WRONG_VALUE;
+ else if (open_fifo () < 0)
+ r = SNMP_ERR_GENERR;
+ }
+ if (r != SNMP_ERR_NOERROR)
+ string_rollback (ctx, &regex_config);
+ break;
+ case SNMP_OP_COMMIT:
+ string_commit (ctx);
+ break;
+ case SNMP_OP_ROLLBACK:
+ string_rollback (ctx, &regex_fifo);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return r;
+ }
+
+ ASSERT(0);
+ return -1;
+}
+
+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];
+
+ switch (op) {
+ case SNMP_OP_GET:
+ break;
+
+ 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_regexCount:
+ value->v.integer = entry_count;
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+int
+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];
+ struct data_entry *data;
+ uint64_t ticks;
+
+ switch (op) {
+ case SNMP_OP_GETNEXT:
+ data = NEXT_OBJECT_INT (&entries, &value->var, sub);
+ if (data == NULL)
+ return SNMP_ERR_NOSUCHNAME;
+ value->var.len = sub + 1;
+ value->var.subs[sub] = data->index;
+ break;
+
+ case SNMP_OP_GET:
+ data = FIND_OBJECT_INT (&entries, &value->var, sub);
+ if (data == NULL)
+ return SNMP_ERR_NOSUCHNAME;
+ break;
+
+ case SNMP_OP_SET:
+ if (index_decode (&value->var, sub, iidx, &data))
+ return SNMP_ERR_NO_CREATION;
+ data = FIND_OBJECT_INT (&entries, &value->var, sub);
+ if (data != NULL)
+ return SNMP_ERR_NOT_WRITEABLE;
+ return SNMP_ERR_NO_CREATION;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ switch (which) {
+ case LEAF_regexIndex:
+ value->v.integer = data->index;
+ break;
+
+ case LEAF_regexDescr:
+ return (string_get (value, data->descr, -1));
+
+ case LEAF_regexLast:
+ if (data->last_update) {
+ ticks = getcurrticks ();
+ if (ticks == 0)
+ return SNMP_ERR_GENERR;
+ value->v.uint32 = (ticks - data->last_update);
+ } else {
+ value->v.uint32 = 0;
+ }
+ break;
+
+ case LEAF_regexValue:
+ value->v.uint32 = data->value;
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ };
+
+ 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;
+ }
+
+ regex_config = strdup (DEFAULT_CONFIG);
+ if (!regex_config)
+ return ENOMEM;
+
+ regex_fifo = strdup (DEFAULT_FIFO);
+ if (!regex_fifo) {
+ free (regex_config);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Module is started */
+static void
+module_start (void)
+{
+ reg_index = or_register (&oid_regex, "The MIB for regex data.", module);
+
+ /* Initial reads */
+ receive_log (fifo_fd, NULL);
+}
+
+/* Called, when the module is to be unloaded after it was successfully loaded */
+static int
+module_fini (void)
+{
+ if (reg_index)
+ or_unregister (reg_index);
+
+ ASSERT (regex_config);
+ free (regex_config);
+
+ ASSERT (regex_fifo);
+ free (regex_fifo);
+
+ close_fifo ();
+ config_free_all ();
+
+ 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/module/regex-tree.def b/module/regex-tree.def
new file mode 100644
index 0000000..3305ada
--- /dev/null
+++ b/module/regex-tree.def
@@ -0,0 +1,20 @@
+(1 internet
+ (4 private
+ (1 enterprises
+ (10277 regexData
+
+ # Valid only during configuration
+ (0 regexConfig OCTETSTRING op_regexconfig GET SET)
+ (1 regexFifo OCTETSTRING op_regexconfig GET SET)
+
+ (10 regexCount INTEGER op_regex GET)
+ (11 regexEntry : INTEGER op_regexentry
+ (1 regexIndex INTEGER GET)
+ (2 regexDescr OCTETSTRING GET)
+ (3 regexLast TIMETICKS GET)
+ (4 regexValue INTEGER GET)
+ )
+ )
+ )
+ )
+)
diff --git a/module/usuals.h b/module/usuals.h
new file mode 100644
index 0000000..6dc7b08
--- /dev/null
+++ b/module/usuals.h
@@ -0,0 +1,32 @@
+
+#ifndef __USUALS_H__
+#define __USUALS_H__
+
+#include <sys/types.h>
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define countof(x) (sizeof(x) / sizeof(x[0]))
+
+#ifdef _DEBUG
+ #include "assert.h"
+ #define ASSERT(x) assert(x)
+#else
+ #define ASSERT(x)
+#endif
+
+#endif /* __USUALS_H__ */