aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-02-17 15:19:58 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2012-02-17 15:29:01 +0200
commit73b4e38d693e80c8e2f8c8f990e063c9e919894a (patch)
tree6c3bb78aa39d030a7a680c179edb6bc81efbab4f
parentcc8e335a68ab8232837515ba3f91b42f7dc7b9e9 (diff)
downloadslb-73b4e38d693e80c8e2f8c8f990e063c9e919894a.tar.gz
slb-73b4e38d693e80c8e2f8c8f990e063c9e919894a.tar.bz2
Extend the functionality of assertions.
* NEWS: Update. * doc/config.texi: Update. * src/Makefile.am (slb_SOURCES): Add wildmat.c. * src/wildmat.c: New file. * src/config.c (cb_server_assert): Take a single string as argument, optionally allowing three distinct arguments as a kind of syntax sugar. Support various string and arithmetical comparisons. * src/slb.h (SLB_ASSERT_NEG,SLB_ASSERT_ICASE): New flags. (SLB_ASSERT_EQ,SLB_ASSERT_PREFIX,SLB_ASSERT_SUFFIX) (SLB_ASSERT_GLOB,SLB_ASSERT_EQUAL,SLB_ASSERT_LT) (SLB_ASSERT_LE): New opcodes. (slb_assertion) <opcode,vallen>: New members. (wildmatch): New proto. (SLB_ASSERT_NEG,SLB_ASSERT_ICASE): New flags. (SLB_ASSERT_EQ,SLB_ASSERT_PREFIX,SLB_ASSERT_SUFFIX) (SLB_ASSERT_GLOB,SLB_ASSERT_EQUAL,SLB_ASSERT_LT) (SLB_ASSERT_LE): New opcodes. (slb_assertion) <opcode,vallen>: New members. (wildmatch): New proto. * src/snmploop.c (assertion_test): Handle new opcodes. Revert the meaning of the return code. All uses updated.
-rw-r--r--NEWS24
-rw-r--r--doc/config.texi24
-rw-r--r--src/Makefile.am3
-rw-r--r--src/config.c162
-rw-r--r--src/slb.h20
-rw-r--r--src/snmploop.c37
-rw-r--r--src/wildmat.c132
7 files changed, 357 insertions, 45 deletions
diff --git a/NEWS b/NEWS
index b8b3cd0..60fefa6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-SLB NEWS -- history of user-visible changes. 2012-02-16
+SLB NEWS -- history of user-visible changes. 2012-02-17
Copyright (C) 2011, 2012 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -30,7 +30,29 @@ the value "eth1". For example, if the SNMP tree has the following MIB
IF-MIB::ifDescr.10: eth0
then the expression "$iftable[eth]" yields "10"
+
+* Assertion syntax changed.
+
+The assertion statement takes a single argument, which must be a
+string consisting of the following three parts:
+
+ <oid: string> [!]<opcode>[/i] <value: string>
+
+The <opcode> part can be either an arithmetical operator (=, <, <=, >,
+>=), or any of the following string operators:
+
+ eq string equality
+ ne string inequality
+ prefix oid value must begin with <value>
+ suffix oid value must end with <value>
+ glob <value> is a glob(7) pattern that oid value must match
+Each of these can be suffixed with "/i" to request case-insensitive comparison.
+
+A "!" in front of opcode reverts its meaning.
+
+The <value> part must not include the type prefix.
+
Version 1.0, 2011-04-26
diff --git a/doc/config.texi b/doc/config.texi
index e39819f..12c9525 100644
--- a/doc/config.texi
+++ b/doc/config.texi
@@ -1091,9 +1091,10 @@ Defines a macro @var{name} to expand to @var{expansion}. Macros allow
to customize output formats on a per-server basis. @xref{macro}.
@end deffn
-@deffn {Config: server} assert oid op pattern
+@deffn {Config: server} assert "oid op pattern"
Ensures that the value of SNMP variable @var{oid} matches
-@var{pattern}. The type of match is given by the @var{op} argument:
+@var{pattern}. The type of match is given by the @var{op} part.
+The following @var{op} values indicate string comparisons:
@table @asis
@item eq
@@ -1101,8 +1102,25 @@ The value must match @var{pattern} exactly.
@item ne
The value must not match @var{pattern}.
+
+@item prefix
+The value must begin with @var{pattern}.
+
+@item suffix
+The value must end with @var{pattern}.
+
+@item glob
+The @var{pattern} is a glob(7) pattern which the value must match.
@end table
+These values can be suffixed with @samp{/i} to indicate
+case-insensitive comparison.
+
+A @samp{!} appearing before @var{op} reverses its meaning.
+
+The following @var{op} codes specify numeric comparisons:
+@samp{=}, @samp{!=}, @samp{<}, @samp{<=}, @samp{>}, @samp{>=}.
+
If the assertion fails, the server is excluded from the load table.
Use this statement to ensure that a variable used in the computation
@@ -1112,7 +1130,7 @@ interface 3), it would be wise to ensure that the 3rd row refers to
the interface in question (say @samp{eth1}):
@example
-assert "IF-MIB::ifDescr.3" eq "eth1";
+assert "IF-MIB::ifDescr.3 eq eth1";
@end example
See also @ref{table, table slices}, for a more elegant way of
diff --git a/src/Makefile.am b/src/Makefile.am
index 827270c..0c38f64 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,7 +28,8 @@ slb_SOURCES =\
slb.h\
test.c\
snmploop.c\
- symimp.c
+ symimp.c\
+ wildmat.c
BUILT_SOURCES=cmdline.h
EXTRA_DIST=cmdline.opt expr.output
diff --git a/src/config.c b/src/config.c
index b7566b5..93683f0 100644
--- a/src/config.c
+++ b/src/config.c
@@ -381,36 +381,71 @@ cb_server_constant(enum grecs_callback_command cmd,
return 0;
}
-int
-cb_server_assert(enum grecs_callback_command cmd,
- grecs_locus_t *locus,
- void *varptr,
- grecs_value_t *value,
- void *cb_data)
+struct asrtop {
+ char *id;
+ int opcode;
+ int flags;
+};
+
+static struct asrtop asrtop[] = {
+ { "eq", SLB_ASSERT_EQ, 0 },
+ { "eq/i", SLB_ASSERT_EQ, SLB_ASSERT_ICASE },
+ { "ne", SLB_ASSERT_EQ, SLB_ASSERT_NEG },
+ { "ne/i", SLB_ASSERT_EQ, SLB_ASSERT_NEG|SLB_ASSERT_ICASE },
+ { "prefix", SLB_ASSERT_PREFIX, 0 },
+ { "prefix/i", SLB_ASSERT_PREFIX, SLB_ASSERT_ICASE },
+ { "suffix", SLB_ASSERT_SUFFIX, 0 },
+ { "suffix/i", SLB_ASSERT_SUFFIX, SLB_ASSERT_ICASE },
+ { "glob", SLB_ASSERT_GLOB, 0 },
+ { "glob/i", SLB_ASSERT_GLOB, SLB_ASSERT_ICASE },
+ { "=", SLB_ASSERT_EQUAL },
+ { "<=", SLB_ASSERT_LE },
+ { "<", SLB_ASSERT_LT },
+ { ">=", SLB_ASSERT_LT, SLB_ASSERT_NEG },
+ { ">", SLB_ASSERT_LE, SLB_ASSERT_NEG },
+ { NULL }
+};
+
+
+static struct asrtop *
+find_asrtop(char *s)
+{
+ struct asrtop *op;
+ for (op = asrtop; op->id; op++)
+ if (strcmp(s, op->id) == 0)
+ return op;
+ return NULL;
+}
+
+static int
+parse_assertion(char **argv, struct grecs_symtab *at, grecs_locus_t *locus)
{
- struct grecs_symtab *at = *(struct grecs_symtab **)varptr;
struct slb_assertion *ap;
int install;
- int flags;
+ int flags = 0;
+ int opcode;
oid oid[MAX_OID_LEN];
size_t oidlen;
size_t len;
+ char *s;
+ struct asrtop *op;
- if (assert_n_strings(locus, cmd, value, 3, 3))
- return 1;
-
- if (strcmp(value->v.arg.v[1]->v.string, "eq") == 0)
- flags = 0;
- else if (strcmp(value->v.arg.v[1]->v.string, "ne") == 0)
- flags = SLB_ASSERT_NE;
- else {
- grecs_error(locus, 0, _("invalid operation: %s"),
- value->v.arg.v[1]->v.string);
+ s = argv[1];
+ if (*s == '!') {
+ ++s;
+ flags |= SLB_ASSERT_NEG;
+ }
+
+ op = find_asrtop(s);
+ if (!op) {
+ grecs_error(locus, 0, _("invalid operation: %s"), s);
return 1;
}
+ opcode = op->opcode;
+ flags ^= op->flags;
oidlen = MAX_OID_LEN;
- if (!read_objid(value->v.arg.v[0]->v.string, oid, &oidlen)) {
+ if (!read_objid(argv[0], oid, &oidlen)) {
grecs_error(locus, 0, _("cannot parse oid"));
return 1;
}
@@ -418,27 +453,74 @@ cb_server_assert(enum grecs_callback_command cmd,
install = 1;
ap = assertion_lookup(at, oid, oidlen, &install);
if (!install)
- grecs_error(locus, 0,
- _("redefinition of %s"),
- value->v.arg.v[0]->v.string);
+ grecs_error(locus, 0, _("redefinition of %s"), argv);
- len = strlen(value->v.arg.v[0]->v.string) + 1 +
- strlen(value->v.arg.v[1]->v.string) + 1 +
- strlen(value->v.arg.v[2]->v.string) + 1;
+ ap->opcode = opcode;
+ ap->flags = flags;
+ ap->value = grecs_strdup(argv[2]);
+ ap->vallen = strlen(ap->value);
+ clone_locus(&ap->locus, locus);
+
+ len = strlen(argv[0]) + 1 +
+ strlen(argv[1]) + 1 +
+ strlen(argv[2]) + 1;
ap->text = grecs_malloc(len);
- strcpy(ap->text, value->v.arg.v[0]->v.string);
+ strcpy(ap->text, argv[0]);
strcat(ap->text, " ");
- strcat(ap->text, value->v.arg.v[1]->v.string);
+ strcat(ap->text, argv[1]);
strcat(ap->text, " ");
- strcat(ap->text, value->v.arg.v[2]->v.string);
-
- ap->value = grecs_strdup(value->v.arg.v[2]->v.string);
- ap->flags = flags;
- clone_locus(&ap->locus, locus);
+ strcat(ap->text, argv[2]);
return 0;
}
int
+cb_server_assert(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ struct grecs_symtab *at = *(struct grecs_symtab **)varptr;
+ int rc;
+
+ if (assert_n_strings(locus, cmd, value, 1, 3))
+ return 1;
+
+ if (value->type == GRECS_TYPE_STRING) {
+ struct wordsplit ws;
+
+ if (wordsplit(value->v.string, &ws, WRDSF_DEFFLAGS)) {
+ grecs_error(locus, 0,
+ _("can't split argument: %s"),
+ wordsplit_strerror(&ws));
+ return 1;
+ }
+ if (ws.ws_wordc > 3) {
+ grecs_error(locus, 0,
+ _("too many arguments to assertion"));
+ wordsplit_free(&ws);
+ return 1;
+ } else if (ws.ws_wordc < 3) {
+ grecs_error(locus, 0,
+ _("too few arguments to assertion"));
+ wordsplit_free(&ws);
+ return 1;
+ }
+ rc = parse_assertion(ws.ws_wordv, at, locus);
+ wordsplit_free(&ws);
+ } else {
+ char *argv[4];
+ argv[0] = value->v.arg.v[0]->v.string;
+ argv[1] = value->v.arg.v[1]->v.string;
+ argv[2] = value->v.arg.v[2]->v.string;
+ argv[3] = NULL;
+ rc = parse_assertion(argv, at, locus);
+ }
+
+ return rc;
+}
+
+int
cb_server_macro(enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
@@ -771,8 +853,20 @@ static struct grecs_keyword server_kw[] = {
grecs_type_string, GRECS_MULT,
NULL, offsetof(struct slb_server, macros),
cb_server_macro },
- { "assert", N_("<oid: string> {eq|ne} <value: string>"),
- N_("Ensure that <oid> has the given <value>"),
+ { "assert", N_("<arg: <oid: string> [!]<opcode>[/i] <value: string>>"),
+ N_("Ensure that <oid> matches the given <value> as per <opcode>.\n"
+ "Valid opcodes are:\n"
+ "Arithmetical operations: =, <, <=, >, >=\n"
+ "String operations:\n"
+ " eq string equality\n"
+ " ne string inequality\n"
+ " prefix oid value must begin with <value>\n"
+ " suffix oid value must end with <value>\n"
+ " glob <value> is a glob(7) pattern that oid value must match\n"
+ "Each of these can be suffixed with \"/i\" to request case-insensitive\n"
+ "comparison.\n"
+ "\n"
+ "A \"!\" in front of opcode reverts its meaning."),
grecs_type_string, GRECS_MULT,
NULL, offsetof(struct slb_server, assertions),
cb_server_assert },
diff --git a/src/slb.h b/src/slb.h
index 5e3b04e..b4f49fb 100644
--- a/src/slb.h
+++ b/src/slb.h
@@ -311,13 +311,25 @@ char *slb_format_oid_value(oid *oid, size_t len, struct variable_list *vp,
-#define SLB_ASSERT_NE 0x01
+#define SLB_ASSERT_NEG 0x01
+#define SLB_ASSERT_ICASE 0x02
+
+/* Opcodes */
+#define SLB_ASSERT_EQ 1
+#define SLB_ASSERT_PREFIX 2
+#define SLB_ASSERT_SUFFIX 3
+#define SLB_ASSERT_GLOB 4
+#define SLB_ASSERT_EQUAL 5
+#define SLB_ASSERT_LT 6
+#define SLB_ASSERT_LE 7
struct slb_assertion {
oid name[MAX_OID_LEN];
size_t length;
+ int opcode;
int flags;
char *value;
+ size_t vallen;
char *text;
grecs_locus_t locus;
struct slb_format_cache cache;
@@ -406,8 +418,8 @@ extern int syslog_include_prio;
void logger_setup(void);
void logmsg(int prio, const char *fmt, ...) SLB_PRINTFLIKE(2,3);
-void logmsg_at_locus(int prio, grecs_locus_t const *locus, const char *fmt, ...)
- SLB_PRINTFLIKE(3,4);
+void logmsg_at_locus(int prio, grecs_locus_t const *locus,
+ const char *fmt, ...) SLB_PRINTFLIKE(3,4);
void debug_printf(const char *fmt, ...) SLB_PRINTFLIKE(1,2);
int string_to_syslog_facility(const char *str, int *pfacility);
@@ -438,4 +450,6 @@ void close_fds(fd_set *fdset);
int slb_test(const char *name);
+int wildmatch(char *expr, char *name, int icase);
+
diff --git a/src/snmploop.c b/src/snmploop.c
index 84e30fe..5d1e98e 100644
--- a/src/snmploop.c
+++ b/src/snmploop.c
@@ -134,8 +134,39 @@ assertion_test(struct slb_assertion *ap, struct variable_list *vp)
buf = slb_format_oid_value(ap->name, ap->length, vp, &ap->cache);
if (!buf)
return 1;
- rc = strcmp(ap->value, buf);
- if (ap->flags & SLB_ASSERT_NE)
+ switch (ap->opcode) {
+ case SLB_ASSERT_EQ:
+ rc = ((ap->flags & SLB_ASSERT_ICASE) ? strcasecmp : strcmp)
+ (ap->value, buf)== 0;
+ break;
+ case SLB_ASSERT_PREFIX:
+ rc = strlen(buf) >= ap->vallen &&
+ ((ap->flags & SLB_ASSERT_ICASE) ?
+ strncasecmp : strncmp) (buf, ap->value, ap->vallen)
+ == 0;
+ break;
+ case SLB_ASSERT_SUFFIX:
+ rc = strlen(buf) >= ap->vallen &&
+ ((ap->flags & SLB_ASSERT_ICASE) ?
+ strncasecmp : strncmp)
+ (buf + strlen(buf) - ap->vallen,
+ ap->value, ap->vallen) == 0;
+ break;
+ case SLB_ASSERT_GLOB:
+ rc = wildmatch(ap->value, buf,
+ ap->flags & SLB_ASSERT_ICASE) == 0;
+ break;
+ case SLB_ASSERT_EQUAL:
+ /* FIXME */
+ rc = strtoul(buf, NULL, 10) == strtoul(ap->value, NULL, 10);
+ break;
+ case SLB_ASSERT_LT:
+ rc = strtoul(buf, NULL, 10) < strtoul(ap->value, NULL, 10);
+ break;
+ case SLB_ASSERT_LE:
+ rc = strtoul(buf, NULL, 10) <= strtoul(ap->value, NULL, 10);
+ }
+ if (ap->flags & SLB_ASSERT_NEG)
rc = !rc;
return rc;
}
@@ -153,7 +184,7 @@ process_result(struct slb_server *srv, struct snmp_pdu *pdu)
ap = assertion_lookup(srv->assertions, vp->name,
vp->name_length, NULL);
- if (ap && assertion_test(ap, vp)) {
+ if (ap && !assertion_test(ap, vp)) {
logmsg(LOG_NOTICE,
"%s:%d: %s: assertion \"%s\" failed, got %s",
ap->locus.beg.file, ap->locus.beg.line,
diff --git a/src/wildmat.c b/src/wildmat.c
new file mode 100644
index 0000000..f1b31be
--- /dev/null
+++ b/src/wildmat.c
@@ -0,0 +1,132 @@
+/* This file is part of SLB
+ Copyright (C) 2011, 2012 Sergey Poznyakoff
+
+ SLB is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ SLB is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SLB. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "slb.h"
+#include "wordsplit.h"
+
+#define WILD_FALSE 0
+#define WILD_TRUE 1
+#define WILD_ABORT 2
+
+int
+match_char_class(char **pexpr, char c, int icase)
+{
+ int res;
+ int rc;
+ char *expr = *pexpr;
+
+ if (icase)
+ c = toupper(c);
+
+ expr++;
+ if (*expr == '^') {
+ res = 0;
+ expr++;
+ } else
+ res = 1;
+
+ if (*expr == '-' || *expr == ']')
+ rc = c == *expr++;
+ else
+ rc = !res;
+
+ for (; *expr && *expr != ']'; expr++) {
+ if (rc == res) {
+ if (*expr == '\\' && expr[1] == ']')
+ expr++;
+ } else if (expr[1] == '-') {
+ if (*expr == '\\')
+ rc = *++expr == c;
+ else {
+ if (icase)
+ rc = toupper(*expr) <= c &&
+ c <= toupper(expr[2]);
+ else
+ rc = *expr <= c && c <= expr[2];
+ expr += 2;
+ }
+ } else if (*expr == '\\' && expr[1] == ']')
+ rc = *++expr == c;
+ else if (icase)
+ rc = toupper(*expr) == c;
+ else
+ rc = *expr == c;
+ }
+ *pexpr = *expr ? expr + 1 : expr;
+ return rc == res;
+}
+
+int
+_wild_match(char *expr, char *name, int icase)
+{
+ int c;
+
+ while (expr && *expr) {
+ if (*name == 0 && *expr != '*')
+ return WILD_ABORT;
+ switch (*expr) {
+ case '*':
+ while (*++expr == '*')
+ ;
+ if (*expr == 0)
+ return WILD_TRUE;
+ while (*name) {
+ int res = _wild_match(expr, name++, icase);
+ if (res != WILD_FALSE)
+ return res;
+ }
+ return WILD_ABORT;
+
+ case '?':
+ expr++;
+ if (*name == 0)
+ return WILD_FALSE;
+ name++;
+ break;
+
+ case '[':
+ if (!match_char_class(&expr, *name, icase))
+ return WILD_FALSE;
+ name++;
+ break;
+
+ case '\\':
+ if (expr[1]) {
+ c = *++expr; expr++;
+ if (*name != wordsplit_c_unquote_char(c))
+ return WILD_FALSE;
+ name++;
+ break;
+ }
+ /* fall through */
+ default:
+ if (icase) {
+ if (toupper(*expr) != toupper(*name))
+ return WILD_FALSE;
+ } else if (*expr != *name)
+ return WILD_FALSE;
+ expr++;
+ name++;
+ }
+ }
+ return *name == 0;
+}
+
+int
+wildmatch(char *expr, char *name, int icase)
+{
+ return _wild_match(expr, name, icase) != WILD_TRUE;
+}

Return to:

Send suggestions and report system problems to the System administrator.