summaryrefslogtreecommitdiff
path: root/agent/mibgroup/deliver/deliverByNotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/deliver/deliverByNotify.c')
-rw-r--r--agent/mibgroup/deliver/deliverByNotify.c475
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);
+}
+