X-Git-Url: https://git.octo.it/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsnmp.c;h=995f39deff913ee77a9cf807f12d323635236a85;hb=2b65cef7870c385b9b56b34701ced62528f3f7c0;hp=622d64885e989ef49c719c5e819e71b50a4a09f5;hpb=1b59d618ba9c6849796ff313cee742082ad08eb1;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index 622d6488..995f39de 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -51,6 +51,7 @@ 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; @@ -66,16 +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 `skip_left < interval_g' */ - STATE_WAIT, /* - waits to be queried. */ - STATE_BUSY /* - is currently being queried. */ - } state; /****************************************************/ + 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; @@ -126,6 +127,7 @@ static pthread_cond_t host_cond = PTHREAD_COND_INITIALIZER; * ! +-> 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 @@ -151,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); @@ -198,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; @@ -221,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); @@ -313,6 +338,8 @@ 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) @@ -350,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); @@ -492,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)) { @@ -501,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 */ @@ -535,8 +560,8 @@ 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++) @@ -632,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: host %s: snmp_sess_close failed: %s", - host->name, (errstr == NULL) ? "Unknown problem" : errstr); - sfree (errstr); - } - + snmp_sess_close (host->sess_handle); host->sess_handle = NULL; } /* void csnmp_host_close_session */ @@ -681,6 +692,7 @@ static void csnmp_host_open_session (host_definition_t *host) } } /* void csnmp_host_open_session */ +/* TODO: Check if negative values wrap around. Problem: negative temperatures. */ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, double scale, double shift) { @@ -697,14 +709,18 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, || (vl->type == ASN_GAUGE)) { temp = (uint32_t) *vl->val.integer; - DEBUG ("snmp plugin: Parsed int32 value is %llu.", temp); + DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", temp); } else if (vl->type == ASN_COUNTER64) { temp = (uint32_t) vl->val.counter64->high; temp = temp << 32; temp += (uint32_t) vl->val.counter64->low; - DEBUG ("snmp plugin: Parsed int64 value is %llu.", temp); + DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", temp); + } + else if (vl->type == ASN_OCTET_STR) + { + /* We'll handle this later.. */ } else { @@ -712,7 +728,51 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, defined = 0; } - if (type == DS_TYPE_COUNTER) + if (vl->type == ASN_OCTET_STR) + { + char *endptr; + + endptr = NULL; + if (vl->val.string != NULL) + { + char string[64]; + size_t string_length; + + string_length = sizeof (string) - 1; + if (vl->val_len < string_length) + string_length = vl->val_len; + + /* The strings we get from the Net-SNMP library may not be null + * terminated. That is why we're using `membpy' here and not `strcpy'. + * `string_length' is set to `vl->val_len' which holds the length of the + * string. -octo */ + memcpy (string, vl->val.string, string_length); + string[string_length] = 0; + + if (type == DS_TYPE_COUNTER) + { + ret.counter = (counter_t) strtoll (string, &endptr, /* base = */ 0); + DEBUG ("snmp plugin: csnmp_value_list_to_value: String to counter: %s -> %llu", + string, (unsigned long long) ret.counter); + } + else if (type == DS_TYPE_GAUGE) + { + ret.gauge = (gauge_t) strtod (string, &endptr); + DEBUG ("snmp plugin: csnmp_value_list_to_value: String to gauge: %s -> %g", + string, (double) ret.gauge); + } + } + + /* Check if an error occurred */ + if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string)) + { + if (type == DS_TYPE_COUNTER) + ret.counter = 0; + else if (type == DS_TYPE_GAUGE) + ret.gauge = NAN; + } + } + else if (type == DS_TYPE_COUNTER) { ret.counter = temp; } @@ -769,7 +829,7 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host, if (vb == NULL) { ERROR ("snmp plugin: host %s: Expected one more variable for " - "the instance.."); + "the instance..", host->name); return (-1); } @@ -819,15 +879,15 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, char *ptr; size_t instance_len; + memset (il->instance, 0, sizeof (il->instance)); 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) + sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR) ? vb->val.string : vb->val.bitstring), - instance_len); - il->instance[instance_len] = '\0'; + instance_len + 1); for (ptr = il->instance; *ptr != '\0'; ptr++) { @@ -841,10 +901,9 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, else { value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0); - snprintf (il->instance, sizeof (il->instance), + ssnprintf (il->instance, sizeof (il->instance), "%llu", val.counter); } - il->instance[sizeof (il->instance) - 1] = '\0'; /* TODO: Debugging output */ @@ -897,11 +956,10 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat 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); subid = 0; @@ -962,19 +1020,28 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat || (instance_list_ptr->subid == value_table_ptr[0]->subid)); #endif - if (instance_list_ptr == NULL) - snprintf (vl.type_instance, sizeof (vl.type_instance), "%u", - (uint32_t) subid); - else - strncpy (vl.type_instance, instance_list_ptr->instance, - sizeof (vl.type_instance)); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + 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); + plugin_dispatch_values (&vl); subid++; } /* while (have_more != 0) */ @@ -1072,15 +1139,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: 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; @@ -1099,7 +1173,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) /* Check if all values (and possibly the instance) have left their * subtree */ if (csnmp_check_res_left_subtree (host, data, res) != 0) + { + status = 0; break; + } /* if an instance-OID is configured.. */ if (data->instance.oid.oid_len > 0) @@ -1119,11 +1196,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) (vb != NULL) && (vb->next_variable != NULL); vb = vb->next_variable) /* do nothing */; - if (vb == NULL) - { - status = -1; - break; - } + assert (vb != NULL); /* Copy OID to oid_list[data->values_len] */ memcpy (oid_list[data->values_len].oid, vb->name, @@ -1186,6 +1259,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) res = NULL; } /* while (status == 0) */ + if (res != NULL) + snmp_free_pdu (res); + res = NULL; + if (status == 0) csnmp_dispatch_table (host, data, instance_list, value_table); @@ -1261,13 +1338,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) @@ -1279,17 +1355,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: host %s: snmp_sess_synch_response failed: %s", host->name, (errstr == NULL) ? "Unknown problem" : errstr); - csnmp_host_close_session (host); + + if (res != NULL) + snmp_free_pdu (res); + res = NULL; + sfree (errstr); + csnmp_host_close_session (host); return (-1); } @@ -1298,10 +1381,12 @@ 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, @@ -1310,10 +1395,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) 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); @@ -1322,8 +1409,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); @@ -1341,6 +1432,16 @@ 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 %u seconds.", + host->name, host->interval, (unsigned int) (time_end - time_start)); + } + return (0); } /* int csnmp_read_host */ @@ -1390,18 +1491,18 @@ static int csnmp_init (void) for (host = host_head; host != NULL; host = host->next) { threads_num++; - /* We need to initialize `skip_num' here, because `interval_g' isn't + /* We need to initialize `interval' here, because `interval_g' isn't * initialized during `configure'. */ - host->skip_left = interval_g; - if (host->skip_num == 0) + host->next_update = time (NULL); + if (host->interval == 0) { - host->skip_num = interval_g; + host->interval = interval_g; } - else if (host->skip_num < interval_g) + else if (host->interval < interval_g) { - host->skip_num = interval_g; + host->interval = interval_g; WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.", - host->name, host->skip_num); + host->name, host->interval); } csnmp_host_open_session (host); @@ -1448,13 +1549,12 @@ static int csnmp_read (void) if (host->state != STATE_IDLE) continue; - host->skip_left -= interval_g; - if (host->skip_left >= interval_g) + /* Skip this host if the next or a later iteration will be sufficient. */ + if (host->next_update >= (now + interval_g)) continue; host->state = STATE_WAIT; - - host->skip_left = host->skip_num; + host->next_update = now + host->interval; } /* for (host) */ pthread_cond_broadcast (&host_cond);