| /* |
| * DisMan Event MIB: |
| * Core implementation of the event handling behaviour |
| */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include "disman/event/mteEvent.h" |
| #include "disman/event/mteTrigger.h" |
| #include "disman/event/mteObjects.h" |
| |
| netsnmp_tdata *event_table_data; |
| |
| /* |
| * Initialize the container for the (combined) mteEvent*Table, |
| * regardless of which table initialisation routine is called first. |
| */ |
| |
| void |
| init_event_table_data(void) |
| { |
| DEBUGMSGTL(("disman:event:init", "init event container\n")); |
| if (!event_table_data) { |
| event_table_data = netsnmp_tdata_create_table("mteEventTable", 0); |
| DEBUGMSGTL(("disman:event:init", "create event container (%x)\n", |
| event_table_data)); |
| } |
| } |
| |
| void _init_default_mteEvent( const char *event, const char *oname, int specific ); |
| void _init_link_mteEvent( const char *event, const char *oname, int specific ); |
| void _init_builtin_mteEvent( const char *event, const char *oname, |
| oid *trapOID, size_t trapOID_len ); |
| |
| |
| /** Initializes the mteEvent module */ |
| void |
| init_mteEvent(void) |
| { |
| static int _defaults_init = 0; |
| init_event_table_data(); |
| |
| /* |
| * Insert fixed events for the default trigger notifications |
| * |
| * NB: internal events (with an owner of "_snmpd") will not in |
| * fact refer to the mteObjectsTable for the payload varbinds. |
| * The routine mteObjects_internal_vblist() hardcodes the |
| * appropriate varbinds for these internal events. |
| * This routine will need to be updated whenever a new |
| * internal event is added. |
| */ |
| if ( _defaults_init) |
| return; |
| |
| _init_default_mteEvent( "mteTriggerFired", "_triggerFire", 1 ); |
| _init_default_mteEvent( "mteTriggerRising", "_triggerFire", 2 ); |
| _init_default_mteEvent( "mteTriggerFalling", "_triggerFire", 3 ); |
| _init_default_mteEvent( "mteTriggerFailure", "_triggerFail", 4 ); |
| |
| _init_link_mteEvent( "linkDown", "_linkUpDown", 3 ); |
| _init_link_mteEvent( "linkUp", "_linkUpDown", 4 ); |
| _defaults_init = 1; |
| } |
| |
| void |
| _init_builtin_mteEvent( const char *event, const char *oname, oid *trapOID, size_t trapOID_len ) |
| { |
| char ename[ MTE_STR1_LEN+1 ]; |
| netsnmp_tdata_row *row; |
| struct mteEvent *entry; |
| |
| memset(ename, 0, sizeof(ename)); |
| ename[0] = '_'; |
| memcpy(ename+1, event, strlen(event)); |
| |
| row = mteEvent_createEntry( "_snmpd", ename, 1 ); |
| if (!row || !row->data) |
| return; |
| entry = (struct mteEvent *)row->data; |
| |
| entry->mteEventActions = MTE_EVENT_NOTIFICATION; |
| entry->mteNotification_len = trapOID_len; |
| memcpy( entry->mteNotification, trapOID, trapOID_len*sizeof(oid)); |
| memcpy( entry->mteNotifyOwner, "_snmpd", 6 ); |
| memcpy( entry->mteNotifyObjects, oname, strlen(oname)); |
| entry->flags |= MTE_EVENT_FLAG_ENABLED| |
| MTE_EVENT_FLAG_ACTIVE| |
| MTE_EVENT_FLAG_VALID; |
| } |
| |
| void |
| _init_default_mteEvent( const char *event, const char *oname, int specific ) |
| { |
| oid mteTrapOID[] = {1, 3, 6, 1, 2, 1, 88, 2, 0, 99 /* placeholder */}; |
| size_t mteTrapOID_len = OID_LENGTH(mteTrapOID); |
| |
| mteTrapOID[ mteTrapOID_len-1 ] = specific; |
| _init_builtin_mteEvent( event, oname, mteTrapOID, mteTrapOID_len ); |
| } |
| |
| |
| void |
| _init_link_mteEvent( const char *event, const char *oname, int specific ) |
| { |
| oid mteTrapOID[] = {1, 3, 6, 1, 6, 3, 1, 1, 5, 99 /* placeholder */}; |
| size_t mteTrapOID_len = OID_LENGTH(mteTrapOID); |
| |
| mteTrapOID[ mteTrapOID_len-1 ] = specific; |
| _init_builtin_mteEvent( event, oname, mteTrapOID, mteTrapOID_len ); |
| } |
| |
| |
| /* =================================================== |
| * |
| * APIs for maintaining the contents of the (combined) |
| * mteEvent*Table container. |
| * |
| * =================================================== */ |
| |
| void |
| _mteEvent_dump(void) |
| { |
| struct mteEvent *entry; |
| netsnmp_tdata_row *row; |
| int i = 0; |
| |
| for (row = netsnmp_tdata_row_first(event_table_data); |
| row; |
| row = netsnmp_tdata_row_next(event_table_data, row)) { |
| entry = (struct mteEvent *)row->data; |
| DEBUGMSGTL(("disman:event:dump", "EventTable 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", ": %x, %x\n", row, entry)); |
| i++; |
| } |
| DEBUGMSGTL(("disman:event:dump", "EventTable %d entries\n", i)); |
| } |
| |
| |
| /* |
| * Create a new row in the event table |
| */ |
| netsnmp_tdata_row * |
| mteEvent_createEntry(const char *mteOwner, const char *mteEName, int fixed) |
| { |
| struct mteEvent *entry; |
| netsnmp_tdata_row *row; |
| size_t mteOwner_len = (mteOwner) ? strlen(mteOwner) : 0; |
| size_t mteEName_len = (mteEName) ? strlen(mteEName) : 0; |
| |
| DEBUGMSGTL(("disman:event:table", "Create event entry (%s, %s)\n", |
| mteOwner, mteEName)); |
| /* |
| * Create the mteEvent entry, and the |
| * (table-independent) row wrapper structure... |
| */ |
| entry = SNMP_MALLOC_TYPEDEF(struct mteEvent); |
| 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 (mteEName) |
| memcpy(entry->mteEName, mteEName, mteEName_len); |
| netsnmp_table_row_add_index(row, ASN_PRIV_IMPLIED_OCTET_STR, |
| entry->mteEName, mteEName_len); |
| |
| entry->mteNotification_len = 2; /* .0.0 */ |
| if (fixed) |
| entry->flags |= MTE_EVENT_FLAG_FIXED; |
| |
| /* |
| * ... and insert the row into the (common) table container |
| */ |
| netsnmp_tdata_add_row(event_table_data, row); |
| DEBUGMSGTL(("disman:event:table", "Event entry created\n")); |
| return row; |
| } |
| |
| |
| /* |
| * Remove a row from the event table |
| */ |
| void |
| mteEvent_removeEntry(netsnmp_tdata_row *row) |
| { |
| struct mteEvent *entry; |
| |
| if (!row) |
| return; /* Nothing to remove */ |
| entry = (struct mteEvent *) |
| netsnmp_tdata_remove_and_delete_row(event_table_data, row); |
| if (entry) |
| SNMP_FREE(entry); |
| } |
| |
| /* =================================================== |
| * |
| * APIs for processing the firing of an event |
| * |
| * =================================================== */ |
| |
| int |
| _mteEvent_fire_notify( struct mteEvent *event, |
| struct mteTrigger *trigger, |
| oid *suffix, size_t sfx_len ); |
| int |
| _mteEvent_fire_set( struct mteEvent *event, |
| struct mteTrigger *trigger, |
| oid *suffix, size_t sfx_len ); |
| |
| int |
| mteEvent_fire( char *owner, char *event, /* Event to invoke */ |
| struct mteTrigger *trigger, /* Trigger that fired */ |
| oid *suffix, size_t s_len ) /* Matching instance */ |
| { |
| struct mteEvent *entry; |
| int fired = 0; |
| netsnmp_variable_list owner_var, event_var; |
| |
| DEBUGMSGTL(("disman:event:fire", "Event fired (%s, %s)\n", |
| owner, event)); |
| |
| /* |
| * Retrieve the entry for the specified event |
| */ |
| memset( &owner_var, 0, sizeof(owner_var)); |
| memset( &event_var, 0, sizeof(event_var)); |
| snmp_set_var_typed_value(&owner_var, ASN_OCTET_STR, owner, strlen(owner)); |
| snmp_set_var_typed_value(&event_var, ASN_PRIV_IMPLIED_OCTET_STR, |
| event, strlen(event)); |
| owner_var.next_variable = &event_var; |
| entry = (struct mteEvent *) |
| netsnmp_tdata_row_entry( |
| netsnmp_tdata_row_get_byidx( event_table_data, &owner_var )); |
| if (!entry) { |
| DEBUGMSGTL(("disman:event:fire", "No matching event\n")); |
| return -1; |
| } |
| |
| if (entry->mteEventActions & MTE_EVENT_NOTIFICATION) { |
| DEBUGMSGTL(("disman:event:fire", "Firing notification event\n")); |
| _mteEvent_fire_notify( entry, trigger, suffix, s_len ); |
| fired = 1; |
| } |
| if (entry->mteEventActions & MTE_EVENT_SET) { |
| DEBUGMSGTL(("disman:event:fire", "Firing set event\n")); |
| _mteEvent_fire_set( entry, trigger, suffix, s_len ); |
| fired = 1; |
| } |
| |
| if (!fired) |
| DEBUGMSGTL(("disman:event:fire", "Matched event is empty\n")); |
| |
| return fired; |
| } |
| |
| |
| #ifdef __NOT_NEEDED |
| void |
| _insert_internal_objects( netsnmp_variable_list *vblist, char *oname, |
| struct mteTrigger *trigger) |
| { |
| netsnmp_variable_list *var = NULL, *vp; |
| oid mteHotTrigger[] = {1, 3, 6, 1, 2, 1, 88, 2, 1, 1, 0}; |
| oid mteHotTarget[] = {1, 3, 6, 1, 2, 1, 88, 2, 1, 2, 0}; |
| oid mteHotContext[] = {1, 3, 6, 1, 2, 1, 88, 2, 1, 3, 0}; |
| oid mteHotOID[] = {1, 3, 6, 1, 2, 1, 88, 2, 1, 4, 0}; |
| oid mteHotValue[] = {1, 3, 6, 1, 2, 1, 88, 2, 1, 5, 0}; |
| |
| /* |
| * Construct the varbinds for this (internal) event... |
| */ |
| if ((!strcmp(oname, "_mteTriggerFired" )) || |
| (!strcmp(oname, "_mteTriggerRising" )) || |
| (!strcmp(oname, "_mteTriggerFalling")) || |
| (!strcmp(oname, "_triggerFire"))) { |
| |
| snmp_varlist_add_variable( &var, |
| mteHotTrigger, OID_LENGTH(mteHotTrigger), |
| ASN_OCTET_STR, trigger->mteTName, |
| strlen(trigger->mteTName)); |
| snmp_varlist_add_variable( &var, |
| mteHotTarget, OID_LENGTH(mteHotTarget), |
| ASN_OCTET_STR, trigger->mteTriggerTarget, |
| strlen(trigger->mteTriggerTarget)); |
| snmp_varlist_add_variable( &var, |
| mteHotContext, OID_LENGTH(mteHotContext), |
| ASN_OCTET_STR, trigger->mteTriggerContext, |
| strlen(trigger->mteTriggerContext)); |
| snmp_varlist_add_variable( &var, |
| mteHotOID, OID_LENGTH(mteHotOID), |
| ASN_OBJECT_ID, (char *)trigger->mteTriggerFired->name, |
| trigger->mteTriggerFired->name_length*sizeof(oid)); |
| snmp_varlist_add_variable( &var, |
| mteHotValue, OID_LENGTH(mteHotValue), |
| trigger->mteTriggerFired->type, |
| trigger->mteTriggerFired->val.string, |
| trigger->mteTriggerFired->val_len); |
| } else { |
| DEBUGMSGTL(("disman:event:fire", |
| "Unknown internal objects tag (%s)\n", oname)); |
| return; |
| } |
| |
| /* |
| * ... and insert them into the main varbind list |
| * (at the point specified) |
| */ |
| for (vp = var; vp && vp->next_variable; vp=vp->next_variable) |
| ; |
| vp->next_variable = vblist->next_variable; |
| vblist->next_variable = var; |
| } |
| #endif |
| |
| 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; |
| oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 }; |
| size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid); |
| 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; |
| } |
| |
| |
| int |
| _mteEvent_fire_set( 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; |
| oid set_oid[ MAX_OID_LEN ]; |
| size_t set_len; |
| |
| /* |
| * Set the basic assignment OID... |
| */ |
| memset(set_oid, 0, sizeof(set_oid)); |
| memcpy(set_oid, entry->mteSetOID, entry->mteSetOID_len*sizeof(oid)); |
| set_len = entry->mteSetOID_len; |
| |
| /* |
| * ... if the trigger value is wildcarded (sfx_len > 0), |
| * *and* the SET event entry is wildcarded, |
| * then add the supplied instance suffix... |
| */ |
| if (sfx_len && |
| entry->flags & MTE_SET_FLAG_OBJWILD) { |
| memcpy( &set_oid[set_len], suffix, sfx_len*sizeof(oid)); |
| set_len += sfx_len; |
| } |
| |
| /* |
| * ... finally build the assignment varbind, |
| * and pass it to be acted on. |
| * |
| * XXX: Need to handle (remote) targets and non-default contexts |
| */ |
| memset( &var, 0, sizeof(var)); |
| snmp_set_var_objid( &var, set_oid, set_len ); |
| snmp_set_var_typed_integer( &var, ASN_INTEGER, entry->mteSetValue ); |
| if (entry->session) |
| return netsnmp_query_set( &var, entry->session ); |
| else |
| return netsnmp_query_set( &var, trigger->session ); |
| |
| /* XXX - Need to check result */ |
| } |
| |