void _dump_stats( const char *name, oid *oid_buf, size_t buf_len, struct stat_table *stable ) { netsnmp_variable_list *var, *vp; struct stat_table *sp; oid stat; var = NULL; for (sp=stable; sp->entry; sp++) { oid_buf[buf_len-2] = sp->entry; snmp_varlist_add_variable( &var, oid_buf, buf_len, ASN_NULL, NULL, 0); } if (netsnmp_query_get( var, ss ) != SNMP_ERR_NOERROR) { /* Need to fix and re-try SNMPv1 errors */ snmp_free_var( var ); return; } printf("%s:\n", name); sp=stable; for (vp=var; vp; vp=vp->next_variable, sp++) { /* * Match the returned results against * the original stats table. */ stat = vp->name[buf_len-2]; while (sp->entry < stat) { sp++; if (sp->entry == 0) break; } if (sp->entry > stat) continue; /* Skip exceptions or missing values */ if ( !vp->val.integer ) continue; /* * If '-Cs' was specified twice, * then only display non-zero stats. */ if ( *vp->val.integer > 0 || sflag == 1 ) { putchar('\t'); printf(sp->description, *vp->val.integer, plural(*vp->val.integer)); putchar('\n'); } } snmp_free_varbind( var ); }
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 */ 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 ) { /* * 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->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 expObject_getData( struct expExpression *expr, struct expObject *obj ) { netsnmp_variable_list *var; int res; /* * Retrieve and store the basic object value(s) * (keeping the previous values if necessary) */ if (obj->flags & EXP_OBJ_FLAG_PREFIX ) { /* * If this is the expExpressionPrefix object, then * we already have the necessary list of values. * There's no need to retrieve it again. * This also takes care of releasing the prefix list * once the results are no longer needed. */ var = expr->pvars; } else { if (!(obj->flags & EXP_OBJ_FLAG_OWILD )) /* * Set up the request 'list' for an * exact (non-wildcarded) object. */ var = _expObject_buildList( obj->expObjectID, obj->expObjectID_len, 0, NULL ); else { if ( !expr->expPrefix_len ) { /* * You can't really have wildcarded objects unless * the expression as a whole is wildcarded too. */ return; } /* * Set up the request list for a wildcarded object */ var = _expObject_buildList( obj->expObjectID, obj->expObjectID_len, expr->expPrefix_len, expr->pvars ); } res = netsnmp_query_get( var, expr->session ); } if ( obj->expObjectSampleType != EXPSAMPLETYPE_ABSOLUTE ) { /* * For Delta (and Changed) samples, we need * to store the previous value as well. */ if ( obj->old_vars ) snmp_free_varbind( obj->old_vars ); obj->old_vars = obj->vars; } else snmp_free_varbind( obj->vars ); obj->vars = var; /* * For Delta samples, there may be a discontinuity marker * (or set of wildcarded markers) to be sampled as well. * This necessarily requires storing the previous marker(s). */ if (( obj->expObjectSampleType != EXPSAMPLETYPE_ABSOLUTE ) && ( obj->flags & EXP_OBJ_FLAG_DDISC )) { if ( obj->flags & EXP_OBJ_FLAG_DWILD ) var = _expObject_buildList( obj->expObjDeltaD, obj->expObjDeltaD_len, expr->expPrefix_len, expr->pvars ); else var = _expObject_buildList( obj->expObjDeltaD, obj->expObjDeltaD_len, 0, NULL ); res = netsnmp_query_get( var, expr->session ); if ( obj->old_dvars ) snmp_free_varbind( obj->old_dvars ); obj->old_dvars = obj->dvars; obj->dvars = var; } /* * If there's an expObjectConditional value specified * (or set of wildcarded values) then add these to the * ever-growing collection of retrieved values. */ if ( obj->expObjCond_len ) { if ( obj->flags & EXP_OBJ_FLAG_CWILD ) var = _expObject_buildList( obj->expObjCond, obj->expObjCond_len, expr->expPrefix_len, expr->pvars ); else var = _expObject_buildList( obj->expObjCond, obj->expObjCond_len, 0, NULL ); /* * XXX - Check when to use GetNext * * (The MIB description seems bogus?) */ res = netsnmp_query_get( var, expr->session ); if ( obj->cvars ) snmp_free_varbind( obj->cvars ); obj->cvars = var; } }
int _mteEvent_fire_notify (struct mteEvent *entry, /* The event to fire */ struct mteTrigger *trigger, /* Trigger that fired */ oid * suffix, size_t sfx_len) /* Matching instance */ { netsnmp_variable_list *var, *v2; extern const oid snmptrap_oid[]; extern const size_t snmptrap_oid_len; netsnmp_session *s; /* * The Event-MIB specification says that objects from the * mteEventTable should come after those from the trigger, * but things actually work better if these come first. * Allow the agent to be configured either way. */ int strictOrdering = netsnmp_ds_get_boolean (NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_STRICT_DISMAN); var = (netsnmp_variable_list *) SNMP_MALLOC_TYPEDEF (netsnmp_variable_list); if (!var) return -1; /* * Set the basic notification OID... */ memset (var, 0, sizeof (netsnmp_variable_list)); snmp_set_var_objid (var, snmptrap_oid, snmptrap_oid_len); snmp_set_var_typed_value (var, ASN_OBJECT_ID, (u_char *) entry->mteNotification, entry->mteNotification_len * sizeof (oid)); /* * ... then add the specified objects from the Objects Table. * * Strictly speaking, the objects from the EventTable are meant * to be listed last (after the various trigger objects). * But logically things actually work better if the event objects * are placed first. So this code handles things either way :-) */ if (!strictOrdering) { DEBUGMSGTL (("disman:event:fire", "Adding event objects (first)\n")); if (strcmp (entry->mteNotifyOwner, "_snmpd") != 0) mteObjects_vblist (var, entry->mteNotifyOwner, entry->mteNotifyObjects, suffix, sfx_len); } DEBUGMSGTL (("disman:event:fire", "Adding trigger objects (general)\n")); mteObjects_vblist (var, trigger->mteTriggerOOwner, trigger->mteTriggerObjects, suffix, sfx_len); DEBUGMSGTL (("disman:event:fire", "Adding trigger objects (specific)\n")); mteObjects_vblist (var, trigger->mteTriggerXOwner, trigger->mteTriggerXObjects, suffix, sfx_len); if (strictOrdering) { DEBUGMSGTL (("disman:event:fire", "Adding event objects (last)\n")); if (strcmp (entry->mteNotifyOwner, "_snmpd") != 0) mteObjects_vblist (var, entry->mteNotifyOwner, entry->mteNotifyObjects, suffix, sfx_len); } /* * Query the agent to retrieve the necessary values... * (skipping the initial snmpTrapOID varbind) */ v2 = var->next_variable; if (entry->session) s = entry->session; else s = trigger->session; netsnmp_query_get (v2, s); /* * ... add any "internal" objects... * (skipped by the processing above, and best handled directly) */ if (strcmp (entry->mteNotifyOwner, "_snmpd") == 0) { DEBUGMSGTL (("disman:event:fire", "Adding event objects (internal)\n")); if (!strictOrdering) { mteObjects_internal_vblist (var, entry->mteNotifyObjects, trigger, s); } else { for (v2 = var; v2 && v2->next_variable; v2 = v2->next_variable) ; mteObjects_internal_vblist (v2, entry->mteNotifyObjects, trigger, s); } } /* * ... and send the resulting varbind list as a notification */ send_v2trap (var); snmp_free_varbind (var); return 0; }