blob: c89879a4dfe715c21d99fef668e5f32bc72e3c02 [file] [log] [blame]
#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);
}