X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fsnmp.c;h=693cd8942ffc10919e6e7d9d9b1084096818a3e7;hb=854f18303f070d34a5373e970b7a81dd505bddfb;hp=995f39deff913ee77a9cf807f12d323635236a85;hpb=b74101a33fad432014a840a8242afe268b4ce3d4;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index 995f39de..693cd894 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -22,6 +22,7 @@ #include "collectd.h" #include "common.h" #include "plugin.h" +#include "utils_complain.h" #include @@ -67,17 +68,10 @@ struct host_definition_s char *community; int version; void *sess_handle; + c_complain_t complaint; 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; @@ -102,20 +96,51 @@ 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; +/* + * Prototypes + */ +static int csnmp_read_host (user_data_t *ud); /* * Private functions */ +static void csnmp_host_close_session (host_definition_t *host) /* {{{ */ +{ + if (host->sess_handle == NULL) + return; + + snmp_sess_close (host->sess_handle); + host->sess_handle = NULL; +} /* }}} void csnmp_host_close_session */ + +static void csnmp_host_definition_destroy (void *arg) /* {{{ */ +{ + host_definition_t *hd; + + hd = arg; + + if (hd == NULL) + return; + + if (hd->name != NULL) + { + DEBUG ("snmp plugin: Destroying host definition for host `%s'.", + hd->name); + } + + csnmp_host_close_session (hd); + + sfree (hd->name); + sfree (hd->address); + sfree (hd->community); + sfree (hd->data_list); + + sfree (hd); +} /* }}} void csnmp_host_definition_destroy */ + +/* Many functions to handle the configuration. {{{ */ /* First there are many functions which do configuration stuff. It's a big * bloated and messy, I'm afraid. */ @@ -527,9 +552,9 @@ static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t return (-1); } - hd->interval = (int) ci->values[0].value.number; - if (hd->interval < 0) - hd->interval = 0; + hd->interval = ci->values[0].value.number >= 0 + ? (uint32_t) ci->values[0].value.number + : 0; return (0); } /* int csnmp_config_add_host_interval */ @@ -540,6 +565,11 @@ static int csnmp_config_add_host (oconfig_item_t *ci) int status = 0; int i; + /* Registration stuff. */ + char cb_name[DATA_MAX_NAME_LEN]; + user_data_t cb_data; + struct timespec cb_interval; + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING ("snmp plugin: `Host' needs exactly one string argument."); @@ -551,6 +581,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) return (-1); memset (hd, '\0', sizeof (host_definition_t)); hd->version = 2; + C_COMPLAIN_INIT (&hd->complaint); hd->name = strdup (ci->values[0].value.string); if (hd->name == NULL) @@ -561,8 +592,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci) hd->sess_handle = NULL; hd->interval = 0; - hd->next_update = 0; - hd->state = STATE_IDLE; for (i = 0; i < ci->children_num; i++) { @@ -609,23 +638,30 @@ static int csnmp_config_add_host (oconfig_item_t *ci) if (status != 0) { - sfree (hd->name); - sfree (hd); + csnmp_host_definition_destroy (hd); return (-1); } DEBUG ("snmp plugin: hd = { name = %s, address = %s, community = %s, version = %i }", hd->name, hd->address, hd->community, hd->version); - if (host_head == NULL) - host_head = hd; - else + ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name); + + memset (&cb_data, 0, sizeof (cb_data)); + cb_data.data = hd; + cb_data.free_func = csnmp_host_definition_destroy; + + memset (&cb_interval, 0, sizeof (cb_interval)); + if (hd->interval != 0) + cb_interval.tv_sec = (time_t) hd->interval; + + status = plugin_register_complex_read (cb_name, csnmp_read_host, + /* interval = */ &cb_interval, /* user_data = */ &cb_data); + if (status != 0) { - host_definition_t *last; - last = host_head; - while (last->next != NULL) - last = last->next; - last->next = hd; + ERROR ("snmp plugin: Registering complex read function failed."); + csnmp_host_definition_destroy (hd); + return (-1); } return (0); @@ -653,16 +689,7 @@ static int csnmp_config (oconfig_item_t *ci) return (0); } /* int csnmp_config */ -/* End of the config stuff. Now the interesting part begins */ - -static void csnmp_host_close_session (host_definition_t *host) -{ - if (host->sess_handle == NULL) - return; - - snmp_sess_close (host->sess_handle); - host->sess_handle = NULL; -} /* void csnmp_host_close_session */ +/* }}} End of the config stuff. Now the interesting part begins */ static void csnmp_host_open_session (host_definition_t *host) { @@ -697,7 +724,8 @@ 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; + uint64_t tmp_unsigned = 0; + int64_t tmp_signed = 0; int defined = 1; if ((vl->type == ASN_INTEGER) @@ -708,15 +736,17 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, #endif || (vl->type == ASN_GAUGE)) { - temp = (uint32_t) *vl->val.integer; - DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", temp); + tmp_unsigned = (uint32_t) *vl->val.integer; + tmp_signed = (int32_t) *vl->val.integer; + DEBUG ("snmp plugin: Parsed int32 value is %"PRIi64".", tmp_signed); } 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 %"PRIu64".", temp); + tmp_unsigned = (uint32_t) vl->val.counter64->high; + tmp_unsigned = tmp_unsigned << 32; + tmp_unsigned += (uint32_t) vl->val.counter64->low; + tmp_signed = (int64_t) tmp_unsigned; + DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", tmp_unsigned); } else if (vl->type == ASN_OCTET_STR) { @@ -724,15 +754,28 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, } else { - WARNING ("snmp plugin: I don't know the ASN type `%i'", (int) vl->type); + char oid_buffer[1024]; + + memset (oid_buffer, 0, sizeof (oid_buffer)); + snprint_objid (oid_buffer, sizeof (oid_buffer) - 1, + vl->name, vl->name_length); + +#ifdef ASN_NULL + if (vl->type == ASN_NULL) + INFO ("snmp plugin: OID \"%s\" is undefined (type ASN_NULL)", + oid_buffer); + else +#endif + WARNING ("snmp plugin: I don't know the ASN type \"%i\" (OID: %s)", + (int) vl->type, oid_buffer); + defined = 0; } if (vl->type == ASN_OCTET_STR) { - char *endptr; + int status = -1; - endptr = NULL; if (vl->val.string != NULL) { char string[64]; @@ -743,44 +786,60 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, 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'. + * terminated. That is why we're using `memcpy' 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) + status = parse_value (string, &ret, type); + if (status != 0) { - 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); + ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s", + DS_TYPE_TO_STRING (type), string); } } - /* Check if an error occurred */ - if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string)) + if (status != 0) { - if (type == DS_TYPE_COUNTER) - ret.counter = 0; - else if (type == DS_TYPE_GAUGE) - ret.gauge = NAN; + switch (type) + { + case DS_TYPE_COUNTER: + case DS_TYPE_DERIVE: + case DS_TYPE_ABSOLUTE: + memset (&ret, 0, sizeof (ret)); + break; + + case DS_TYPE_GAUGE: + ret.gauge = NAN; + break; + + default: + ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown " + "data source type: %i.", type); + ret.gauge = NAN; + } } - } + } /* if (vl->type == ASN_OCTET_STR) */ else if (type == DS_TYPE_COUNTER) { - ret.counter = temp; + ret.counter = tmp_unsigned; } else if (type == DS_TYPE_GAUGE) { ret.gauge = NAN; if (defined != 0) - ret.gauge = (scale * temp) + shift; + ret.gauge = (scale * tmp_signed) + shift; + } + else if (type == DS_TYPE_DERIVE) + ret.derive = (derive_t) tmp_signed; + else if (type == DS_TYPE_ABSOLUTE) + ret.absolute = (absolute_t) tmp_unsigned; + else + { + ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source " + "type: %i.", type); + ret.gauge = NAN; } return (ret); @@ -849,6 +908,72 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host, return (0); } /* int csnmp_check_res_left_subtree */ +static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */ + const struct variable_list *vb, size_t dst_size) +{ + char *buffer_ptr; + size_t buffer_free; + size_t i; + + buffer_ptr = dst; + buffer_free = dst_size; + + for (i = 0; i < vb->val_len; i++) + { + int status; + + status = snprintf (buffer_ptr, buffer_free, + (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]); + + if (status >= buffer_free) + { + buffer_ptr += (buffer_free - 1); + *buffer_ptr = 0; + return (dst_size + (buffer_free - status)); + } + else /* if (status < buffer_free) */ + { + buffer_ptr += status; + buffer_free -= status; + } + } + + return ((int) (dst_size - buffer_free)); +} /* }}} int csnmp_strvbcopy_hexstring */ + +static int csnmp_strvbcopy (char *dst, /* {{{ */ + const struct variable_list *vb, size_t dst_size) +{ + char *src; + size_t num_chars; + size_t i; + + if (vb->type == ASN_OCTET_STR) + src = (char *) vb->val.string; + else if (vb->type == ASN_BIT_STR) + src = (char *) vb->val.bitstring; + else + { + dst[0] = 0; + return (EINVAL); + } + + num_chars = dst_size - 1; + if (num_chars > vb->val_len) + num_chars = vb->val_len; + + for (i = 0; i < num_chars; i++) + { + /* Check for control characters. */ + if ((src[i] >= 0) && (src[i] < 32)) + return (csnmp_strvbcopy_hexstring (dst, vb, dst_size)); + dst[i] = src[i]; + } + dst[num_chars] = 0; + + return ((int) vb->val_len); +} /* }}} int csnmp_strvbcopy */ + static int csnmp_instance_list_add (csnmp_list_instances_t **head, csnmp_list_instances_t **tail, const struct snmp_pdu *res) @@ -877,17 +1002,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR)) { 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; - sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR) - ? vb->val.string - : vb->val.bitstring), - instance_len + 1); + csnmp_strvbcopy (il->instance, vb, sizeof (il->instance)); for (ptr = il->instance; *ptr != '\0'; ptr++) { @@ -960,7 +1076,6 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin)); vl.interval = host->interval; - vl.time = time (NULL); subid = 0; have_more = 1; @@ -1026,7 +1141,7 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat char temp[DATA_MAX_NAME_LEN]; if (instance_list_ptr == NULL) - ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid); + ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid); else sstrncpy (temp, instance_list_ptr->instance, sizeof (temp)); @@ -1136,7 +1251,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) break; } - for (i = 0; i < oid_list_len; i++) + for (i = 0; (uint32_t) i < oid_list_len; i++) snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len); res = NULL; @@ -1147,7 +1262,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) char *errstr = NULL; snmp_sess_error (host->sess_handle, NULL, NULL, &errstr); - ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s", + + c_complain (LOG_ERR, &host->complaint, + "snmp plugin: host %s: snmp_sess_synch_response failed: %s", host->name, (errstr == NULL) ? "Unknown problem" : errstr); if (res != NULL) @@ -1162,6 +1279,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) } status = 0; assert (res != NULL); + c_release (LOG_INFO, &host->complaint, + "snmp plugin: host %s: snmp_sess_synch_response successful.", + host->name); vb = res->variables; if (vb == NULL) @@ -1377,7 +1497,6 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) return (-1); } - vl.time = time (NULL); for (vb = res->variables; vb != NULL; vb = vb->next_variable) { @@ -1406,11 +1525,19 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) return (0); } /* int csnmp_read_value */ -static int csnmp_read_host (host_definition_t *host) +static int csnmp_read_host (user_data_t *ud) { - int i; + host_definition_t *host; time_t time_start; time_t time_end; + int status; + int success; + int i; + + host = ud->data; + + if (host->interval == 0) + host->interval = interval_g; time_start = time (NULL); DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name, @@ -1422,184 +1549,51 @@ static int csnmp_read_host (host_definition_t *host) if (host->sess_handle == NULL) return (-1); + success = 0; for (i = 0; i < host->data_list_len; i++) { data_definition_t *data = host->data_list[i]; if (data->is_table) - csnmp_read_table (host, data); + status = csnmp_read_table (host, data); else - csnmp_read_value (host, data); + status = csnmp_read_value (host, data); + + if (status == 0) + success++; } 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) + if ((uint32_t) (time_end - time_start) > host->interval) { - WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, " - "but reading all values takes %u seconds.", + WARNING ("snmp plugin: Host `%s' should be queried every %"PRIu32 + " seconds, but reading all values takes %u seconds.", host->name, host->interval, (unsigned int) (time_end - time_start)); } + if (success == 0) + return (-1); + 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; - time_t now; - - if (host_head == NULL) - { - INFO ("snmp plugin: No hosts configured."); - return (-1); - } - - now = time (NULL); - - pthread_mutex_lock (&host_lock); - for (host = host_head; host != NULL; host = host->next) - { - if (host->state != STATE_IDLE) - continue; - - /* 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->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; - } + /* When we get here, the read threads have been stopped and all the + * `host_definition_t' will be freed. */ + DEBUG ("snmp plugin: Destroying all data definitions."); data_this = data_head; data_head = NULL; @@ -1622,10 +1616,9 @@ 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 */ /* - * vim: shiftwidth=2 softtabstop=2 tabstop=8 + * vim: shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker */