#include "common.h"
#include "plugin.h"
+#include <pthread.h>
+
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
char *community;
int version;
void *sess_handle;
- int16_t skip_num;
- int16_t skip_left;
+ uint32_t interval;
+ time_t next_update;
data_definition_t **data_list;
int data_list_len;
+ enum /******************************************************/
+ { /* This host.. */
+ STATE_IDLE, /* - just sits there until `next_update < interval_g' */
+ STATE_WAIT, /* - waits to be queried. */
+ STATE_BUSY /* - is currently being queried. */
+ } state; /******************************************************/
struct host_definition_s *next;
};
typedef struct host_definition_s host_definition_t;
/*
* Private variables
*/
+static int do_shutdown = 0;
+
+pthread_t *threads = NULL;
+int threads_num = 0;
+
static data_definition_t *data_head = NULL;
static host_definition_t *host_head = NULL;
+static pthread_mutex_t host_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t host_cond = PTHREAD_COND_INITIALIZER;
+
/*
* Private functions
*/
static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t *ci)
{
- int interval;
-
if ((ci->values_num != 1)
|| (ci->values[0].type != OCONFIG_TYPE_NUMBER))
{
return (-1);
}
- interval = (int) ci->values[0].value.number;
- hd->skip_num = interval;
- if (hd->skip_num < 0)
- hd->skip_num = 0;
+ hd->interval = (int) ci->values[0].value.number;
+ if (hd->interval < 0)
+ hd->interval = 0;
return (0);
} /* int csnmp_config_add_host_interval */
}
hd->sess_handle = NULL;
- hd->skip_num = 0;
- hd->skip_left = 0;
+ hd->interval = 0;
+ hd->next_update = 0;
+ hd->state = STATE_IDLE;
for (i = 0; i < ci->children_num; i++)
{
static void csnmp_host_close_session (host_definition_t *host)
{
- int status;
-
if (host->sess_handle == NULL)
return;
- status = snmp_sess_close (host->sess_handle);
-
- if (status != 0)
- {
- char *errstr = NULL;
-
- snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
-
- ERROR ("snmp plugin: snmp_sess_close failed: %s",
- (errstr == NULL) ? "Unknown problem" : errstr);
- sfree (errstr);
- }
-
+ snmp_sess_close (host->sess_handle);
host->sess_handle = NULL;
} /* void csnmp_host_close_session */
snmp_error (&sess, NULL, NULL, &errstr);
- ERROR ("snmp plugin: snmp_sess_open failed: %s",
- (errstr == NULL) ? "Unknown problem" : errstr);
+ ERROR ("snmp plugin: host %s: snmp_sess_open failed: %s",
+ host->name, (errstr == NULL) ? "Unknown problem" : errstr);
sfree (errstr);
}
} /* void csnmp_host_open_session */
-static int csnmp_init (void)
-{
- host_definition_t *host;
-
- call_snmp_init_once ();
-
- for (host = host_head; host != NULL; host = host->next)
- {
- /* We need to initialize `skip_num' here, because `interval_g' isn't
- * initialized during `configure'. */
- host->skip_left = interval_g;
- if (host->skip_num == 0)
- {
- host->skip_num = interval_g;
- }
- else if (host->skip_num < interval_g)
- {
- host->skip_num = interval_g;
- WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
- host->name, host->skip_num);
- }
-
- csnmp_host_open_session (host);
- } /* for (host) */
-
- return (0);
-} /* int csnmp_init */
-
static value_t csnmp_value_list_to_value (struct variable_list *vl, int type)
{
value_t ret;
if ((vl->type == ASN_INTEGER)
|| (vl->type == ASN_UINTEGER)
|| (vl->type == ASN_COUNTER)
+#ifdef ASN_TIMETICKS
+ || (vl->type == ASN_TIMETICKS)
+#endif
|| (vl->type == ASN_GAUGE))
{
temp = (uint32_t) *vl->val.integer;
vl.host[sizeof (vl.host) - 1] = '\0';
strcpy (vl.plugin, "snmp");
- vl.interval = host->skip_num;
+ vl.interval = host->interval;
vl.time = time (NULL);
for (instance_list_ptr = instance_list;
DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)",
host->name, data->name);
+ if (host->sess_handle == NULL)
+ {
+ DEBUG ("snmp plugin: csnmp_read_table: host->sess_handle == NULL");
+ return (-1);
+ }
+
ds = plugin_get_ds (data->type);
if (!ds)
{
for (i = 0; i < oid_list_len; i++)
snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len);
+ res = NULL;
status = snmp_sess_synch_response (host->sess_handle, req, &res);
- if (status != STAT_SUCCESS)
+ if ((status != STAT_SUCCESS) || (res == NULL))
{
char *errstr = NULL;
snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
- ERROR ("snmp plugin: snmp_sess_synch_response failed: %s",
- (errstr == NULL) ? "Unknown problem" : errstr);
+ ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+ host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
+ sfree (errstr);
csnmp_host_close_session (host);
status = -1;
vb = res->variables;
if (vb == NULL)
{
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
status = -1;
break;
}
if (snmp_oid_ncompare (data->instance.oid.oid, data->instance.oid.oid_len,
vb->name, vb->name_length,
data->instance.oid.oid_len) != 0)
+ {
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
break;
+ }
/* Allocate a new `csnmp_list_instances_t', insert the instance name and
* add it to the list */
il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
if (il == NULL)
{
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
status = -1;
break;
}
DEBUG ("snmp plugin: csnmp_read_value (host = %s, data = %s)",
host->name, data->name);
+ if (host->sess_handle == NULL)
+ {
+ DEBUG ("snmp plugin: csnmp_read_table: host->sess_handle == NULL");
+ return (-1);
+ }
+
ds = plugin_get_ds (data->type);
if (!ds)
{
strncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
- vl.interval = host->skip_num;
+ vl.interval = host->interval;
req = snmp_pdu_create (SNMP_MSG_GET);
if (req == NULL)
for (i = 0; i < data->values_len; i++)
snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len);
+
+ res = NULL;
status = snmp_sess_synch_response (host->sess_handle, req, &res);
- if (status != STAT_SUCCESS)
+ if ((status != STAT_SUCCESS) || (res == NULL))
{
char *errstr = NULL;
snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
- ERROR ("snmp plugin: snmp_sess_synch_response failed: %s",
- (errstr == NULL) ? "Unknown problem" : errstr);
- csnmp_host_close_session (host);
+ ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+ host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
sfree (errstr);
+ csnmp_host_close_session (host);
return (-1);
}
for (vb = res->variables; vb != NULL; vb = vb->next_variable)
{
+#if COLLECT_DEBUG
char buffer[1024];
snprint_variable (buffer, sizeof (buffer),
vb->name, vb->name_length, vb);
DEBUG ("snmp plugin: Got this variable: %s", buffer);
+#endif /* COLLECT_DEBUG */
for (i = 0; i < data->values_len; i++)
if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type);
} /* for (res->variables) */
- snmp_free_pdu (res);
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
DEBUG ("snmp plugin: -> plugin_dispatch_values (%s, &vl);", data->type);
plugin_dispatch_values (data->type, &vl);
static int csnmp_read_host (host_definition_t *host)
{
int i;
+ time_t time_start;
+ time_t time_end;
- DEBUG ("snmp plugin: csnmp_read_host (%s);", host->name);
+ time_start = time (NULL);
+ DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name,
+ (unsigned int) time_start);
if (host->sess_handle == NULL)
csnmp_host_open_session (host);
csnmp_read_value (host, data);
}
+ time_end = time (NULL);
+ DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name,
+ (unsigned int) time_end);
+ if ((time_end - time_start) > host->interval)
+ {
+ WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, "
+ "but reading all values takes %i seconds.",
+ host->name, host->interval, time_end - time_start);
+ }
+
return (0);
} /* int csnmp_read_host */
+static void *csnmp_read_thread (void *data)
+{
+ host_definition_t *host;
+
+ pthread_mutex_lock (&host_lock);
+ while (do_shutdown == 0)
+ {
+ pthread_cond_wait (&host_cond, &host_lock);
+
+ for (host = host_head; host != NULL; host = host->next)
+ {
+ if (do_shutdown != 0)
+ break;
+ if (host->state != STATE_WAIT)
+ continue;
+
+ host->state = STATE_BUSY;
+ pthread_mutex_unlock (&host_lock);
+ csnmp_read_host (host);
+ pthread_mutex_lock (&host_lock);
+ host->state = STATE_IDLE;
+ } /* for (host) */
+ } /* while (do_shutdown == 0) */
+ pthread_mutex_unlock (&host_lock);
+
+ pthread_exit ((void *) 0);
+ return ((void *) 0);
+} /* void *csnmp_read_thread */
+
+static int csnmp_init (void)
+{
+ host_definition_t *host;
+ int i;
+
+ if (host_head == NULL)
+ {
+ NOTICE ("snmp plugin: No host has been defined.");
+ return (-1);
+ }
+
+ call_snmp_init_once ();
+
+ threads_num = 0;
+ for (host = host_head; host != NULL; host = host->next)
+ {
+ threads_num++;
+ /* We need to initialize `interval' here, because `interval_g' isn't
+ * initialized during `configure'. */
+ host->next_update = time (NULL);
+ if (host->interval == 0)
+ {
+ host->interval = interval_g;
+ }
+ else if (host->interval < interval_g)
+ {
+ host->interval = interval_g;
+ WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
+ host->name, host->interval);
+ }
+
+ csnmp_host_open_session (host);
+ } /* for (host) */
+
+ /* Now start the reading threads */
+ if (threads_num > 3)
+ {
+ threads_num = 3 + ((threads_num - 3) / 10);
+ if (threads_num > 10)
+ threads_num = 10;
+ }
+
+ threads = (pthread_t *) malloc (threads_num * sizeof (pthread_t));
+ if (threads == NULL)
+ {
+ ERROR ("snmp plugin: malloc failed.");
+ return (-1);
+ }
+ memset (threads, '\0', threads_num * sizeof (pthread_t));
+
+ for (i = 0; i < threads_num; i++)
+ pthread_create (threads + i, NULL, csnmp_read_thread, (void *) 0);
+
+ return (0);
+} /* int csnmp_init */
+
static int csnmp_read (void)
{
host_definition_t *host;
now = time (NULL);
+ pthread_mutex_lock (&host_lock);
for (host = host_head; host != NULL; host = host->next)
{
- host->skip_left -= interval_g;
- if (host->skip_left >= interval_g)
+ if (host->state != STATE_IDLE)
continue;
- csnmp_read_host (host);
+ /* Skip this host if the next or a later iteration will be sufficient. */
+ if (host->next_update >= (now + interval_g))
+ continue;
- host->skip_left = host->skip_num;
+ host->state = STATE_WAIT;
+ host->next_update = now + host->interval;
} /* for (host) */
+ pthread_cond_broadcast (&host_cond);
+ pthread_mutex_unlock (&host_lock);
+
return (0);
} /* int csnmp_read */
+static int csnmp_shutdown (void)
+{
+ host_definition_t *host_this;
+ host_definition_t *host_next;
+
+ data_definition_t *data_this;
+ data_definition_t *data_next;
+
+ int i;
+
+ pthread_mutex_lock (&host_lock);
+ do_shutdown = 1;
+ pthread_cond_broadcast (&host_cond);
+ pthread_mutex_unlock (&host_lock);
+
+ for (i = 0; i < threads_num; i++)
+ pthread_join (threads[i], NULL);
+
+ /* Now that all the threads have exited, let's free all the global variables.
+ * This isn't really neccessary, I guess, but I think it's good stile to do
+ * so anyway. */
+ host_this = host_head;
+ host_head = NULL;
+ while (host_this != NULL)
+ {
+ host_next = host_this->next;
+
+ csnmp_host_close_session (host_this);
+
+ sfree (host_this->name);
+ sfree (host_this->address);
+ sfree (host_this->community);
+ sfree (host_this->data_list);
+ sfree (host_this);
+
+ host_this = host_next;
+ }
+
+ data_this = data_head;
+ data_head = NULL;
+ while (data_this != NULL)
+ {
+ data_next = data_this->next;
+
+ sfree (data_this->name);
+ sfree (data_this->type);
+ sfree (data_this->values);
+ sfree (data_this);
+
+ data_this = data_next;
+ }
+
+ return (0);
+} /* int csnmp_shutdown */
+
void module_register (void)
{
plugin_register_complex_config ("snmp", csnmp_config);
plugin_register_init ("snmp", csnmp_init);
plugin_register_read ("snmp", csnmp_read);
+ plugin_register_shutdown ("snmp", csnmp_shutdown);
} /* void module_register */
/*