blob: 5a0dcba533ba5ca08e19c9a05a5a191802526845 [file] [log] [blame]
/*
* DisMan Event MIB:
* Core implementation of the trigger handling behaviour
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "disman/event/mteTrigger.h"
#include "disman/event/mteEvent.h"
netsnmp_feature_child_of(disman_debugging, libnetsnmpmibs)
netsnmp_feature_child_of(mtetrigger, libnetsnmpmibs)
netsnmp_feature_child_of(mtetrigger_removeentry, mtetrigger)
netsnmp_tdata *trigger_table_data;
oid _sysUpTime_instance[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
size_t _sysUpTime_inst_len = OID_LENGTH(_sysUpTime_instance);
long mteTriggerFailures;
/*
* Initialize the container for the (combined) mteTrigger*Table,
* regardless of which table initialisation routine is called first.
*/
void
init_trigger_table_data(void)
{
DEBUGMSGTL(("disman:event:init", "init trigger container\n"));
if (!trigger_table_data) {
trigger_table_data = netsnmp_tdata_create_table("mteTriggerTable", 0);
if (!trigger_table_data) {
snmp_log(LOG_ERR, "failed to create mteTriggerTable");
return;
}
DEBUGMSGTL(("disman:event:init", "create trigger container (%p)\n",
trigger_table_data));
}
mteTriggerFailures = 0;
}
/** Initializes the mteTrigger module */
void
init_mteTrigger(void)
{
init_trigger_table_data();
}
/* ===================================================
*
* APIs for maintaining the contents of the (combined)
* mteTrigger*Table container.
*
* =================================================== */
#ifndef NETSNMP_FEATURE_REMOVE_DISMAN_DEBUGGING
void
_mteTrigger_dump(void)
{
struct mteTrigger *entry;
netsnmp_tdata_row *row;
int i = 0;
for (row = netsnmp_tdata_row_first(trigger_table_data);
row;
row = netsnmp_tdata_row_next(trigger_table_data, row)) {
entry = (struct mteTrigger *)row->data;
DEBUGMSGTL(("disman:event:dump", "TriggerTable entry %d: ", i));
DEBUGMSGOID(("disman:event:dump", row->oid_index.oids, row->oid_index.len));
DEBUGMSG(("disman:event:dump", "(%s, %s)",
row->indexes->val.string,
row->indexes->next_variable->val.string));
DEBUGMSG(("disman:event:dump", ": %p, %p\n", row, entry));
i++;
}
DEBUGMSGTL(("disman:event:dump", "TriggerTable %d entries\n", i));
}
#endif /* NETSNMP_FEATURE_REMOVE_DISMAN_DEBUGGING */
/*
* Create a new row in the trigger table
*/
netsnmp_tdata_row *
mteTrigger_createEntry(const char *mteOwner, char *mteTName, int fixed)
{
struct mteTrigger *entry;
netsnmp_tdata_row *row;
size_t mteOwner_len = (mteOwner) ? strlen(mteOwner) : 0;
size_t mteTName_len = (mteTName) ? strlen(mteTName) : 0;
DEBUGMSGTL(("disman:event:table", "Create trigger entry (%s, %s)\n",
mteOwner, mteTName));
/*
* Create the mteTrigger entry, and the
* (table-independent) row wrapper structure...
*/
entry = SNMP_MALLOC_TYPEDEF(struct mteTrigger);
if (!entry)
return NULL;
row = netsnmp_tdata_create_row();
if (!row) {
SNMP_FREE(entry);
return NULL;
}
row->data = entry;
/*
* ... initialize this row with the indexes supplied
* and the default values for the row...
*/
if (mteOwner)
memcpy(entry->mteOwner, mteOwner, mteOwner_len);
netsnmp_table_row_add_index(row, ASN_OCTET_STR,
entry->mteOwner, mteOwner_len);
if (mteTName)
memcpy(entry->mteTName, mteTName, mteTName_len);
netsnmp_table_row_add_index(row, ASN_PRIV_IMPLIED_OCTET_STR,
entry->mteTName, mteTName_len);
/* entry->mteTriggerTest = MTE_TRIGGER_BOOLEAN; */
entry->mteTriggerValueID_len = 2; /* .0.0 */
entry->mteTriggerFrequency = 600;
memcpy(entry->mteDeltaDiscontID, _sysUpTime_instance,
sizeof(_sysUpTime_instance));
entry->mteDeltaDiscontID_len = _sysUpTime_inst_len;
entry->mteDeltaDiscontIDType = MTE_DELTAD_TTICKS;
entry->flags |= MTE_TRIGGER_FLAG_SYSUPT;
entry->mteTExTest = (MTE_EXIST_PRESENT | MTE_EXIST_ABSENT);
entry->mteTExStartup = (MTE_EXIST_PRESENT | MTE_EXIST_ABSENT);
entry->mteTBoolComparison = MTE_BOOL_UNEQUAL;
entry->flags |= MTE_TRIGGER_FLAG_BSTART;
entry->mteTThStartup = MTE_THRESH_START_RISEFALL;
if (fixed)
entry->flags |= MTE_TRIGGER_FLAG_FIXED;
/*
* ... and insert the row into the (common) table container
*/
netsnmp_tdata_add_row(trigger_table_data, row);
DEBUGMSGTL(("disman:event:table", "Trigger entry created\n"));
return row;
}
#ifndef NETSNMP_FEATURE_REMOVE_MTETRIGGER_REMOVEENTRY
/*
* Remove a row from the trigger table
*/
void
mteTrigger_removeEntry(netsnmp_tdata_row *row)
{
struct mteTrigger *entry;
if (!row)
return; /* Nothing to remove */
entry = (struct mteTrigger *)
netsnmp_tdata_remove_and_delete_row(trigger_table_data, row);
if (entry) {
mteTrigger_disable( entry );
SNMP_FREE(entry);
}
}
#endif /* NETSNMP_FEATURE_REMOVE_MTETRIGGER_REMOVEENTRY */
/* ===================================================
*
* APIs for evaluating a trigger,
* and firing the appropriate event
*
* =================================================== */
const char *_ops[] = { "",
"!=", /* MTE_BOOL_UNEQUAL */
"==", /* MTE_BOOL_EQUAL */
"<", /* MTE_BOOL_LESS */
"<=", /* MTE_BOOL_LESSEQUAL */
">", /* MTE_BOOL_GREATER */
">=" /* MTE_BOOL_GREATEREQUAL */ };
void
_mteTrigger_failure( /* int error, */ const char *msg )
{
/*
* XXX - Send an mteTriggerFailure trap
* (if configured to do so)
*/
mteTriggerFailures++;
snmp_log(LOG_ERR, "%s\n", msg );
return;
}
void
mteTrigger_run( unsigned int reg, void *clientarg)
{
struct mteTrigger *entry = (struct mteTrigger *)clientarg;
netsnmp_variable_list *var, *vtmp;
netsnmp_variable_list *vp1, *vp1_prev;
netsnmp_variable_list *vp2, *vp2_prev;
netsnmp_variable_list *dvar = NULL;
netsnmp_variable_list *dv1 = NULL, *dv2 = NULL;
netsnmp_variable_list sysUT_var;
int cmp = 0, n, n2;
long value;
const char *reason;
if (!entry) {
snmp_alarm_unregister( reg );
return;
}
if (!(entry->flags & MTE_TRIGGER_FLAG_ENABLED ) ||
!(entry->flags & MTE_TRIGGER_FLAG_ACTIVE ) ||
!(entry->flags & MTE_TRIGGER_FLAG_VALID )) {
return;
}
{
extern netsnmp_agent_session *netsnmp_processing_set;
if (netsnmp_processing_set) {
/*
* netsnmp_handle_request will not be responsive to our efforts to
* Retrieve the requested MIB value(s)...
* so we will skip it.
* https://sourceforge.net/tracker/
* index.php?func=detail&aid=1557406&group_id=12694&atid=112694
*/
DEBUGMSGTL(("disman:event:trigger:monitor",
"Skipping trigger (%s) while netsnmp_processing_set\n",
entry->mteTName));
return;
}
}
/*
* Retrieve the requested MIB value(s)...
*/
DEBUGMSGTL(( "disman:event:trigger:monitor", "Running trigger (%s)\n", entry->mteTName));
var = (netsnmp_variable_list *)SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!var) {
_mteTrigger_failure("failed to create mteTrigger query varbind");
return;
}
snmp_set_var_objid( var, entry->mteTriggerValueID,
entry->mteTriggerValueID_len );
if ( entry->flags & MTE_TRIGGER_FLAG_VWILD ) {
n = netsnmp_query_walk( var, entry->session );
} else {
n = netsnmp_query_get( var, entry->session );
}
if ( n != SNMP_ERR_NOERROR ) {
DEBUGMSGTL(( "disman:event:trigger:monitor", "Trigger query (%s) failed: %d\n",
(( entry->flags & MTE_TRIGGER_FLAG_VWILD ) ? "walk" : "get"), n));
_mteTrigger_failure( "failed to run mteTrigger query" );
snmp_free_varbind(var);
return;
}
/*
* ... canonicalise the results (to simplify later comparisons)...
*/
vp1 = var; vp1_prev = NULL;
vp2 = entry->old_results; vp2_prev = NULL;
entry->count=0;
while (vp1) {
/*
* Flatten various missing values/exceptions into a single form
*/
switch (vp1->type) {
case SNMP_NOSUCHINSTANCE:
case SNMP_NOSUCHOBJECT:
case ASN_PRIV_RETRY: /* Internal only ? */
vp1->type = ASN_NULL;
}
/*
* Keep track of how many entries have been retrieved.
*/
entry->count++;
/*
* Ensure previous and current result match
* (with corresponding entries in both lists)
* and set the flags indicating which triggers are armed
*/
if (vp2) {
cmp = snmp_oid_compare(vp1->name, vp1->name_length,
vp2->name, vp2->name_length);
if ( cmp < 0 ) {
/*
* If a new value has appeared, insert a matching
* dummy entry into the previous result list.
*
* XXX - check how this is best done.
*/
vtmp = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!vtmp) {
_mteTrigger_failure(
"failed to create mteTrigger temp varbind");
snmp_free_varbind(var);
return;
}
vtmp->type = ASN_NULL;
snmp_set_var_objid( vtmp, vp1->name, vp1->name_length );
vtmp->next_variable = vp2;
if (vp2_prev) {
vp2_prev->next_variable = vtmp;
} else {
entry->old_results = vtmp;
}
vp2_prev = vtmp;
vp1->index = MTE_ARMED_ALL; /* XXX - plus a new flag */
vp1_prev = vp1;
vp1 = vp1->next_variable;
}
else if ( cmp == 0 ) {
/*
* If it's a continuing entry, just copy across the armed flags
*/
vp1->index = vp2->index;
vp1_prev = vp1;
vp1 = vp1->next_variable;
vp2_prev = vp2;
vp2 = vp2->next_variable;
} else {
/*
* If a value has just disappeared, insert a
* matching dummy entry into the current result list.
*
* XXX - check how this is best done.
*
*/
if ( vp2->type != ASN_NULL ) {
vtmp = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!vtmp) {
_mteTrigger_failure(
"failed to create mteTrigger temp varbind");
snmp_free_varbind(var);
return;
}
vtmp->type = ASN_NULL;
snmp_set_var_objid( vtmp, vp2->name, vp2->name_length );
vtmp->next_variable = vp1;
if (vp1_prev) {
vp1_prev->next_variable = vtmp;
} else {
var = vtmp;
}
vp1_prev = vtmp;
vp2_prev = vp2;
vp2 = vp2->next_variable;
} else {
/*
* But only if this entry has *just* disappeared. If the
* entry from the last run was a dummy too, then remove it.
* (leaving vp2_prev unchanged)
*/
vtmp = vp2;
if (vp2_prev) {
vp2_prev->next_variable = vp2->next_variable;
} else {
entry->old_results = vp2->next_variable;
}
vp2 = vp2->next_variable;
vtmp->next_variable = NULL;
snmp_free_varbind( vtmp );
}
}
} else {
/*
* No more old results to compare.
* Either all remaining values have only just been created ...
* (and we need to create dummy 'old' entries for them)
*/
if ( vp2_prev ) {
vtmp = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!vtmp) {
_mteTrigger_failure(
"failed to create mteTrigger temp varbind");
snmp_free_varbind(var);
return;
}
vtmp->type = ASN_NULL;
snmp_set_var_objid( vtmp, vp1->name, vp1->name_length );
vtmp->next_variable = vp2_prev->next_variable;
vp2_prev->next_variable = vtmp;
vp2_prev = vtmp;
}
/*
* ... or this is the first run through
* (and there were no old results at all)
*
* In either case, mark the current entry as armed and new.
* Note that we no longer need to maintain 'vp1_prev'
*/
vp1->index = MTE_ARMED_ALL; /* XXX - plus a new flag */
vp1 = vp1->next_variable;
}
}
/*
* ... and then work through these result(s), deciding
* whether or not to trigger the corresponding event.
*
* Note that there's no point in evaluating Existence or
* Boolean tests if there's no corresponding event.
* (Even if the trigger matched, nothing would be done anyway).
*/
if ((entry->mteTriggerTest & MTE_TRIGGER_EXISTENCE) &&
(entry->mteTExEvent[0] != '\0' )) {
/*
* If we don't have a record of previous results,
* this must be the first time through, so consider
* the mteTriggerExistenceStartup tests.
*/
if ( !entry->old_results ) {
/*
* With the 'present(0)' test, the trigger should fire
* for each value in the varbind list returned
* (whether the monitored value is wildcarded or not).
*/
if (entry->mteTExTest & entry->mteTExStartup & MTE_EXIST_PRESENT) {
for (vp1 = var; vp1; vp1=vp1->next_variable) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing initial existence test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire",
" (present)\n"));;
entry->mteTriggerXOwner = entry->mteTExObjOwner;
entry->mteTriggerXObjects = entry->mteTExObjects;
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTExEvOwner, entry->mteTExEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
/*
* An initial 'absent(1)' test only makes sense when
* monitoring a non-wildcarded OID (how would we know
* which rows of the table "ought" to exist, but don't?)
*/
if (entry->mteTExTest & entry->mteTExStartup & MTE_EXIST_ABSENT) {
if (!(entry->flags & MTE_TRIGGER_FLAG_VWILD) &&
var->type == ASN_NULL ) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing initial existence test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
var->name, var->name_length));
DEBUGMSG(( "disman:event:trigger:fire",
" (absent)\n"));;
entry->mteTriggerXOwner = entry->mteTExObjOwner;
entry->mteTriggerXObjects = entry->mteTExObjects;
/*
* It's unclear what value the 'mteHotValue' payload
* should take when a monitored instance does not
* exist on startup. The only sensible option is
* to report a NULL value, but this clashes with
* the syntax of the mteHotValue MIB object.
*/
entry->mteTriggerFired = var;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTExEvOwner, entry->mteTExEvent,
entry, var->name+n, var->name_length-n);
}
}
} /* !old_results */
/*
* Otherwise, compare the current set of results with
* the previous ones, looking for changes. We can
* assume that the two lists match (see above).
*/
else {
for (vp1 = var, vp2 = entry->old_results;
vp1;
vp1=vp1->next_variable, vp2=vp2->next_variable) {
/* Use this field to indicate that the trigger should fire */
entry->mteTriggerFired = NULL;
reason = NULL;
if ((entry->mteTExTest & MTE_EXIST_PRESENT) &&
(vp1->type != ASN_NULL) &&
(vp2->type == ASN_NULL)) {
/* A new instance has appeared */
entry->mteTriggerFired = vp1;
reason = "(present)";
} else if ((entry->mteTExTest & MTE_EXIST_ABSENT) &&
(vp1->type == ASN_NULL) &&
(vp2->type != ASN_NULL)) {
/*
* A previous instance has disappeared.
*
* It's unclear what value the 'mteHotValue' payload
* should take when this happens - the previous
* value (vp2), or a NULL value (vp1) ?
* NULL makes more sense logically, but clashes
* with the syntax of the mteHotValue MIB object.
*/
entry->mteTriggerFired = vp2;
reason = "(absent)";
} else if ((entry->mteTExTest & MTE_EXIST_CHANGED) &&
((vp1->val_len != vp2->val_len) ||
(memcmp( vp1->val.string, vp2->val.string,
vp1->val_len) != 0 ))) {
/*
* This comparison detects changes in *any* type
* of value, numeric or string (or even OID).
*
* Unfortunately, the default 'mteTriggerFired'
* notification payload can't report non-numeric
* changes properly (see syntax of 'mteHotValue')
*/
entry->mteTriggerFired = vp1;
reason = "(changed)";
}
if ( entry->mteTriggerFired ) {
/*
* One of the above tests has matched,
* so fire the trigger.
*/
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing existence test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire",
" %s\n", reason));;
entry->mteTriggerXOwner = entry->mteTExObjOwner;
entry->mteTriggerXObjects = entry->mteTExObjects;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTExEvOwner, entry->mteTExEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
} /* !old_results - end of else block */
} /* MTE_TRIGGER_EXISTENCE */
/*
* We'll need sysUpTime.0 regardless...
*/
DEBUGMSGTL(("disman:event:delta", "retrieve sysUpTime.0\n"));
memset( &sysUT_var, 0, sizeof( netsnmp_variable_list ));
snmp_set_var_objid( &sysUT_var, _sysUpTime_instance, _sysUpTime_inst_len );
netsnmp_query_get( &sysUT_var, entry->session );
if (( entry->mteTriggerTest & MTE_TRIGGER_BOOLEAN ) ||
( entry->mteTriggerTest & MTE_TRIGGER_THRESHOLD )) {
/*
* Although Existence tests can work with any syntax values,
* Boolean and Threshold tests are integer-only. Ensure that
* the returned value(s) are appropriate.
*
* Note that we only need to check the first value, since all
* instances of a given object should have the same syntax.
*/
switch (var->type) {
case ASN_INTEGER:
case ASN_COUNTER:
case ASN_GAUGE:
case ASN_TIMETICKS:
case ASN_UINTEGER:
case ASN_COUNTER64:
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
case ASN_OPAQUE_COUNTER64:
case ASN_OPAQUE_U64:
case ASN_OPAQUE_I64:
#endif
/* OK */
break;
default:
/*
* Other syntax values can't be used for Boolean/Theshold
* tests. Report this as an error, and then rotate the
* results ready for the next run, (which will presumably
* also detect this as an error once again!)
*/
DEBUGMSGTL(( "disman:event:trigger:fire",
"Returned non-integer result(s): "));
DEBUGMSGOID(("disman:event:trigger:fire",
var->name, var->name_length));
DEBUGMSG(( "disman:event:trigger:fire",
" (boolean/threshold) %d\n", var->type));;
snmp_free_varbind( entry->old_results );
entry->old_results = var;
return;
}
/*
* Retrieve the discontinuity markers for delta-valued samples.
* (including sysUpTime.0 if not specified explicitly).
*/
if ( entry->flags & MTE_TRIGGER_FLAG_DELTA ) {
if (!(entry->flags & MTE_TRIGGER_FLAG_SYSUPT)) {
/*
* ... but only retrieve the configured discontinuity
* marker(s) if they refer to something different.
*/
DEBUGMSGTL(( "disman:event:delta",
"retrieve discontinuity marker(s): "));
DEBUGMSGOID(("disman:event:delta", entry->mteDeltaDiscontID,
entry->mteDeltaDiscontID_len ));
DEBUGMSG(( "disman:event:delta", " %s\n",
(entry->flags & MTE_TRIGGER_FLAG_DWILD ? " (wild)" : "")));
dvar = (netsnmp_variable_list *)
SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!dvar) {
_mteTrigger_failure(
"failed to create mteTrigger delta query varbind");
return;
}
snmp_set_var_objid( dvar, entry->mteDeltaDiscontID,
entry->mteDeltaDiscontID_len );
if ( entry->flags & MTE_TRIGGER_FLAG_DWILD ) {
n = netsnmp_query_walk( dvar, entry->session );
} else {
n = netsnmp_query_get( dvar, entry->session );
}
if ( n != SNMP_ERR_NOERROR ) {
_mteTrigger_failure( "failed to run mteTrigger delta query" );
snmp_free_varbind( dvar );
return;
}
}
/*
* We can't calculate delta values the first time through,
* so there's no point in evaluating the remaining tests.
*
* Save the results (and discontinuity markers),
* ready for the next run.
*/
if ( !entry->old_results ) {
entry->old_results = var;
entry->old_deltaDs = dvar;
entry->sysUpTime = *sysUT_var.val.integer;
return;
}
/*
* If the sysUpTime marker has been reset (or strictly,
* has advanced by less than the monitor frequency),
* there's no point in trying the remaining tests.
*/
if (*sysUT_var.val.integer < entry->sysUpTime) {
DEBUGMSGTL(( "disman:event:delta",
"single discontinuity: (sysUT)\n"));
snmp_free_varbind( entry->old_results );
snmp_free_varbind( entry->old_deltaDs );
entry->old_results = var;
entry->old_deltaDs = dvar;
entry->sysUpTime = *sysUT_var.val.integer;
return;
}
/*
* Similarly if a separate (non-wildcarded) discontinuity
* marker has changed, then there's no
* point in trying to evaluate these tests either.
*/
if (!(entry->flags & MTE_TRIGGER_FLAG_DWILD) &&
!(entry->flags & MTE_TRIGGER_FLAG_SYSUPT) &&
(!entry->old_deltaDs ||
(entry->old_deltaDs->val.integer != dvar->val.integer))) {
DEBUGMSGTL(( "disman:event:delta", "single discontinuity: ("));
DEBUGMSGOID(( "disman:event:delta", entry->mteDeltaDiscontID,
entry->mteDeltaDiscontID_len));
DEBUGMSG(( "disman:event:delta", ")\n"));
snmp_free_varbind( entry->old_results );
snmp_free_varbind( entry->old_deltaDs );
entry->old_results = var;
entry->old_deltaDs = dvar;
entry->sysUpTime = *sysUT_var.val.integer;
return;
}
/*
* Ensure that the list of (wildcarded) discontinuity
* markers matches the list of monitored values
* (inserting/removing discontinuity varbinds as needed)
*
* XXX - An alternative approach would be to use the list
* of monitored values (instance subidentifiers) to build
* the exact list of delta markers to retrieve earlier.
*/
if (entry->flags & MTE_TRIGGER_FLAG_DWILD) {
vp1 = var;
vp2 = dvar;
vp2_prev = NULL;
n = entry->mteTriggerValueID_len;
n2 = entry->mteDeltaDiscontID_len;
while (vp1) {
/*
* For each monitored instance, check whether
* there's a matching discontinuity entry.
*/
cmp = snmp_oid_compare(vp1->name+n, vp1->name_length-n,
vp2->name+n2, vp2->name_length-n2 );
if ( cmp < 0 ) {
/*
* If a discontinuity entry is missing,
* insert a (dummy) varbind.
* The corresponding delta calculation will
* fail, but this simplifies the later code.
*/
vtmp = (netsnmp_variable_list *)
SNMP_MALLOC_TYPEDEF( netsnmp_variable_list );
if (!vtmp) {
_mteTrigger_failure(
"failed to create mteTrigger discontinuity varbind");
snmp_free_varbind(dvar);
return;
}
snmp_set_var_objid(vtmp, entry->mteDeltaDiscontID,
entry->mteDeltaDiscontID_len);
/* XXX - append instance subids */
vtmp->next_variable = vp2;
vp2_prev->next_variable = vtmp;
vp2_prev = vtmp;
vp1 = vp1->next_variable;
} else if ( cmp == 0 ) {
/*
* Matching discontinuity entry - all OK.
*/
vp2_prev = vp2;
vp2 = vp2->next_variable;
vp1 = vp1->next_variable;
} else {
/*
* Remove unneeded discontinuity entry
*/
vtmp = vp2;
vp2_prev->next_variable = vp2->next_variable;
vp2 = vp2->next_variable;
vtmp->next_variable = NULL;
snmp_free_varbind( vtmp );
}
}
/*
* XXX - Now need to ensure that the old list of
* delta discontinuity markers matches as well.
*/
}
} /* delta samples */
} /* Boolean/Threshold test checks */
/*
* Only run the Boolean tests if there's an event to be triggered
*/
if ((entry->mteTriggerTest & MTE_TRIGGER_BOOLEAN) &&
(entry->mteTBoolEvent[0] != '\0' )) {
if (entry->flags & MTE_TRIGGER_FLAG_DELTA) {
vp2 = entry->old_results;
if (entry->flags & MTE_TRIGGER_FLAG_DWILD) {
dv1 = dvar;
dv2 = entry->old_deltaDs;
}
}
for ( vp1 = var; vp1; vp1=vp1->next_variable ) {
/*
* Determine the value to be monitored...
*/
if ( !vp1->val.integer ) { /* No value */
if ( vp2 )
vp2 = vp2->next_variable;
continue;
}
if (entry->flags & MTE_TRIGGER_FLAG_DELTA) {
if (entry->flags & MTE_TRIGGER_FLAG_DWILD) {
/*
* We've already checked any non-wildcarded
* discontinuity markers (inc. sysUpTime.0).
* Validate this particular sample against
* the relevant wildcarded marker...
*/
if ((dv1->type == ASN_NULL) ||
(dv1->type != dv2->type) ||
(*dv1->val.integer != *dv2->val.integer)) {
/*
* Bogus or changed discontinuity marker.
* Need to skip this sample.
*/
DEBUGMSGTL(( "disman:event:delta", "discontinuity occurred: "));
DEBUGMSGOID(("disman:event:delta", vp1->name,
vp1->name_length ));
DEBUGMSG(( "disman:event:delta", " \n" ));
vp2 = vp2->next_variable;
continue;
}
}
/*
* ... and check there is a previous sample to calculate
* the delta value against (regardless of whether the
* discontinuity marker was wildcarded or not).
*/
if (vp2->type == ASN_NULL) {
DEBUGMSGTL(( "disman:event:delta", "missing sample: "));
DEBUGMSGOID(("disman:event:delta", vp1->name,
vp1->name_length ));
DEBUGMSG(( "disman:event:delta", " \n" ));
vp2 = vp2->next_variable;
continue;
}
value = (*vp1->val.integer - *vp2->val.integer);
DEBUGMSGTL(( "disman:event:delta", "delta sample: "));
DEBUGMSGOID(("disman:event:delta", vp1->name,
vp1->name_length ));
DEBUGMSG(( "disman:event:delta", " (%ld - %ld) = %ld\n",
*vp1->val.integer, *vp2->val.integer, value));
vp2 = vp2->next_variable;
} else {
value = *vp1->val.integer;
}
/*
* ... evaluate the comparison ...
*/
switch (entry->mteTBoolComparison) {
case MTE_BOOL_UNEQUAL:
cmp = ( value != entry->mteTBoolValue );
break;
case MTE_BOOL_EQUAL:
cmp = ( value == entry->mteTBoolValue );
break;
case MTE_BOOL_LESS:
cmp = ( value < entry->mteTBoolValue );
break;
case MTE_BOOL_LESSEQUAL:
cmp = ( value <= entry->mteTBoolValue );
break;
case MTE_BOOL_GREATER:
cmp = ( value > entry->mteTBoolValue );
break;
case MTE_BOOL_GREATEREQUAL:
cmp = ( value >= entry->mteTBoolValue );
break;
}
DEBUGMSGTL(( "disman:event:delta", "Bool comparison: (%ld %s %ld) %d\n",
value, _ops[entry->mteTBoolComparison],
entry->mteTBoolValue, cmp));
/*
* ... and decide whether to trigger the event.
* (using the 'index' field of the varbind structure
* to remember whether the trigger has already fired)
*/
if ( cmp ) {
if (vp1->index & MTE_ARMED_BOOLEAN ) {
vp1->index &= ~MTE_ARMED_BOOLEAN;
/*
* NB: Clear the trigger armed flag even if the
* (starting) event dosn't actually fire.
* Otherwise initially true (but suppressed)
* triggers will fire on the *second* probe.
*/
if ( entry->old_results ||
(entry->flags & MTE_TRIGGER_FLAG_BSTART)) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing boolean test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire", "%s\n",
(entry->old_results ? "" : " (startup)")));
entry->mteTriggerXOwner = entry->mteTBoolObjOwner;
entry->mteTriggerXObjects = entry->mteTBoolObjects;
/*
* XXX - when firing a delta-based trigger, should
* 'mteHotValue' report the actual value sampled
* (as here), or the delta that triggered the event ?
*/
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTBoolEvOwner, entry->mteTBoolEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
} else {
vp1->index |= MTE_ARMED_BOOLEAN;
}
}
}
/*
* Only run the basic threshold tests if there's an event to
* be triggered. (Either rising or falling will do)
*/
if (( entry->mteTriggerTest & MTE_TRIGGER_THRESHOLD ) &&
((entry->mteTThRiseEvent[0] != '\0' ) ||
(entry->mteTThFallEvent[0] != '\0' ))) {
/*
* The same delta-sample validation from Boolean
* tests also applies here too.
*/
if (entry->flags & MTE_TRIGGER_FLAG_DELTA) {
vp2 = entry->old_results;
if (entry->flags & MTE_TRIGGER_FLAG_DWILD) {
dv1 = dvar;
dv2 = entry->old_deltaDs;
}
}
for ( vp1 = var; vp1; vp1=vp1->next_variable ) {
/*
* Determine the value to be monitored...
*/
if ( !vp1->val.integer ) { /* No value */
if ( vp2 )
vp2 = vp2->next_variable;
continue;
}
if (entry->flags & MTE_TRIGGER_FLAG_DELTA) {
if (entry->flags & MTE_TRIGGER_FLAG_DWILD) {
/*
* We've already checked any non-wildcarded
* discontinuity markers (inc. sysUpTime.0).
* Validate this particular sample against
* the relevant wildcarded marker...
*/
if ((dv1->type == ASN_NULL) ||
(dv1->type != dv2->type) ||
(*dv1->val.integer != *dv2->val.integer)) {
/*
* Bogus or changed discontinuity marker.
* Need to skip this sample.
*/
vp2 = vp2->next_variable;
continue;
}
}
/*
* ... and check there is a previous sample to calculate
* the delta value against (regardless of whether the
* discontinuity marker was wildcarded or not).
*/
if (vp2->type == ASN_NULL) {
vp2 = vp2->next_variable;
continue;
}
value = (*vp1->val.integer - *vp2->val.integer);
vp2 = vp2->next_variable;
} else {
value = *vp1->val.integer;
}
/*
* ... evaluate the single-value comparisons,
* and decide whether to trigger the event.
*/
cmp = vp1->index; /* working copy of 'armed' flags */
if ( value >= entry->mteTThRiseValue ) {
if (cmp & MTE_ARMED_TH_RISE ) {
cmp &= ~MTE_ARMED_TH_RISE;
cmp |= MTE_ARMED_TH_FALL;
/*
* NB: Clear the trigger armed flag even if the
* (starting) event dosn't actually fire.
* Otherwise initially true (but suppressed)
* triggers will fire on the *second* probe.
* Similarly for falling thresholds (see below).
*/
if ( entry->old_results ||
(entry->mteTThStartup & MTE_THRESH_START_RISE)) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing rising threshold test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire", "%s\n",
(entry->old_results ? "" : " (startup)")));
/*
* If no riseEvent is configured, we need still to
* set the armed flags appropriately, but there's
* no point in trying to fire the (missing) event.
*/
if (entry->mteTThRiseEvent[0] != '\0' ) {
entry->mteTriggerXOwner = entry->mteTThObjOwner;
entry->mteTriggerXObjects = entry->mteTThObjects;
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTThRiseOwner,
entry->mteTThRiseEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
}
}
if ( value <= entry->mteTThFallValue ) {
if (cmp & MTE_ARMED_TH_FALL ) {
cmp &= ~MTE_ARMED_TH_FALL;
cmp |= MTE_ARMED_TH_RISE;
/* Clear the trigger armed flag (see above) */
if ( entry->old_results ||
(entry->mteTThStartup & MTE_THRESH_START_FALL)) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing falling threshold test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire", "%s\n",
(entry->old_results ? "" : " (startup)")));
/*
* Similarly, if no fallEvent is configured,
* there's no point in trying to fire it either.
*/
if (entry->mteTThRiseEvent[0] != '\0' ) {
entry->mteTriggerXOwner = entry->mteTThObjOwner;
entry->mteTriggerXObjects = entry->mteTThObjects;
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTThFallOwner,
entry->mteTThFallEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
}
}
vp1->index = cmp;
}
}
/*
* The same processing also works for delta-threshold tests (if configured)
*/
if (( entry->mteTriggerTest & MTE_TRIGGER_THRESHOLD ) &&
((entry->mteTThDRiseEvent[0] != '\0' ) ||
(entry->mteTThDFallEvent[0] != '\0' ))) {
/*
* Delta-threshold tests can only be used with
* absolute valued samples.
*/
vp2 = entry->old_results;
if (entry->flags & MTE_TRIGGER_FLAG_DELTA) {
DEBUGMSGTL(( "disman:event:trigger",
"Delta-threshold on delta-sample\n"));
} else if ( vp2 != NULL ) {
for ( vp1 = var; vp1; vp1=vp1->next_variable ) {
/*
* Determine the value to be monitored...
* (similar to previous delta-sample processing,
* but without the discontinuity marker checks)
*/
if (!vp2) {
break; /* Run out of 'old' values */
}
if (( !vp1->val.integer ) ||
(vp2->type == ASN_NULL)) {
vp2 = vp2->next_variable;
continue;
}
value = (*vp1->val.integer - *vp2->val.integer);
vp2 = vp2->next_variable;
/*
* ... evaluate the single-value comparisons,
* and decide whether to trigger the event.
*/
cmp = vp1->index; /* working copy of 'armed' flags */
if ( value >= entry->mteTThDRiseValue ) {
if (vp1->index & MTE_ARMED_TH_DRISE ) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing rising delta threshold test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire", "\n"));
cmp &= ~MTE_ARMED_TH_DRISE;
cmp |= MTE_ARMED_TH_DFALL;
/*
* If no riseEvent is configured, we need still to
* set the armed flags appropriately, but there's
* no point in trying to fire the (missing) event.
*/
if (entry->mteTThDRiseEvent[0] != '\0' ) {
entry->mteTriggerXOwner = entry->mteTThObjOwner;
entry->mteTriggerXObjects = entry->mteTThObjects;
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTThDRiseOwner,
entry->mteTThDRiseEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
}
if ( value <= entry->mteTThDFallValue ) {
if (vp1->index & MTE_ARMED_TH_DFALL ) {
DEBUGMSGTL(( "disman:event:trigger:fire",
"Firing falling delta threshold test: "));
DEBUGMSGOID(("disman:event:trigger:fire",
vp1->name, vp1->name_length));
DEBUGMSG(( "disman:event:trigger:fire", "\n"));
cmp &= ~MTE_ARMED_TH_DFALL;
cmp |= MTE_ARMED_TH_DRISE;
/*
* Similarly, if no fallEvent is configured,
* there's no point in trying to fire it either.
*/
if (entry->mteTThDRiseEvent[0] != '\0' ) {
entry->mteTriggerXOwner = entry->mteTThObjOwner;
entry->mteTriggerXObjects = entry->mteTThObjects;
entry->mteTriggerFired = vp1;
n = entry->mteTriggerValueID_len;
mteEvent_fire(entry->mteTThDFallOwner,
entry->mteTThDFallEvent,
entry, vp1->name+n, vp1->name_length-n);
}
}
}
vp1->index = cmp;
}
}
}
/*
* Finally, rotate the results - ready for the next run.
*/
snmp_free_varbind( entry->old_results );
entry->old_results = var;
if ( entry->flags & MTE_TRIGGER_FLAG_DELTA ) {
snmp_free_varbind( entry->old_deltaDs );
entry->old_deltaDs = dvar;
entry->sysUpTime = *sysUT_var.val.integer;
}
}
void
mteTrigger_enable( struct mteTrigger *entry )
{
if (!entry)
return;
if (entry->alarm) {
/* XXX - or explicitly call mteTrigger_disable ?? */
snmp_alarm_unregister( entry->alarm );
entry->alarm = 0;
}
if (entry->mteTriggerFrequency) {
/*
* register once to run ASAP, and another to run
* at the trigger frequency
*/
snmp_alarm_register(0, 0, mteTrigger_run, entry );
entry->alarm = snmp_alarm_register(
entry->mteTriggerFrequency, SA_REPEAT,
mteTrigger_run, entry );
}
}
void
mteTrigger_disable( struct mteTrigger *entry )
{
if (!entry)
return;
if (entry->alarm) {
snmp_alarm_unregister( entry->alarm );
entry->alarm = 0;
/* XXX - perhaps release any previous results */
}
}
long _mteTrigger_MaxCount = 0;
long _mteTrigger_countEntries(void)
{
struct mteTrigger *entry;
netsnmp_tdata_row *row;
long count = 0;
for (row = netsnmp_tdata_row_first(trigger_table_data);
row;
row = netsnmp_tdata_row_next(trigger_table_data, row)) {
entry = (struct mteTrigger *)row->data;
count += entry->count;
}
return count;
}
long mteTrigger_getNumEntries(int max)
{
long count;
/* XXX - implement some form of caching ??? */
count = _mteTrigger_countEntries();
if ( count > _mteTrigger_MaxCount )
_mteTrigger_MaxCount = count;
return ( max ? _mteTrigger_MaxCount : count);
}