X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fsnmp.c;h=d19493df9a53d5e4aefee9190cdc36cc6396afb3;hb=10075e6fe3c384b73c2dd398a1435f8d10e56654;hp=ea9bd43ea52f78891c9e1e70ad55333d88856c8a;hpb=10dc7025709caaab852de76f161a47f4c4a443d1;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index ea9bd43e..d19493df 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -23,6 +23,8 @@ #include "common.h" #include "plugin.h" +#include + #include #include @@ -49,8 +51,11 @@ struct data_definition_s char *type; /* used to find the data_set */ int is_table; instance_t instance; + char *instance_prefix; oid_t *values; int values_len; + double scale; + double shift; struct data_definition_s *next; }; typedef struct data_definition_s data_definition_t; @@ -62,10 +67,16 @@ struct host_definition_s 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; @@ -91,9 +102,17 @@ typedef struct csnmp_table_values_s csnmp_table_values_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 */ @@ -108,6 +127,7 @@ static host_definition_t *host_head = NULL; * ! +-> csnmp_config_add_data_type * ! +-> csnmp_config_add_data_table * ! +-> csnmp_config_add_data_instance + * ! +-> csnmp_config_add_data_instance_prefix * ! +-> csnmp_config_add_data_values * +-> csnmp_config_add_host * +-> csnmp_config_add_host_address @@ -133,9 +153,7 @@ static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci return (-1); } - if (dd->type != NULL) - free (dd->type); - + sfree (dd->type); dd->type = strdup (ci->values[0].value.string); if (dd->type == NULL) return (-1); @@ -180,12 +198,37 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t else { /* Instance is a simple string */ - strncpy (dd->instance.string, ci->values[0].value.string, DATA_MAX_NAME_LEN - 1); + sstrncpy (dd->instance.string, ci->values[0].value.string, + sizeof (dd->instance.string)); } return (0); } /* int csnmp_config_add_data_instance */ +static int csnmp_config_add_data_instance_prefix (data_definition_t *dd, + oconfig_item_t *ci) +{ + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument."); + return (-1); + } + + if (!dd->is_table) + { + WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' " + "is set to `false'.", dd->name); + return (-1); + } + + sfree (dd->instance_prefix); + dd->instance_prefix = strdup (ci->values[0].value.string); + if (dd->instance_prefix == NULL) + return (-1); + + return (0); +} /* int csnmp_config_add_data_instance_prefix */ + static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci) { int i; @@ -203,8 +246,8 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t * return (-1); } - if (dd->values != NULL) - free (dd->values); + sfree (dd->values); + dd->values_len = 0; dd->values = (oid_t *) malloc (sizeof (oid_t) * ci->values_num); if (dd->values == NULL) return (-1); @@ -229,6 +272,34 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t * return (0); } /* int csnmp_config_add_data_instance */ +static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument."); + return (-1); + } + + dd->shift = ci->values[0].value.number; + + return (0); +} /* int csnmp_config_add_data_shift */ + +static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument."); + return (-1); + } + + dd->scale = ci->values[0].value.number; + + return (0); +} /* int csnmp_config_add_data_scale */ + static int csnmp_config_add_data (oconfig_item_t *ci) { data_definition_t *dd; @@ -253,6 +324,8 @@ static int csnmp_config_add_data (oconfig_item_t *ci) free (dd); return (-1); } + dd->scale = 1.0; + dd->shift = 0.0; for (i = 0; i < ci->children_num; i++) { @@ -265,8 +338,14 @@ static int csnmp_config_add_data (oconfig_item_t *ci) status = csnmp_config_add_data_table (dd, option); else if (strcasecmp ("Instance", option->key) == 0) status = csnmp_config_add_data_instance (dd, option); + else if (strcasecmp ("InstancePrefix", option->key) == 0) + status = csnmp_config_add_data_instance_prefix (dd, option); else if (strcasecmp ("Values", option->key) == 0) status = csnmp_config_add_data_values (dd, option); + else if (strcasecmp ("Shift", option->key) == 0) + status = csnmp_config_add_data_shift (dd, option); + else if (strcasecmp ("Scale", option->key) == 0) + status = csnmp_config_add_data_scale (dd, option); else { WARNING ("snmp plugin: Option `%s' not allowed here.", option->key); @@ -298,6 +377,7 @@ static int csnmp_config_add_data (oconfig_item_t *ci) if (status != 0) { sfree (dd->name); + sfree (dd->instance_prefix); sfree (dd->values); sfree (dd); return (-1); @@ -440,8 +520,6 @@ static int csnmp_config_add_host_collect (host_definition_t *host, 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)) { @@ -449,10 +527,9 @@ static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t 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 */ @@ -483,8 +560,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci) } 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++) { @@ -579,24 +657,10 @@ static int csnmp_config (oconfig_item_t *ci) 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 */ @@ -622,41 +686,14 @@ static void csnmp_host_open_session (host_definition_t *host) 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) +static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, + double scale, double shift) { value_t ret; uint64_t temp = 0; @@ -665,6 +702,9 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type) 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; @@ -691,12 +731,141 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type) { ret.gauge = NAN; if (defined != 0) - ret.gauge = temp; + ret.gauge = (scale * temp) + shift; } return (ret); } /* value_t csnmp_value_list_to_value */ +/* Returns true if all OIDs have left their subtree */ +static int csnmp_check_res_left_subtree (const host_definition_t *host, + const data_definition_t *data, + struct snmp_pdu *res) +{ + struct variable_list *vb; + int num_checked; + int num_left_subtree; + int i; + + vb = res->variables; + if (vb == NULL) + return (-1); + + num_checked = 0; + num_left_subtree = 0; + + /* check all the variables and count how many have left their subtree */ + for (vb = res->variables, i = 0; + (vb != NULL) && (i < data->values_len); + vb = vb->next_variable, i++) + { + num_checked++; + if (snmp_oid_ncompare (data->values[i].oid, + data->values[i].oid_len, + vb->name, vb->name_length, + data->values[i].oid_len) != 0) + num_left_subtree++; + } + + /* check if enough variables have been returned */ + if (i < data->values_len) + { + ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i", + host->name, data->values_len, i); + return (-1); + } + + if (data->instance.oid.oid_len > 0) + { + if (vb == NULL) + { + ERROR ("snmp plugin: host %s: Expected one more variable for " + "the instance..", host->name); + return (-1); + } + + num_checked++; + if (snmp_oid_ncompare (data->instance.oid.oid, + data->instance.oid.oid_len, + vb->name, vb->name_length, + data->instance.oid.oid_len) != 0) + num_left_subtree++; + } + + DEBUG ("snmp plugin: csnmp_check_res_left_subtree: %i of %i variables have " + "left their subtree", + num_left_subtree, num_checked); + if (num_left_subtree >= num_checked) + return (1); + return (0); +} /* int csnmp_check_res_left_subtree */ + +static int csnmp_instance_list_add (csnmp_list_instances_t **head, + csnmp_list_instances_t **tail, + const struct snmp_pdu *res) +{ + csnmp_list_instances_t *il; + struct variable_list *vb; + + /* Set vb on the last variable */ + for (vb = res->variables; + (vb != NULL) && (vb->next_variable != NULL); + vb = vb->next_variable) + /* do nothing */; + if (vb == NULL) + return (-1); + + il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t)); + if (il == NULL) + { + ERROR ("snmp plugin: malloc failed."); + return (-1); + } + il->subid = vb->name[vb->name_length - 1]; + il->next = NULL; + + /* Get instance name */ + if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR)) + { + char *ptr; + size_t instance_len; + + instance_len = sizeof (il->instance) - 1; + if (instance_len > vb->val_len) + instance_len = vb->val_len; + + sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR) + ? vb->val.string + : vb->val.bitstring), + instance_len); + + for (ptr = il->instance; *ptr != '\0'; ptr++) + { + if ((*ptr > 0) && (*ptr < 32)) + *ptr = ' '; + else if (*ptr == '/') + *ptr = '_'; + } + DEBUG ("snmp plugin: il->instance = `%s';", il->instance); + } + else + { + value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0); + ssnprintf (il->instance, sizeof (il->instance), + "%llu", val.counter); + } + + /* TODO: Debugging output */ + + if (*head == NULL) + *head = il; + else + (*tail)->next = il; + *tail = il; + + return (0); +} /* int csnmp_instance_list_add */ + static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *data, csnmp_list_instances_t *instance_list, csnmp_table_values_t **value_table) @@ -708,6 +877,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat csnmp_table_values_t **value_table_ptr; int i; + oid subid; + int have_more; ds = plugin_get_ds (data->type); if (!ds) @@ -717,6 +888,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat } assert (ds->ds_num == data->values_len); + instance_list_ptr = instance_list; + value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *) * data->values_len); if (value_table_ptr == NULL) @@ -728,47 +901,100 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len); if (vl.values == NULL) { + ERROR ("snmp plugin: malloc failed."); sfree (value_table_ptr); return (-1); } - strncpy (vl.host, host->name, sizeof (vl.host)); - vl.host[sizeof (vl.host) - 1] = '\0'; - strcpy (vl.plugin, "snmp"); + sstrncpy (vl.host, host->name, sizeof (vl.host)); + sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin)); - vl.interval = host->skip_num; + vl.interval = host->interval; vl.time = time (NULL); - for (instance_list_ptr = instance_list; - instance_list_ptr != NULL; - instance_list_ptr = instance_list_ptr->next) + subid = 0; + have_more = 1; + + while (have_more != 0) { - strncpy (vl.type_instance, instance_list_ptr->instance, sizeof (vl.type_instance)); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + if (instance_list != NULL) + { + while ((instance_list_ptr != NULL) + && (instance_list_ptr->subid < subid)) + instance_list_ptr = instance_list_ptr->next; + + if (instance_list_ptr == NULL) + { + have_more = 0; + continue; + } + else if (instance_list_ptr->subid > subid) + { + subid = instance_list_ptr->subid; + continue; + } + } /* if (instance_list != NULL) */ for (i = 0; i < data->values_len; i++) { while ((value_table_ptr[i] != NULL) - && (value_table_ptr[i]->subid < instance_list_ptr->subid)) + && (value_table_ptr[i]->subid < subid)) value_table_ptr[i] = value_table_ptr[i]->next; - if ((value_table_ptr[i] == NULL) - || (value_table_ptr[i]->subid != instance_list_ptr->subid)) - break; - vl.values[i] = value_table_ptr[i]->value; - } /* for (data->values_len) */ - /* If the for-loop was aborted early, not all subid's match. */ + if (value_table_ptr[i] == NULL) + { + have_more = 0; + break; + } + else if (value_table_ptr[i]->subid > subid) + { + subid = value_table_ptr[i]->subid; + break; + } + } /* for (i = 0; i < columns; i++) */ + /* The subid has been increased - start scanning from the beginning + * again.. */ if (i < data->values_len) - { - DEBUG ("snmp plugin: host = %s; data = %s; i = %i; " - "Skipping SUBID %i", - host->name, data->name, i, instance_list_ptr->subid); continue; + + /* if we reach this line, all value_table_ptr[i] are non-NULL and are set + * to the same subid. instance_list_ptr is either NULL or points to the + * same subid, too. */ +#if COLLECT_DEBUG + for (i = 1; i < data->values_len; i++) + { + assert (value_table_ptr[i] != NULL); + assert (value_table_ptr[i-1]->subid == value_table_ptr[i]->subid); + } + assert ((instance_list_ptr == NULL) + || (instance_list_ptr->subid == value_table_ptr[0]->subid)); +#endif + + sstrncpy (vl.type, data->type, sizeof (vl.type)); + + { + char temp[DATA_MAX_NAME_LEN]; + + if (instance_list_ptr == NULL) + ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid); + else + sstrncpy (temp, instance_list_ptr->instance, sizeof (temp)); + + if (data->instance_prefix == NULL) + sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance)); + else + ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s", + data->instance_prefix, temp); } + for (i = 0; i < data->values_len; i++) + vl.values[i] = value_table_ptr[i]->value; + /* If we get here `vl.type_instance' and all `vl.values' have been set */ - plugin_dispatch_values (data->type, &vl); - } /* for (instance_list) */ + plugin_dispatch_values (&vl); + + subid++; + } /* while (have_more != 0) */ sfree (vl.values); sfree (value_table_ptr); @@ -800,6 +1026,12 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) 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) { @@ -818,16 +1050,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) oid_list_len = data->values_len + 1; oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len)); if (oid_list == NULL) + { + ERROR ("snmp plugin: csnmp_read_table: malloc failed."); return (-1); - memcpy (oid_list, &data->instance.oid, sizeof (oid_t)); - for (i = 0; i < data->values_len; i++) - memcpy (oid_list + (i + 1), data->values + i, sizeof (oid_t)); + } + memcpy (oid_list, data->values, data->values_len * sizeof (oid_t)); + if (data->instance.oid.oid_len > 0) + memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t)); + else + oid_list_len--; /* Allocate the `value_table' */ value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *) * 2 * data->values_len); if (value_table == NULL) { + ERROR ("snmp plugin: csnmp_read_table: malloc failed."); sfree (oid_list); return (-1); } @@ -840,8 +1078,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) status = 0; while (status == 0) { - csnmp_list_instances_t *il; - req = snmp_pdu_create (SNMP_MSG_GETNEXT); if (req == NULL) { @@ -853,15 +1089,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) 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; @@ -873,83 +1116,61 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) vb = res->variables; if (vb == NULL) { + if (res != NULL) + snmp_free_pdu (res); + res = NULL; + status = -1; break; } - /* Check if we left the subtree */ - if (snmp_oid_ncompare (data->instance.oid.oid, data->instance.oid.oid_len, - vb->name, vb->name_length, - data->instance.oid.oid_len) != 0) - 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) + /* Check if all values (and possibly the instance) have left their + * subtree */ + if (csnmp_check_res_left_subtree (host, data, res) != 0) { - status = -1; + if (res != NULL) + snmp_free_pdu (res); + res = NULL; + break; } - il->subid = vb->name[vb->name_length - 1]; - il->next = NULL; - /* Get instance name */ - if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR)) + /* if an instance-OID is configured.. */ + if (data->instance.oid.oid_len > 0) { - char *ptr; - size_t instance_len; - - instance_len = sizeof (il->instance) - 1; - if (instance_len > vb->val_len) - instance_len = vb->val_len; - - strncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR) - ? vb->val.string - : vb->val.bitstring), - instance_len); - il->instance[instance_len] = '\0'; - - for (ptr = il->instance; *ptr != '\0'; ptr++) + /* Allocate a new `csnmp_list_instances_t', insert the instance name and + * add it to the list */ + if (csnmp_instance_list_add (&instance_list, &instance_list_ptr, + res) != 0) { - if ((*ptr > 0) && (*ptr < 32)) - *ptr = ' '; - else if (*ptr == '/') - *ptr = '_'; + ERROR ("snmp plugin: csnmp_instance_list_add failed."); + status = -1; + break; } - DEBUG ("snmp plugin: il->instance = `%s';", il->instance); - } - else - { - value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER); - snprintf (il->instance, sizeof (il->instance), - "%llu", val.counter); - } - il->instance[sizeof (il->instance) - 1] = '\0'; - DEBUG ("snmp plugin: data = `%s'; il->instance = `%s';", - data->name, il->instance); - - if (instance_list_ptr == NULL) - instance_list = il; - else - instance_list_ptr->next = il; - instance_list_ptr = il; - - /* Copy OID to oid_list[0] */ - memcpy (oid_list[0].oid, vb->name, sizeof (oid) * vb->name_length); - oid_list[0].oid_len = vb->name_length; - for (i = 0; i < data->values_len; i++) - { - csnmp_table_values_t *vt; - - vb = vb->next_variable; + /* Set vb on the last variable */ + for (vb = res->variables; + (vb != NULL) && (vb->next_variable != NULL); + vb = vb->next_variable) + /* do nothing */; if (vb == NULL) { status = -1; break; } + /* Copy OID to oid_list[data->values_len] */ + memcpy (oid_list[data->values_len].oid, vb->name, + sizeof (oid) * vb->name_length); + oid_list[data->values_len].oid_len = vb->name_length; + } + + for (vb = res->variables, i = 0; + (vb != NULL) && (i < data->values_len); + vb = vb->next_variable, i++) + { + csnmp_table_values_t *vt; + /* Check if we left the subtree */ if (snmp_oid_ncompare (data->values[i].oid, data->values[i].oid_len, @@ -964,28 +1185,34 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) if ((value_table_ptr[i] != NULL) && (vb->name[vb->name_length - 1] <= value_table_ptr[i]->subid)) { - DEBUG ("snmp plugin: host = %s; data = %s; i = %i; SUBID is not increasing.", + DEBUG ("snmp plugin: host = %s; data = %s; i = %i; " + "SUBID is not increasing.", host->name, data->name, i); continue; } vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t)); - if (vt != NULL) + if (vt == NULL) { - vt->subid = vb->name[vb->name_length - 1]; - vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type); - vt->next = NULL; - - if (value_table_ptr[i] == NULL) - value_table[i] = vt; - else - value_table_ptr[i]->next = vt; - value_table_ptr[i] = vt; + ERROR ("snmp plugin: malloc failed."); + status = -1; + break; } + vt->subid = vb->name[vb->name_length - 1]; + vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type, + data->scale, data->shift); + vt->next = NULL; + + if (value_table_ptr[i] == NULL) + value_table[i] = vt; + else + value_table_ptr[i]->next = vt; + value_table_ptr[i] = vt; + /* Copy OID to oid_list[i + 1] */ - memcpy (oid_list[i + 1].oid, vb->name, sizeof (oid) * vb->name_length); - oid_list[i + 1].oid_len = vb->name_length; + memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length); + oid_list[i].oid_len = vb->name_length; } /* for (i = data->values_len) */ if (res != NULL) @@ -1036,6 +1263,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) 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) { @@ -1062,13 +1295,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) vl.values[i].gauge = NAN; } - strncpy (vl.host, host->name, sizeof (vl.host)); - vl.host[sizeof (vl.host) - 1] = '\0'; - strcpy (vl.plugin, "snmp"); - strncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance)); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + sstrncpy (vl.host, host->name, sizeof (vl.host)); + sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin)); + sstrncpy (vl.type, data->type, sizeof (vl.type)); + sstrncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance)); - vl.interval = host->skip_num; + vl.interval = host->interval; req = snmp_pdu_create (SNMP_MSG_GET); if (req == NULL) @@ -1080,17 +1312,24 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) 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); } @@ -1099,21 +1338,26 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) 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, vb->name, vb->name_length) == 0) - vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type); + vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type, + data->scale, data->shift); } /* 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); + DEBUG ("snmp plugin: -> plugin_dispatch_values (&vl);"); + plugin_dispatch_values (&vl); sfree (vl.values); return (0); @@ -1122,8 +1366,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) 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); @@ -1141,9 +1389,104 @@ static int csnmp_read_host (host_definition_t *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 %lu seconds.", + host->name, host->interval, (unsigned long)(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; @@ -1157,25 +1500,87 @@ static int csnmp_read (void) 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 */ /*