X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fsnmp.c;h=b8bbee44b05d86f4ce5503f8e7a5a3c0a3b29a62;hb=ab6ab6ad6428ba0a0987a20a7d1cfa47d6cc6f8b;hp=45d13c73e4d6f0959672985ec80cea558406ba5a;hpb=69f341f4042f56279be79501d3222c6cb1382e85;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index 45d13c73..b8bbee44 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -1,6 +1,6 @@ /** * collectd - src/snmp.c - * Copyright (C) 2007 Florian octo Forster + * Copyright (C) 2007-2012 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -69,7 +69,7 @@ struct host_definition_s int version; void *sess_handle; c_complain_t complaint; - uint32_t interval; + cdtime_t interval; data_definition_t **data_list; int data_list_len; }; @@ -79,7 +79,7 @@ typedef struct host_definition_s host_definition_t; * gaps in tables. */ struct csnmp_list_instances_s { - oid subid; + oid_t suffix; char instance[DATA_MAX_NAME_LEN]; struct csnmp_list_instances_s *next; }; @@ -87,7 +87,7 @@ typedef struct csnmp_list_instances_s csnmp_list_instances_t; struct csnmp_table_values_s { - oid subid; + oid_t suffix; value_t value; struct csnmp_table_values_s *next; }; @@ -106,6 +106,54 @@ static int csnmp_read_host (user_data_t *ud); /* * Private functions */ +static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n) +{ + assert (n <= STATIC_ARRAY_LEN (dst->oid)); + memcpy (dst->oid, src, sizeof (*src) * n); + dst->oid_len = n; +} + +static int csnmp_oid_compare (oid_t const *left, oid_t const *right) +{ + return (snmp_oid_compare (left->oid, left->oid_len, + right->oid, right->oid_len)); +} + +static int csnmp_oid_suffix (oid_t *dst, oid_t const *src, + oid_t const *root) +{ + /* Make sure "src" is in "root"s subtree. */ + if (src->oid_len <= root->oid_len) + return (EINVAL); + if (snmp_oid_ncompare (root->oid, root->oid_len, + src->oid, src->oid_len, + /* n = */ root->oid_len) != 0) + return (EINVAL); + + memset (dst, 0, sizeof (*dst)); + dst->oid_len = src->oid_len - root->oid_len; + memcpy (dst->oid, &src->oid[root->oid_len], + dst->oid_len * sizeof (dst->oid[0])); + return (0); +} + +static int csnmp_oid_to_string (char *buffer, size_t buffer_size, + oid_t const *o) +{ + char oid_str[MAX_OID_LEN][16]; + char *oid_str_ptr[MAX_OID_LEN]; + size_t i; + + for (i = 0; i < o->oid_len; i++) + { + ssnprintf (oid_str[i], sizeof (oid_str[i]), "%lu", (unsigned long) o->oid[i]); + oid_str_ptr[i] = oid_str[i]; + } + + return (strjoin (buffer, buffer_size, + oid_str_ptr, o->oid_len, /* separator = */ ".")); +} + static void csnmp_host_close_session (host_definition_t *host) /* {{{ */ { if (host->sess_handle == NULL) @@ -159,7 +207,6 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */ * +-> csnmp_config_add_host_community * +-> csnmp_config_add_host_version * +-> csnmp_config_add_host_collect - * +-> csnmp_config_add_host_interval */ static void call_snmp_init_once (void) { @@ -543,22 +590,6 @@ static int csnmp_config_add_host_collect (host_definition_t *host, return (0); } /* int csnmp_config_add_host_collect */ -static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("snmp plugin: The `Interval' config option needs exactly one number argument."); - return (-1); - } - - hd->interval = ci->values[0].value.number >= 0 - ? (uint32_t) ci->values[0].value.number - : 0; - - return (0); -} /* int csnmp_config_add_host_interval */ - static int csnmp_config_add_host (oconfig_item_t *ci) { host_definition_t *hd; @@ -607,7 +638,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) else if (strcasecmp ("Collect", option->key) == 0) csnmp_config_add_host_collect (hd, option); else if (strcasecmp ("Interval", option->key) == 0) - csnmp_config_add_host_interval (hd, option); + cf_util_get_cdtime (option, &hd->interval); else { WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key); @@ -651,9 +682,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) 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; + CDTIME_T_TO_TIMESPEC (hd->interval, &cb_interval); status = plugin_register_complex_read (/* group = */ NULL, cb_name, csnmp_read_host, /* interval = */ &cb_interval, @@ -996,10 +1025,13 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */ static int csnmp_instance_list_add (csnmp_list_instances_t **head, csnmp_list_instances_t **tail, - const struct snmp_pdu *res) + struct snmp_pdu const *res, + oid_t const *root) { csnmp_list_instances_t *il; struct variable_list *vb; + oid_t vb_name; + int status; /* Set vb on the last variable */ for (vb = res->variables; @@ -1009,16 +1041,24 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, if (vb == NULL) return (-1); + csnmp_oid_init (&vb_name, vb->name, vb->name_length); + il = malloc (sizeof (*il)); if (il == NULL) { ERROR ("snmp plugin: malloc failed."); return (-1); } - /* XXX copy entire OID */ - il->subid = vb->name[vb->name_length - 1]; + memset (il, 0, sizeof (*il)); il->next = NULL; + status = csnmp_oid_suffix (&il->suffix, &vb_name, root); + if (status != 0) + { + sfree (il); + return (status); + } + /* Get instance name */ if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR)) { @@ -1064,8 +1104,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; + _Bool have_more; + oid_t current_suffix; ds = plugin_get_ds (data->type); if (!ds) @@ -1077,15 +1117,14 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat instance_list_ptr = instance_list; - value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *) - * data->values_len); + value_table_ptr = malloc (sizeof (*value_table_ptr) * data->values_len); if (value_table_ptr == NULL) return (-1); for (i = 0; i < data->values_len; i++) value_table_ptr[i] = value_table[i]; vl.values_len = ds->ds_num; - vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len); + vl.values = malloc (sizeof (*vl.values) * vl.values_len); if (vl.values == NULL) { ERROR ("snmp plugin: malloc failed."); @@ -1098,50 +1137,70 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat vl.interval = host->interval; - subid = 0; have_more = 1; - - while (have_more != 0) + memset (¤t_suffix, 0, sizeof (current_suffix)); + while (have_more) { + _Bool suffix_skipped = 0; + + /* Determine next suffix to handle. */ 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) + + memcpy (¤t_suffix, &instance_list_ptr->suffix, sizeof (current_suffix)); + } + else /* no instance configured */ + { + csnmp_table_values_t *ptr = value_table_ptr[0]; + if (ptr == NULL) { - subid = instance_list_ptr->subid; + have_more = 0; continue; } - } /* if (instance_list != NULL) */ + memcpy (¤t_suffix, &ptr->suffix, sizeof (current_suffix)); + } + + /* Update all the value_table_ptr to point at the entry with the same + * trailing partial OID */ for (i = 0; i < data->values_len; i++) { while ((value_table_ptr[i] != NULL) - && (value_table_ptr[i]->subid < subid)) - value_table_ptr[i] = value_table_ptr[i]->next; + && (csnmp_oid_compare (&value_table_ptr[i]->suffix, ¤t_suffix) < 0)) + value_table_ptr[i] = value_table_ptr[i]->next; if (value_table_ptr[i] == NULL) { have_more = 0; break; } - else if (value_table_ptr[i]->subid > subid) + else if (csnmp_oid_compare (&value_table_ptr[i]->suffix, ¤t_suffix) > 0) { - subid = value_table_ptr[i]->subid; + /* This suffix is missing in the subtree. Indicate this with the + * "suffix_skipped" flag and try the next instance / suffix. */ + suffix_skipped = 1; break; } } /* for (i = 0; i < columns; i++) */ - /* The subid has been increased - start scanning from the beginning - * again.. */ - if (i < data->values_len) + + if (!have_more) + break; + + /* Matching the values failed. Start from the beginning again. */ + if (suffix_skipped) + { + if (instance_list != NULL) + instance_list_ptr = instance_list_ptr->next; + else + value_table_ptr[0] = value_table_ptr[0]->next; + 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 @@ -1150,10 +1209,12 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat 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 (csnmp_oid_compare (&value_table_ptr[i-1]->suffix, + &value_table_ptr[i]->suffix) == 0); } assert ((instance_list_ptr == NULL) - || (instance_list_ptr->subid == value_table_ptr[0]->subid)); + || (csnmp_oid_compare (&instance_list_ptr->suffix, + &value_table_ptr[0]->suffix) == 0)); #endif sstrncpy (vl.type, data->type, sizeof (vl.type)); @@ -1162,7 +1223,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), "%"PRIu32, (uint32_t) subid); + csnmp_oid_to_string (temp, sizeof (temp), ¤t_suffix); else sstrncpy (temp, instance_list_ptr->instance, sizeof (temp)); @@ -1179,8 +1240,11 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat /* If we get here `vl.type_instance' and all `vl.values' have been set */ plugin_dispatch_values (&vl); - subid++; - } /* while (have_more != 0) */ + if (instance_list != NULL) + instance_list_ptr = instance_list_ptr->next; + else + value_table_ptr[0] = value_table_ptr[0]->next; + } /* while (have_more) */ sfree (vl.values); sfree (value_table_ptr); @@ -1344,7 +1408,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) /* do nothing */; assert (vb != NULL); - /* Copy the OID of the instance value to oid_list[data->values_len] */ + /* Copy the OID of the instance value to oid_list[data->values_len]. + * "oid_list" is used for the next GETNEXT request. */ memcpy (oid_list[data->values_len].oid, vb->name, sizeof (oid) * vb->name_length); oid_list[data->values_len].oid_len = vb->name_length; @@ -1358,38 +1423,45 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) vb = vb->next_variable, i++) { csnmp_table_values_t *vt; + oid_t vb_name; + oid_t suffix; - /* Check if we left the subtree */ - if (snmp_oid_ncompare (data->values[i].oid, - data->values[i].oid_len, - vb->name, vb->name_length, - data->values[i].oid_len) != 0) + csnmp_oid_init (&vb_name, vb->name, vb->name_length); + + /* Calculate the current suffix. This is later used to check that the + * suffix is increasing. This also checks if we left the subtree */ + status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i); + if (status != 0) { - DEBUG ("snmp plugin: host = %s; data = %s; Value %i left its subtree.", + DEBUG ("snmp plugin: host = %s; data = %s; Value %i failed. " + "It probably left its subtree.", host->name, data->name, i); continue; } + /* Make sure the OIDs returned by the agent are increasing. Otherwise our + * table matching algorithm will get confused. */ if ((value_list_tail[i] != NULL) - && (vb->name[vb->name_length - 1] <= value_list_tail[i]->subid)) + && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0)) { DEBUG ("snmp plugin: host = %s; data = %s; i = %i; " - "SUBID is not increasing.", + "Suffix is not increasing.", host->name, data->name, i); continue; } - vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t)); + vt = malloc (sizeof (*vt)); if (vt == NULL) { ERROR ("snmp plugin: malloc failed."); status = -1; break; } + memset (vt, 0, sizeof (*vt)); - vt->subid = vb->name[vb->name_length - 1]; vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type, data->scale, data->shift); + memcpy (&vt->suffix, &suffix, sizeof (vt->suffix)); vt->next = NULL; if (value_list_tail[i] == NULL) @@ -1557,8 +1629,8 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) static int csnmp_read_host (user_data_t *ud) { host_definition_t *host; - time_t time_start; - time_t time_end; + cdtime_t time_start; + cdtime_t time_end; int status; int success; int i; @@ -1568,9 +1640,7 @@ static int csnmp_read_host (user_data_t *ud) if (host->interval == 0) host->interval = interval_g; - time_start = time (NULL); - DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name, - (unsigned int) time_start); + time_start = cdtime (); if (host->sess_handle == NULL) csnmp_host_open_session (host); @@ -1592,14 +1662,14 @@ static int csnmp_read_host (user_data_t *ud) success++; } - time_end = time (NULL); - DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name, - (unsigned int) time_end); - if ((uint32_t) (time_end - time_start) > host->interval) + time_end = cdtime (); + if ((time_end - time_start) > host->interval) { - 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)); + WARNING ("snmp plugin: Host `%s' should be queried every %.3f " + "seconds, but reading all values takes %.3f seconds.", + host->name, + CDTIME_T_TO_DOUBLE (host->interval), + CDTIME_T_TO_DOUBLE (time_end - time_start)); } if (success == 0)