diff options
Diffstat (limited to 'agent/mibgroup/deliver/deliverByNotify.c')
-rw-r--r-- | agent/mibgroup/deliver/deliverByNotify.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/agent/mibgroup/deliver/deliverByNotify.c b/agent/mibgroup/deliver/deliverByNotify.c new file mode 100644 index 0000000..c89879a --- /dev/null +++ b/agent/mibgroup/deliver/deliverByNotify.c @@ -0,0 +1,475 @@ +#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 <limits.h> + +netsnmp_feature_require(container_fifo) + +#include "deliverByNotify.h" + +/* we should never split beyond this */ +#define MAX_MESSAGE_COUNT 128 +#define BASE_PACKET_SIZE 100 /* should be enough to store SNMPv3 msg headers */ + +/* if v is !NULL, then estimate it's likely size */ +#define ESTIMATE_VAR_SIZE(v) (v?(v->name_length + v->val_len + 8):0) + +void parse_deliver_config(const char *, char *); +void parse_deliver_maxsize_config(const char *, char *); +void parse_data_notification_oid_config(const char *, char *); +void parse_periodic_time_oid_config(const char *, char *); +void parse_message_number_oid_config(const char *, char *); +void parse_max_message_number_oid_config(const char *, char *); +void free_deliver_config(void); + +static void _schedule_next_execute_time(void); + +oid data_notification_oid[MAX_OID_LEN] + = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 4, 0, 1 }; +size_t data_notification_oid_len = 13; + +oid netsnmp_periodic_time_oid[MAX_OID_LEN] + = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 1, 0 }; +size_t netsnmp_periodic_time_oid_len = 13; + +oid netsnmp_message_number_oid[MAX_OID_LEN] + = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 2, 0 }; +size_t netsnmp_message_number_oid_len = 13; + +oid netsnmp_max_message_number_oid[MAX_OID_LEN] + = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 3, 0 }; +size_t netsnmp_max_message_number_oid_len = 13; + +oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; + +#define DEFAULT_MAX_DELIVER_SIZE -1; +static int default_max_size; +unsigned int alarm_reg; +static netsnmp_container *deliver_container; + +static int +_deliver_compare(deliver_by_notify *lhs, deliver_by_notify *rhs) { + /* sort by the next_run time */ + if (lhs->next_run < rhs->next_run) + return -1; + else + return 1; +} + +/** Initializes the mteTrigger module */ +void +init_deliverByNotify(void) +{ + /* register the config tokens */ + snmpd_register_config_handler("deliverByNotify", + &parse_deliver_config, &free_deliver_config, + "[-p] [-m] [-s maxsize] FREQUENCY OID"); + + snmpd_register_config_handler("deliverByNotifyMaxPacketSize", + &parse_deliver_maxsize_config, NULL, + "sizeInBytes"); + + snmpd_register_config_handler("deliverByNotifyOid", + &parse_data_notification_oid_config, + NULL, /* XXX: reset to default */ + "OID"); + + snmpd_register_config_handler("deliverByNotifyFrequencyOid", + &parse_periodic_time_oid_config, + NULL, /* XXX: reset to default */ + "OID"); + + snmpd_register_config_handler("deliverByNotifyMessageNumberOid", + &parse_message_number_oid_config, + NULL, /* XXX: reset to default */ + "OID"); + + snmpd_register_config_handler("deliverByNotifyMaxMessageNumberOid", + &parse_max_message_number_oid_config, + NULL, /* XXX: reset to default */ + "OID"); + + /* create the container to store the config objects*/ + deliver_container = netsnmp_container_find("deliverByNotify:fifo"); + if (NULL == deliver_container) { + snmp_log(LOG_ERR, + "deliverByNotify: failed to initialize our data container\n"); + return; + } + deliver_container->container_name = strdup("deliverByNotify"); + deliver_container->compare = (netsnmp_container_compare *) _deliver_compare; + + /* set the defaults */ + default_max_size = DEFAULT_MAX_DELIVER_SIZE; + + alarm_reg = 0; +} + +void +_parse_config_oid(const char *token, char *line, + oid *oid_store, size_t *oid_store_len) { + size_t tmp_len = MAX_OID_LEN; + /* parse the OID given */ + + if (!snmp_parse_oid(line, oid_store, &tmp_len)) { + char buf[SPRINT_MAX_LEN]; + snprintf(buf, SPRINT_MAX_LEN-1, "unknown %s OID: %s", token, line); + config_perror(buf); + return; + } + + *oid_store_len = tmp_len; +} + +void +parse_data_notification_oid_config(const char *token, char *line) { + _parse_config_oid(token, line, + data_notification_oid, &data_notification_oid_len); +} + +void +parse_periodic_time_oid_config(const char *token, char *line) { + _parse_config_oid(token, line, + netsnmp_periodic_time_oid, &netsnmp_periodic_time_oid_len); +} + +void +parse_message_number_oid_config(const char *token, char *line) { + _parse_config_oid(token, line, + netsnmp_message_number_oid, + &netsnmp_message_number_oid_len); +} + +void +parse_max_message_number_oid_config(const char *token, char *line) { + _parse_config_oid(token, line, + netsnmp_max_message_number_oid, + &netsnmp_max_message_number_oid_len); +} + +void +parse_deliver_config(const char *token, char *line) { + const char *cp = line; + char buf[SPRINT_MAX_LEN]; + size_t buf_len = SPRINT_MAX_LEN; + int max_size = DEFAULT_MAX_DELIVER_SIZE; + int frequency; + oid target_oid[MAX_OID_LEN]; + size_t target_oid_len = MAX_OID_LEN; + deliver_by_notify *new_notify = NULL; + int flags = 0; + + while(cp && *cp == '-') { + switch (*(cp+1)) { + case 's': + cp = skip_token_const(cp); + if (!cp) { + config_perror("no argument given to -s"); + return; + } + max_size = atoi(cp); + break; + + case 'p': + flags = flags | NETSNMP_DELIVER_NO_PERIOD_OID; + break; + + case 'm': + flags = flags | NETSNMP_DELIVER_NO_MSG_COUNTS; + break; + + default: + config_perror("unknown flag"); + return; + } + cp = skip_token_const(cp); + } + + if (!cp) { + config_perror("no frequency given"); + return; + } + copy_nword(cp, buf, buf_len); + frequency = netsnmp_string_time_to_secs(buf); + cp = skip_token_const(cp); + + if (frequency <= 0) { + config_perror("illegal frequency given"); + return; + } + + if (!cp) { + config_perror("no OID given"); + return; + } + + /* parse the OID given */ + if (!snmp_parse_oid(cp, target_oid, &target_oid_len)) { + config_perror("unknown deliverByNotify OID"); + DEBUGMSGTL(("deliverByNotify", "The OID with the problem: %s\n", cp)); + return; + } + + /* set up the object to store all the data */ + new_notify = SNMP_MALLOC_TYPEDEF(deliver_by_notify); + new_notify->frequency = frequency; + new_notify->max_packet_size = max_size; + new_notify->last_run = time(NULL); + new_notify->next_run = new_notify->last_run + frequency; + new_notify->flags = flags; + + new_notify->target = malloc(target_oid_len * sizeof(oid)); + new_notify->target_len = target_oid_len; + memcpy(new_notify->target, target_oid, target_oid_len*sizeof(oid)); + + /* XXX: need to do the whole container */ + snmp_alarm_register(calculate_time_until_next_run(new_notify, NULL), 0, + &deliver_execute, NULL); + + /* add it to the container */ + CONTAINER_INSERT(deliver_container, new_notify); + _schedule_next_execute_time(); +} + +void +parse_deliver_maxsize_config(const char *token, char *line) { + default_max_size = atoi(line); +} + +static void +_free_deliver_obj(deliver_by_notify *obj, void *context) { + netsnmp_assert_or_return(obj != NULL, ); + SNMP_FREE(obj->target); + SNMP_FREE(obj); +} + +void +free_deliver_config(void) { + default_max_size = DEFAULT_MAX_DELIVER_SIZE; + CONTAINER_CLEAR(deliver_container, + (netsnmp_container_obj_func *) _free_deliver_obj, NULL); + if (alarm_reg) { + snmp_alarm_unregister(alarm_reg); + alarm_reg = 0; + } +} + +void +deliver_execute(unsigned int clientreg, void *clientarg) { + netsnmp_variable_list *vars, *walker, *deliver_notification, *vartmp; + netsnmp_variable_list *ready_for_delivery[MAX_MESSAGE_COUNT]; + netsnmp_session *sess; + int rc, i; + deliver_by_notify *obj; + netsnmp_iterator *iterator; + time_t now = time(NULL); + u_long message_count, max_message_count, tmp_long; + u_long *max_message_count_ptrs[MAX_MESSAGE_COUNT]; + size_t estimated_pkt_size; + + DEBUGMSGTL(("deliverByNotify", "Starting the execute routine\n")); + + /* XXX: need to do the whole container */ + iterator = CONTAINER_ITERATOR(deliver_container); + netsnmp_assert_or_return(iterator != NULL,); + + sess = netsnmp_query_get_default_session(); + + for(obj = ITERATOR_FIRST(iterator); obj; + obj = ITERATOR_NEXT(iterator)) { + + /* check if we need to run this one yet */ + if (obj->next_run > now) + continue; + + max_message_count = 1; + message_count = 0; + + /* fill the varbind list with the target object */ + vars = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list ); + snmp_set_var_objid( vars, obj->target, obj->target_len ); + vars->type = ASN_NULL; + + /* walk the OID tree for the data */ + rc = netsnmp_query_walk(vars, sess); + if (rc != SNMP_ERR_NOERROR) { + /* XXX: disable? and reset the next query time point! */ + snmp_log(LOG_ERR, "deliverByNotify: failed to issue the query"); + ITERATOR_RELEASE(iterator); + return; + } + + walker = vars; + + while (walker) { + + /* Set up the notification itself */ + deliver_notification = NULL; + estimated_pkt_size = BASE_PACKET_SIZE; + + /* add in the notification type */ + snmp_varlist_add_variable(&deliver_notification, + objid_snmptrap, OID_LENGTH(objid_snmptrap), + ASN_OBJECT_ID, + data_notification_oid, + data_notification_oid_len * sizeof(oid)); + estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); + + /* add in the current message number in this sequence */ + if (!(obj->flags & NETSNMP_DELIVER_NO_PERIOD_OID)) { + tmp_long = obj->frequency; + snmp_varlist_add_variable(&deliver_notification, + netsnmp_periodic_time_oid, + netsnmp_periodic_time_oid_len, + ASN_UNSIGNED, + (const void *) &tmp_long, + sizeof(tmp_long)); + estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); + } + + /* add in the current message number in this sequence */ + message_count++; + if (message_count > MAX_MESSAGE_COUNT) { + snmp_log(LOG_ERR, "delivery construct grew too large... giving up\n"); + /* XXX: disable it */ + /* XXX: send a notification about it? */ + ITERATOR_RELEASE(iterator); + return; + } + + /* store this for later updating and sending */ + ready_for_delivery[message_count-1] = deliver_notification; + + if (!(obj->flags & NETSNMP_DELIVER_NO_MSG_COUNTS)) { + snmp_varlist_add_variable(&deliver_notification, + netsnmp_message_number_oid, + netsnmp_message_number_oid_len, + ASN_UNSIGNED, + (const void *) &message_count, + sizeof(message_count)); + estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); + + /* add in the max message number count for this sequence */ + vartmp = snmp_varlist_add_variable(&deliver_notification, + netsnmp_max_message_number_oid, + netsnmp_max_message_number_oid_len, + ASN_UNSIGNED, + (const void *) &max_message_count, + sizeof(max_message_count)); + estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); + + /* we'll need to update this counter later */ + max_message_count_ptrs[message_count-1] = + (u_long *) vartmp->val.integer; + } else { + /* just to be sure */ + max_message_count_ptrs[message_count-1] = NULL; + } + + /* copy in the collected data */ + while(walker) { + snmp_varlist_add_variable(&deliver_notification, + walker->name, walker->name_length, + walker->type, + walker->val.string, walker->val_len); + + /* 8 byte padding for ASN encodings an a few extra OID bytes */ + estimated_pkt_size += ESTIMATE_VAR_SIZE(walker); + + walker = walker->next_variable; + + /* if the current size PLUS the next one (which is now + in 'walker') is greater than the limet then we stop here */ + if (obj->max_packet_size > 0 && + estimated_pkt_size + + ESTIMATE_VAR_SIZE(walker) >= + obj->max_packet_size) { + break; + } + } + + /* send out the notification */ + send_v2trap(deliver_notification); + } + + for(i = 0; i < message_count; i++) { + /* update the max count pointer */ + if (max_message_count_ptrs[i]) + *(max_message_count_ptrs[i]) = message_count; + + send_v2trap(ready_for_delivery[i]); + snmp_free_varbind(ready_for_delivery[i]); + } + + snmp_free_varbind(vars); + + /* record this as the time processed */ + /* XXX: this may creep by a few seconds when processing and maybe we want + to do the time stamp at the beginning? */ + obj->last_run = time(NULL); + } + ITERATOR_RELEASE(iterator); + + /* calculate the next time to sleep for */ + _schedule_next_execute_time(); +} + +int +calculate_time_until_next_run(deliver_by_notify *it, time_t *now) { + time_t local_now; + + /* if we weren't passed a valid time, fake it */ + if (NULL == now) { + now = &local_now; + time(&local_now); + } + + netsnmp_assert_or_return(it->last_run != 0, -1); + + /* set the timestamp for the next run */ + it->next_run = it->last_run + it->frequency; + + /* how long until the next run? */ + return it->next_run - *now; +} + +static void +_schedule_next_execute_time(void) { + time_t local_now = time(NULL); + int sleep_for = INT_MAX; + int next_time; + netsnmp_iterator *iterator; + deliver_by_notify *obj; + + DEBUGMSGTL(("deliverByNotify", "Calculating scheduling needed\n")); + + if (alarm_reg) { + snmp_alarm_unregister(alarm_reg); + alarm_reg = 0; + } + + iterator = CONTAINER_ITERATOR(deliver_container); + if (NULL == iterator) + return; + + for(obj = ITERATOR_FIRST(iterator); obj; + obj = ITERATOR_NEXT(iterator)) { + next_time = calculate_time_until_next_run(obj, &local_now); + DEBUGMSGTL(("deliverByNotify", " obj: %d (last=%d, next_run=%d)\n", next_time, obj->last_run, obj->next_run)); + if (next_time < sleep_for) + sleep_for = next_time; + } + + if (sleep_for != INT_MAX) { + if (sleep_for < 1) + sleep_for = 1; /* give at least a small pause */ + DEBUGMSGTL(("deliverByNotify", "Next execution in %d (max = %d) seconds\n", + sleep_for, INT_MAX)); + alarm_reg = snmp_alarm_register(sleep_for, 0, &deliver_execute, NULL); + } + + ITERATOR_RELEASE(iterator); +} + |