2
* DisMan Expression MIB:
3
* Core implementation of expression evaluation
6
#include <net-snmp/net-snmp-config.h>
7
#include <net-snmp/net-snmp-includes.h>
8
#include <net-snmp/agent/net-snmp-agent-includes.h>
9
#include "disman/expr/expExpression.h"
10
#include "disman/expr/expObject.h"
11
#include "disman/expr/expValue.h"
15
void _expValue_setError( struct expExpression *exp, int reason,
16
oid *suffix, size_t suffix_len,
17
netsnmp_variable_list *var);
20
#define ASN_PRIV_OPERATOR (ASN_PRIVATE | 0x0f)
21
#define ASN_PRIV_FUNCTION (ASN_PRIVATE | 0x0e)
23
int ops[128]; /* mapping from operator characters to numeric
24
tokens (ordered by priority). */
29
DEBUGMSGTL(("disman:expr:eval", "Init expValue"));
30
/* Single-character operators */
31
ops['+'] = EXP_OPERATOR_ADD;
32
ops['-'] = EXP_OPERATOR_SUBTRACT;
33
ops['*'] = EXP_OPERATOR_MULTIPLY;
34
ops['/'] = EXP_OPERATOR_DIVIDE;
35
ops['%'] = EXP_OPERATOR_REMAINDER;
36
ops['^'] = EXP_OPERATOR_BITXOR;
37
ops['~'] = EXP_OPERATOR_BITNEGATE;
38
ops['|'] = EXP_OPERATOR_BITOR;
39
ops['&'] = EXP_OPERATOR_BITAND;
40
ops['!'] = EXP_OPERATOR_NOT;
41
ops['<'] = EXP_OPERATOR_LESS;
42
ops['>'] = EXP_OPERATOR_GREAT;
45
* Arbitrary offsets, chosen so
46
* the three blocks don't overlap.
49
ops['='+20] = EXP_OPERATOR_EQUAL;
50
ops['!'+20] = EXP_OPERATOR_NOTEQ;
51
ops['<'+20] = EXP_OPERATOR_LESSEQ;
52
ops['>'+20] = EXP_OPERATOR_GREATEQ;
55
ops['|'-30] = EXP_OPERATOR_OR;
56
ops['&'-30] = EXP_OPERATOR_AND;
57
ops['<'-30] = EXP_OPERATOR_LSHIFT;
58
ops['>'-30] = EXP_OPERATOR_RSHIFT;
62
* Insert the value of the specified object parameter,
63
* using the instance 'suffix' for wildcarded objects.
65
netsnmp_variable_list *
66
_expValue_evalParam( netsnmp_variable_list *expIdx, int param,
67
oid *suffix, size_t suffix_len )
69
netsnmp_variable_list *var = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
70
struct expObject *obj;
71
netsnmp_variable_list *val_var = NULL, *oval_var = NULL; /* values */
72
netsnmp_variable_list *dd_var = NULL, *odd_var = NULL; /* deltaDs */
73
netsnmp_variable_list *cond_var = NULL; /* conditionals */
77
* Retrieve the expObject entry for the requested parameter.
79
if ( !var || !expIdx || !expIdx->next_variable ||
80
!expIdx->next_variable->next_variable )
83
*expIdx->next_variable->next_variable->val.integer = param;
84
obj = (struct expObject *)
85
netsnmp_tdata_row_entry(
86
netsnmp_tdata_row_get_byidx( expObject_table_data, expIdx ));
89
* No such parameter configured for this expression
91
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_INDEX );
95
if ( obj->expObjectSampleType != EXPSAMPLETYPE_ABSOLUTE &&
96
obj->old_vars == NULL ) {
98
* Can't calculate delta values until the second pass
100
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_RESOURCE );
101
var->type = ASN_NULL;
107
* For a wildcarded object, search for the matching suffix.
110
if ( obj->flags & EXP_OBJ_FLAG_OWILD ) {
113
* If there's no suffix to match against, throw an error.
114
* An exact expression with a wildcarded object is invalid.
115
* XXX - Or just use first entry?
117
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_INDEX );
118
var->type = ASN_NULL;
122
* Otherwise, we'll walk *all* wildcarded values in parallel.
123
* This relies on the various varbind lists being set up with
124
* exactly the same entries. A little extra preparation
125
* during the data gathering simplifies things significantly!
127
if ( obj->expObjectSampleType != EXPSAMPLETYPE_ABSOLUTE )
128
oval_var = obj->old_vars;
129
if ( obj->flags & EXP_OBJ_FLAG_DWILD ) {
131
odd_var = obj->old_dvars;
133
if ( obj->flags & EXP_OBJ_FLAG_CWILD )
134
cond_var = obj->cvars;
136
n = obj->expObjectID_len;
138
if ( snmp_oid_compare( val_var->name+n, val_var->name_length-n,
139
suffix, suffix_len ))
141
val_var = val_var->next_variable;
143
oval_var = oval_var->next_variable;
145
dd_var = dd_var->next_variable;
146
odd_var = odd_var->next_variable;
149
cond_var = cond_var->next_variable;
157
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_INDEX );
158
var->type = ASN_NULL;
162
* Set up any non-wildcarded values - some
163
* of which may be null. That's fine.
166
oval_var = obj->old_vars;
169
odd_var = obj->old_dvars;
172
cond_var = obj->cvars;
176
* ... and return the appropriate value.
178
if (obj->expObjCond_len &&
179
(!cond_var || *cond_var->val.integer == 0)) {
181
* expObjectConditional says no
183
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_INDEX );
184
var->type = ASN_NULL;
187
if (dd_var && odd_var &&
188
*dd_var->val.integer != *odd_var->val.integer) {
190
* expObjectDeltaD says no
192
snmp_set_var_typed_integer( var, ASN_INTEGER, EXPERRCODE_INDEX );
193
var->type = ASN_NULL;
198
* XXX - May need to check sysUpTime discontinuities
199
* (unless this is handled earlier....)
201
switch ( obj->expObjectSampleType ) {
202
case EXPSAMPLETYPE_ABSOLUTE:
203
snmp_clone_var( val_var, var );
205
case EXPSAMPLETYPE_DELTA:
206
snmp_set_var_typed_integer( var, ASN_INTEGER /* or UNSIGNED? */,
207
*val_var->val.integer - *oval_var->val.integer );
209
case EXPSAMPLETYPE_CHANGED:
210
if ( val_var->val_len != oval_var->val_len )
212
else if (memcmp( val_var->val.string, oval_var->val.string,
213
val_var->val_len ) != 0 )
217
snmp_set_var_typed_integer( var, ASN_UNSIGNED, n );
224
* Utility routine to parse (and skip over) an integer constant
227
_expParse_integer( char *start, char **end ) {
232
for (cp=start; *cp; cp++)
239
netsnmp_variable_list *
240
_expValue_evalOperator(netsnmp_variable_list *left,
241
netsnmp_variable_list *op,
242
netsnmp_variable_list *right) {
245
switch( *op->val.integer ) {
246
case EXP_OPERATOR_ADD:
247
n = *left->val.integer + *right->val.integer; break;
248
case EXP_OPERATOR_SUBTRACT:
249
n = *left->val.integer - *right->val.integer; break;
250
case EXP_OPERATOR_MULTIPLY:
251
n = *left->val.integer * *right->val.integer; break;
252
case EXP_OPERATOR_DIVIDE:
253
n = *left->val.integer / *right->val.integer; break;
254
case EXP_OPERATOR_REMAINDER:
255
n = *left->val.integer % *right->val.integer; break;
256
case EXP_OPERATOR_BITXOR:
257
n = *left->val.integer ^ *right->val.integer; break;
258
case EXP_OPERATOR_BITNEGATE:
259
n = 99; /* *left->val.integer ~ *right->val.integer; */ break;
260
case EXP_OPERATOR_BITOR:
261
n = *left->val.integer | *right->val.integer; break;
262
case EXP_OPERATOR_BITAND:
263
n = *left->val.integer & *right->val.integer; break;
264
case EXP_OPERATOR_NOT:
265
n = 99; /* *left->val.integer ! *right->val.integer; */ break;
266
case EXP_OPERATOR_LESS:
267
n = *left->val.integer < *right->val.integer; break;
268
case EXP_OPERATOR_GREAT:
269
n = *left->val.integer > *right->val.integer; break;
270
case EXP_OPERATOR_EQUAL:
271
n = *left->val.integer == *right->val.integer; break;
272
case EXP_OPERATOR_NOTEQ:
273
n = *left->val.integer != *right->val.integer; break;
274
case EXP_OPERATOR_LESSEQ:
275
n = *left->val.integer <= *right->val.integer; break;
276
case EXP_OPERATOR_GREATEQ:
277
n = *left->val.integer >= *right->val.integer; break;
278
case EXP_OPERATOR_OR:
279
n = *left->val.integer || *right->val.integer; break;
280
case EXP_OPERATOR_AND:
281
n = *left->val.integer && *right->val.integer; break;
282
case EXP_OPERATOR_LSHIFT:
283
n = *left->val.integer << *right->val.integer; break;
284
case EXP_OPERATOR_RSHIFT:
285
n = *left->val.integer >> *right->val.integer; break;
288
left->next_variable = NULL;
290
right->next_variable = NULL;
291
snmp_free_var(right);
293
snmp_set_var_typed_integer( op, ASN_INTEGER, EXPERRCODE_OPERATOR );
299
left->next_variable = NULL;
301
op->next_variable = NULL;
303
snmp_set_var_typed_integer( right, ASN_INTEGER, n );
307
netsnmp_variable_list *
308
_expValue_evalFunction(netsnmp_variable_list *func) {
309
netsnmp_variable_list *params = func->next_variable;
311
params->next_variable = NULL;
312
snmp_free_var(params);
313
snmp_set_var_typed_integer( func, ASN_INTEGER, 99 );
317
netsnmp_variable_list *_expValue_evalExpr2(netsnmp_variable_list *exprAlDente);
318
netsnmp_variable_list *
319
_expValue_evalExpr( netsnmp_variable_list *expIdx,
320
char *exprRaw, char **exprEnd,
321
oid *suffix, size_t suffix_len )
323
netsnmp_variable_list *exprAlDente = NULL;
324
netsnmp_variable_list *vtail = NULL;
325
char *cp1, *cp2, *cp3;
326
netsnmp_variable_list *var = NULL;
329
oid oid_buf[MAX_OID_LEN];
331
DEBUGMSGTL(("disman:expr:eval1", "Evaluating '%s'\n", exprRaw));
332
if (!expIdx || !exprRaw)
336
* The expression is evaluated in two stages.
337
* First, we simplify ("parboil") the raw expression,
338
* tokenizing it into a sequence of varbind values, inserting
339
* object parameters, and (recursively) evaluating any
340
* parenthesised sub-expressions or function arguments.
343
for (cp1=exprRaw; cp1 && *cp1; ) {
347
* Locate the appropriate instance of the specified
348
* parameter, and insert the corresponding value.
350
n = _expParse_integer( cp1+1, &cp1 );
351
var = _expValue_evalParam( expIdx, n, suffix, suffix_len );
352
if ( var && var->type == ASN_NULL ) {
353
DEBUGMSGTL(("disman:expr:eval", "Invalid parameter '%d'\n", n));
354
/* Note position of failure in expression */
355
var->data = (void *)(cp1 - exprRaw);
356
snmp_free_var(exprAlDente);
360
vtail->next_variable = var;
369
* Recursively evaluate the sub-expression
371
var = _expValue_evalExpr( expIdx, cp1+1, &cp2,
372
suffix, suffix_len );
373
if ( var && var->type == ASN_NULL ) {
374
/* Adjust position of failure */
375
var->data = (void *)(cp1 - exprRaw + (int)var->data);
377
} else if (*cp2 != ')') {
378
snmp_free_var(exprAlDente);
379
DEBUGMSGTL(("disman:expr:eval", "Unbalanced parenthesis\n"));
380
snmp_set_var_typed_integer( var, ASN_INTEGER,
381
EXPERRCODE_PARENTHESIS );
382
var->type = ASN_NULL;
383
var->data = (void *)(cp2 - exprRaw);
387
vtail->next_variable = var;
392
cp1 = cp2+1; /* Skip to end of sub-expression */
398
* End of current (sub-)expression
399
* Note the end-position, and evaluate
400
* the parboiled list of tokens.
403
var = _expValue_evalExpr2( exprAlDente );
404
snmp_free_var(exprAlDente);
407
/* === Constants === */
410
memset( oid_buf, 0, sizeof(oid_buf));
411
while ( cp1 && *cp1 == '.' ) {
412
n = _expParse_integer( cp1+1, &cp2 );
417
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
419
(u_char*)oid_buf, i*sizeof(oid));
432
n = _expParse_integer( cp1, &cp2 );
433
if ( cp2 && *cp2 == '.' ) {
435
memset( oid_buf, 0, sizeof(oid_buf));
440
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
442
(u_char*)&n, sizeof(n));
449
case '"': /* String Constant */
450
for ( cp2 = cp1+1; *cp2; cp2++ ) {
453
if ( *cp2 == '\\' && *(cp2+1) == '"' )
458
* Unterminated string
460
DEBUGMSGTL(("disman:expr:eval", "Unterminated string\n"));
461
snmp_set_var_typed_integer( var, ASN_INTEGER,
463
var->type = ASN_NULL;
464
var->data = (void *)(cp2 - exprRaw);
468
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
476
/* === Operators === */
479
* Could be a unary minus....
481
if (!vtail || vtail->type == ASN_PRIV_OPERATOR) {
486
* ... or a (single-character) binary operator.
497
* Can't start an expression with a binary operator
499
DEBUGMSGTL(("disman:expr:eval", "Initial binary operator\n"));
500
snmp_set_var_typed_integer( var, ASN_INTEGER,
502
var->type = ASN_NULL;
503
var->data = (void *)(cp1 - exprRaw);
507
DEBUGMSGTL(("disman:expr:eval", "Binary operator %c (%d)\n", *cp1, n));
508
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
510
(u_char*)&n, sizeof(n));
511
var->type = ASN_PRIV_OPERATOR;
517
* Multi-character binary operators
527
* Can't start an expression with a binary operator
529
DEBUGMSGTL(("disman:expr:eval", "Initial binary operator\n"));
530
snmp_set_var_typed_integer( var, ASN_INTEGER,
532
var->type = ASN_NULL;
533
var->data = (void *)(cp1 - exprRaw);
536
if ( *(cp1+1) == '=' )
537
n = ops[ *cp1++ + 20];
538
else if ( *(cp1+1) == *cp1 )
539
n = ops[ *cp1++ - 30];
542
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
544
(u_char*)&n, sizeof(n));
545
var->type = ASN_PRIV_OPERATOR;
550
/* === Functions === */
551
case 'a': /* average/arraySection */
552
case 'c': /* counter32/counter64 */
553
case 'e': /* exists */
554
case 'm': /* maximum/minimum */
555
case 'o': /* oidBegins/Ends/Contains */
556
case 's': /* sum / string{B,E,C} */
557
var = snmp_varlist_add_variable( &exprAlDente, NULL, 0,
561
var->type = ASN_PRIV_FUNCTION;
563
while (*cp1 >= 'a' && *cp1 <= 'z')
568
if (isalpha( *cp1 )) {
570
* Unrecognised function call ?
572
DEBUGMSGTL(("disman:expr:eval", "Unrecognised function '%s'\n", cp1));
573
snmp_set_var_typed_integer( var, ASN_INTEGER,
574
EXPERRCODE_FUNCTION );
575
var->type = ASN_NULL;
576
var->data = (void *)(cp1 - exprRaw);
579
else if (!isspace( *cp1 )) {
581
* Unrecognised operator ?
583
DEBUGMSGTL(("disman:expr:eval", "Unrecognised operator '%c'\n", *cp1));
584
snmp_set_var_typed_integer( var, ASN_INTEGER,
585
EXPERRCODE_OPERATOR );
586
var->type = ASN_NULL;
587
var->data = (void *)(cp1 - exprRaw);
596
* ... then we evaluate the resulting simplified ("al dente")
597
* expression, in the usual manner.
600
var = _expValue_evalExpr2( exprAlDente );
601
DEBUGMSGTL(( "disman:expr:eval1", "Evaluated to "));
602
DEBUGMSGVAR(("disman:expr:eval1", var));
603
DEBUGMSG(( "disman:expr:eval1", "\n"));
604
/* snmp_free_var(exprAlDente); XXX - Crashes */
608
netsnmp_variable_list *
609
_expValue_evalExpr2( netsnmp_variable_list *exprAlD )
611
netsnmp_variable_list *stack = NULL;
612
netsnmp_variable_list *lval, *rval, *op;
613
netsnmp_variable_list *vp, *vp1 = NULL;
615
DEBUGIF(( "disman:expr:eval2")) {
616
for (vp = exprAlD; vp; vp=vp->next_variable) {
617
if ( vp->type == ASN_PRIV_OPERATOR )
618
DEBUGMSGTL(( "disman:expr:eval2", "Operator %d\n",
620
else if ( vp->type == ASN_PRIV_FUNCTION )
621
DEBUGMSGTL(( "disman:expr:eval2", "Function %s\n",
624
DEBUGMSGTL(( "disman:expr:eval2", "Operand "));
625
DEBUGMSGVAR(("disman:expr:eval2", vp));
626
DEBUGMSG(( "disman:expr:eval2", "\n"));
631
for (vp = exprAlD; vp; vp=vp1) {
632
vp1 = vp->next_variable;
633
if ( vp->type == ASN_PRIV_OPERATOR ) {
634
/* Must *always* follow a value */
635
if ( !stack || stack->type == ASN_PRIV_OPERATOR ) {
636
snmp_set_var_typed_integer( vp, ASN_INTEGER,
639
snmp_free_var( stack );
643
* Evaluate any higher priority operators
644
* already on the stack....
646
while ( stack->next_variable &&
647
stack->next_variable->type == ASN_PRIV_OPERATOR &&
648
(*stack->next_variable->val.integer > *vp->val.integer)) {
650
op = stack->next_variable;
651
lval = op->next_variable;
652
stack = lval->next_variable;
654
rval = _expValue_evalOperator( lval, op, rval );
655
rval->next_variable = stack;
659
* ... and then push this operator onto the stack.
661
vp->next_variable = stack;
663
} else if ( vp->type == ASN_PRIV_FUNCTION ) {
664
/* Must be first, or follow an operator */
665
if ( stack && stack->type != ASN_PRIV_OPERATOR ) {
666
snmp_set_var_typed_integer( vp, ASN_INTEGER,
669
snmp_free_var( stack );
673
* Evaluate this function (consuming the
674
* appropriate parameters from the token
675
* list), and push the result onto the stack.
677
vp = _expValue_evalFunction( vp );
678
vp1 = vp->next_variable;
679
vp->next_variable = stack;
682
/* Must be first, or follow an operator */
683
if ( stack && stack->type != ASN_PRIV_OPERATOR ) {
684
snmp_set_var_typed_integer( vp, ASN_INTEGER,
687
snmp_free_var( stack );
691
* Push this value onto the stack
693
vp->next_variable = stack;
699
* Now evaluate whatever's left on the stack
700
* and return the final result.
702
while ( stack && stack->next_variable ) {
704
op = stack->next_variable;
705
lval = op->next_variable;
706
stack = lval->next_variable;
708
rval = _expValue_evalOperator( lval, op, rval );
709
rval->next_variable = stack;
718
netsnmp_variable_list *
719
expValue_evaluateExpression( struct expExpression *exp,
720
oid *suffix, size_t suffix_len )
722
char exprRaw[ EXP_STR3_LEN+1 ];
723
netsnmp_variable_list *var;
724
netsnmp_variable_list owner_var, name_var, param_var;
732
* Set up a varbind list containing the various index values
733
* (including a placeholder for expObjectIndex).
735
* This saves having to construct the same index list repeatedly
737
memset(&owner_var, 0, sizeof(netsnmp_variable_list));
738
memset(&name_var, 0, sizeof(netsnmp_variable_list));
739
memset(¶m_var, 0, sizeof(netsnmp_variable_list));
740
snmp_set_var_typed_value( &owner_var, ASN_OCTET_STR,
741
(u_char*)exp->expOwner, strlen(exp->expOwner));
742
snmp_set_var_typed_value( &name_var, ASN_OCTET_STR,
743
(u_char*)exp->expName, strlen(exp->expName));
744
n = 99; /* dummy value */
745
snmp_set_var_typed_value( ¶m_var, ASN_INTEGER,
746
(u_char*)&n, sizeof(long));
747
owner_var.next_variable = &name_var;
748
name_var.next_variable = ¶m_var;
751
* Make a working copy of the expression, and evaluate it.
753
memset(exprRaw, 0, sizeof(exprRaw));
754
memcpy(exprRaw, exp->expExpression, sizeof(exprRaw));
756
var = _expValue_evalExpr( &owner_var, exprRaw, &cp, suffix, suffix_len );
758
* Check for any problems, and record the appropriate error
760
if (!cp || *cp != '\0') {
762
* When we had finished, there was a lot
763
* of bricks^Wcharacters left over....
765
_expValue_setError( exp, EXPERRCODE_SYNTAX, suffix, suffix_len, NULL );
769
/* Shouldn't happen */
770
_expValue_setError( exp, EXPERRCODE_RESOURCE, suffix, suffix_len, NULL );
773
if (var->type == ASN_NULL) {
775
* Error explicitly reported from the evaluation routines.
777
_expValue_setError( exp, *(var->val.integer), suffix, suffix_len, var );
780
if (0 /* COMPARE var->type WITH exp->expValueType */ ) {
782
* XXX - Check to see whether the returned type (ASN_XXX)
783
* is compatible with the requested type (an enum)
786
/* If not, throw an error */
787
_expValue_setError( exp, EXPERRCODE_TYPE, suffix, suffix_len, var );
794
_expValue_setError( struct expExpression *exp, int reason,
795
oid *suffix, size_t suffix_len,
796
netsnmp_variable_list *var)
800
exp->expErrorCount++;
801
/* exp->expErrorTime = NOW; */
802
exp->expErrorIndex = ( var && var->data ? (int)var->data : 0 );
803
exp->expErrorCode = reason;
804
memset( exp->expErrorInstance, 0, sizeof(exp->expErrorInstance));
805
memcpy( exp->expErrorInstance, suffix, suffix_len * sizeof(oid));
806
exp->expErrorInst_len = suffix_len;
807
snmp_free_var( var );