X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fsnmp.c;h=2c4c930cb652dce0c5f3a7642a12b5f347a561d3;hb=1a7050de376608268d11293d4e5faa3fb8516c5a;hp=972bc294b8aeb563ebf2bee155c923fb6b2d6333;hpb=2aafb6b932b2b28fde2a81c9c17fe13634a5a16e;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index 972bc294..2c4c930c 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -23,6 +23,8 @@ #include "common.h" #include "plugin.h" +#include + #include #include @@ -32,7 +34,7 @@ struct oid_s { oid oid[MAX_OID_LEN]; - uint32_t oid_len; + size_t oid_len; }; typedef struct oid_s oid_t; @@ -61,18 +63,52 @@ struct host_definition_s char *address; char *community; int version; - struct snmp_session sess; + void *sess_handle; + int16_t skip_num; + int16_t skip_left; 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; /****************************************************/ struct host_definition_s *next; }; typedef struct host_definition_s host_definition_t; +/* These two types are used to cache values in `csnmp_read_table' to handle + * gaps in tables. */ +struct csnmp_list_instances_s +{ + oid subid; + char instance[DATA_MAX_NAME_LEN]; + struct csnmp_list_instances_s *next; +}; +typedef struct csnmp_list_instances_s csnmp_list_instances_t; + +struct csnmp_table_values_s +{ + oid subid; + value_t value; + struct csnmp_table_values_s *next; +}; +typedef struct csnmp_table_values_s csnmp_table_values_t; + /* * Private variables */ -data_definition_t *data_head = NULL; -host_definition_t *host_head = NULL; +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 @@ -83,13 +119,18 @@ host_definition_t *host_head = NULL; /* * Callgraph for the config stuff: * csnmp_config + * +-> call_snmp_init_once * +-> csnmp_config_add_data * ! +-> csnmp_config_add_data_type * ! +-> csnmp_config_add_data_table * ! +-> csnmp_config_add_data_instance * ! +-> csnmp_config_add_data_values * +-> csnmp_config_add_host - * +-> csnmp_config_add_collect + * +-> csnmp_config_add_host_address + * +-> 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) { @@ -314,8 +355,6 @@ static int csnmp_config_add_host_address (host_definition_t *hd, oconfig_item_t DEBUG ("snmp plugin: host = %s; host->address = %s;", hd->name, hd->address); - hd->sess.peername = hd->address; - return (0); } /* int csnmp_config_add_host_address */ @@ -338,9 +377,6 @@ static int csnmp_config_add_host_community (host_definition_t *hd, oconfig_item_ DEBUG ("snmp plugin: host = %s; host->community = %s;", hd->name, hd->community); - hd->sess.community = (u_char *) hd->community; - hd->sess.community_len = strlen (hd->community); - return (0); } /* int csnmp_config_add_host_community */ @@ -364,14 +400,79 @@ static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t hd->version = version; - if (hd->version == 1) - hd->sess.version = SNMP_VERSION_1; - else - hd->sess.version = SNMP_VERSION_2c; - return (0); } /* int csnmp_config_add_host_address */ +static int csnmp_config_add_host_collect (host_definition_t *host, + oconfig_item_t *ci) +{ + data_definition_t *data; + data_definition_t **data_list; + int data_list_len; + int i; + + if (ci->values_num < 1) + { + WARNING ("snmp plugin: `Collect' needs at least one argument."); + return (-1); + } + + for (i = 0; i < ci->values_num; i++) + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + WARNING ("snmp plugin: All arguments to `Collect' must be strings."); + return (-1); + } + + data_list_len = host->data_list_len + ci->values_num; + data_list = (data_definition_t **) realloc (host->data_list, + sizeof (data_definition_t *) * data_list_len); + if (data_list == NULL) + return (-1); + host->data_list = data_list; + + for (i = 0; i < ci->values_num; i++) + { + for (data = data_head; data != NULL; data = data->next) + if (strcasecmp (ci->values[i].value.string, data->name) == 0) + break; + + if (data == NULL) + { + WARNING ("snmp plugin: No such data configured: `%s'", + ci->values[i].value.string); + continue; + } + + DEBUG ("snmp plugin: Collect: host = %s, data[%i] = %s;", + host->name, host->data_list_len, data->name); + + host->data_list[host->data_list_len] = data; + host->data_list_len++; + } /* for (values_num) */ + + return (0); +} /* int csnmp_config_add_host_collect */ + +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)) + { + WARNING ("snmp plugin: The `Interval' config option needs exactly one number argument."); + return (-1); + } + + interval = (int) ci->values[0].value.number; + hd->skip_num = interval; + if (hd->skip_num < 0) + hd->skip_num = 0; + + return (0); +} /* int csnmp_config_add_host_interval */ + static int csnmp_config_add_host (oconfig_item_t *ci) { host_definition_t *hd; @@ -397,8 +498,10 @@ static int csnmp_config_add_host (oconfig_item_t *ci) return (-1); } - snmp_sess_init (&hd->sess); - hd->sess.version = SNMP_VERSION_2c; + hd->sess_handle = NULL; + hd->skip_num = 0; + hd->skip_left = 0; + hd->state = STATE_IDLE; for (i = 0; i < ci->children_num; i++) { @@ -411,6 +514,10 @@ static int csnmp_config_add_host (oconfig_item_t *ci) status = csnmp_config_add_host_community (hd, option); else if (strcasecmp ("Version", option->key) == 0) status = csnmp_config_add_host_version (hd, option); + 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); else { WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key); @@ -446,7 +553,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci) return (-1); } - /* TODO: Check all fields in `hd'. */ DEBUG ("snmp plugin: hd = { name = %s, address = %s, community = %s, version = %i }", hd->name, hd->address, hd->community, hd->version); @@ -464,68 +570,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci) return (0); } /* int csnmp_config_add_host */ -static int csnmp_config_add_collect (oconfig_item_t *ci) -{ - data_definition_t *data; - host_definition_t *host; - data_definition_t **data_list; - int data_list_len; - int i; - - if (ci->values_num < 2) - { - WARNING ("snmp plugin: `Collect' needs at least two arguments."); - return (-1); - } - - for (i = 0; i < ci->values_num; i++) - if (ci->values[i].type != OCONFIG_TYPE_STRING) - { - WARNING ("snmp plugin: All arguments to `Collect' must be strings."); - return (-1); - } - - for (host = host_head; host != NULL; host = host->next) - if (strcasecmp (ci->values[0].value.string, host->name) == 0) - break; - - if (host == NULL) - { - WARNING ("snmp plugin: No such host configured: `%s'", - ci->values[0].value.string); - return (-1); - } - - data_list_len = host->data_list_len + ci->values_num - 1; - data_list = (data_definition_t **) realloc (host->data_list, - sizeof (data_definition_t *) * data_list_len); - if (data_list == NULL) - return (-1); - host->data_list = data_list; - - for (i = 1; i < ci->values_num; i++) - { - for (data = data_head; data != NULL; data = data->next) - if (strcasecmp (ci->values[i].value.string, data->name) == 0) - break; - - if (data == NULL) - { - WARNING ("snmp plugin: No such data configured: `%s'", - ci->values[i].value.string); - continue; - } - - DEBUG ("snmp plugin: Collect: host = %s, data[%i] = %s;", - host->name, host->data_list_len, data->name); - - host->data_list[host->data_list_len] = data; - host->data_list_len++; - } /* for (values_num) */ - - return (0); -} /* int csnmp_config_add_collect */ - static int csnmp_config (oconfig_item_t *ci) { int i; @@ -539,8 +583,6 @@ static int csnmp_config (oconfig_item_t *ci) csnmp_config_add_data (child); else if (strcasecmp ("Host", child->key) == 0) csnmp_config_add_host (child); - else if (strcasecmp ("Collect", child->key) == 0) - csnmp_config_add_collect (child); else { WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key); @@ -550,31 +592,58 @@ static int csnmp_config (oconfig_item_t *ci) return (0); } /* int csnmp_config */ -static int csnmp_init (void) +/* End of the config stuff. Now the interesting part begins */ + +static void csnmp_host_close_session (host_definition_t *host) { - call_snmp_init_once (); - return (0); -} + int status; + + if (host->sess_handle == NULL) + return; + + status = snmp_sess_close (host->sess_handle); -#if 0 -static void csnmp_submit (gauge_t snum, gauge_t mnum, gauge_t lnum) + 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); + } + + host->sess_handle = NULL; +} /* void csnmp_host_close_session */ + +static void csnmp_host_open_session (host_definition_t *host) { - value_t values[3]; - value_list_t vl = VALUE_LIST_INIT; + struct snmp_session sess; - values[0].gauge = snum; - values[1].gauge = mnum; - values[2].gauge = lnum; + if (host->sess_handle != NULL) + csnmp_host_close_session (host); - vl.values = values; - vl.values_len = STATIC_ARRAY_SIZE (values); - vl.time = time (NULL); - strcpy (vl.host, hostname_g); - strcpy (vl.plugin, "load"); + snmp_sess_init (&sess); + sess.peername = host->address; + sess.community = (u_char *) host->community; + sess.community_len = strlen (host->community); + sess.version = (host->version == 1) ? SNMP_VERSION_1 : SNMP_VERSION_2c; + + /* snmp_sess_open will copy the `struct snmp_session *'. */ + host->sess_handle = snmp_sess_open (&sess); - plugin_dispatch_values ("load", &vl); -} -#endif + if (host->sess_handle == NULL) + { + char *errstr = NULL; + + snmp_error (&sess, NULL, NULL, &errstr); + + ERROR ("snmp plugin: snmp_sess_open failed: %s", + (errstr == NULL) ? "Unknown problem" : errstr); + sfree (errstr); + } +} /* void csnmp_host_open_session */ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type) { @@ -587,13 +656,15 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type) || (vl->type == ASN_COUNTER) || (vl->type == ASN_GAUGE)) { - temp = *vl->val.integer; + temp = (uint32_t) *vl->val.integer; + DEBUG ("snmp plugin: Parsed int32 value is %llu.", temp); } else if (vl->type == ASN_COUNTER64) { - temp = vl->val.counter64->high; + temp = (uint32_t) vl->val.counter64->high; temp = temp << 32; - temp += vl->val.counter64->low; + temp += (uint32_t) vl->val.counter64->low; + DEBUG ("snmp plugin: Parsed int64 value is %llu.", temp); } else { @@ -615,14 +686,331 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type) return (ret); } /* value_t csnmp_value_list_to_value */ -static int csnmp_read_table (struct snmp_session *sess_ptr, - host_definition_t *host, data_definition_t *data) +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) +{ + const data_set_t *ds; + value_list_t vl = VALUE_LIST_INIT; + + csnmp_list_instances_t *instance_list_ptr; + csnmp_table_values_t **value_table_ptr; + + int i; + + ds = plugin_get_ds (data->type); + if (!ds) + { + ERROR ("snmp plugin: DataSet `%s' not defined.", data->type); + return (-1); + } + assert (ds->ds_num == data->values_len); + + value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *) + * 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); + if (vl.values == NULL) + { + 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"); + + vl.interval = host->skip_num; + vl.time = time (NULL); + + for (instance_list_ptr = instance_list; + instance_list_ptr != NULL; + instance_list_ptr = instance_list_ptr->next) + { + strncpy (vl.type_instance, instance_list_ptr->instance, sizeof (vl.type_instance)); + vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + + 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] = 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 (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 get here `vl.type_instance' and all `vl.values' have been set */ + plugin_dispatch_values (data->type, &vl); + } /* for (instance_list) */ + + sfree (vl.values); + sfree (value_table_ptr); + + return (0); +} /* int csnmp_dispatch_table */ + +static int csnmp_read_table (host_definition_t *host, data_definition_t *data) { + struct snmp_pdu *req; + struct snmp_pdu *res; + struct variable_list *vb; + + const data_set_t *ds; + oid_t *oid_list; + uint32_t oid_list_len; + + int status; + int i; + + /* `value_table' and `value_table_ptr' implement a linked list for each + * value. `instance_list' and `instance_list_ptr' implement a linked list of + * instance names. This is used to jump gaps in the table. */ + csnmp_list_instances_t *instance_list; + csnmp_list_instances_t *instance_list_ptr; + csnmp_table_values_t **value_table; + csnmp_table_values_t **value_table_ptr; + + DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)", + host->name, data->name); + + ds = plugin_get_ds (data->type); + if (!ds) + { + ERROR ("snmp plugin: DataSet `%s' not defined.", data->type); + return (-1); + } + + if (ds->ds_num != data->values_len) + { + ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i", + data->type, ds->ds_num, data->values_len); + return (-1); + } + + /* We need a copy of all the OIDs, because GETNEXT will destroy them. */ + oid_list_len = data->values_len + 1; + oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len)); + if (oid_list == NULL) + 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)); + + /* Allocate the `value_table' */ + value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *) + * 2 * data->values_len); + if (value_table == NULL) + { + sfree (oid_list); + return (-1); + } + memset (value_table, '\0', sizeof (csnmp_table_values_t *) * 2 * data->values_len); + value_table_ptr = value_table + data->values_len; + + instance_list = NULL; + instance_list_ptr = NULL; + + status = 0; + while (status == 0) + { + csnmp_list_instances_t *il; + + req = snmp_pdu_create (SNMP_MSG_GETNEXT); + if (req == NULL) + { + ERROR ("snmp plugin: snmp_pdu_create failed."); + status = -1; + break; + } + + for (i = 0; i < oid_list_len; i++) + snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len); + + status = snmp_sess_synch_response (host->sess_handle, req, &res); + + if (status != STAT_SUCCESS) + { + 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); + + status = -1; + break; + } + status = 0; + assert (res != NULL); + + vb = res->variables; + if (vb == 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) + { + status = -1; + 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)) + { + 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++) + { + 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); + 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; + if (vb == NULL) + { + status = -1; + break; + } + + /* 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) + { + DEBUG ("snmp plugin: host = %s; data = %s; Value %i left its subtree.", + host->name, data->name, i); + continue; + } + + 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.", + host->name, data->name, i); + continue; + } + + vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t)); + 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; + } + + /* 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; + } /* for (i = data->values_len) */ + + if (res != NULL) + snmp_free_pdu (res); + res = NULL; + } /* while (status == 0) */ + + if (status == 0) + csnmp_dispatch_table (host, data, instance_list, value_table); + + /* Free all allocated variables here */ + while (instance_list != NULL) + { + instance_list_ptr = instance_list->next; + sfree (instance_list); + instance_list = instance_list_ptr; + } + + for (i = 0; i < data->values_len; i++) + { + csnmp_table_values_t *tmp; + while (value_table[i] != NULL) + { + tmp = value_table[i]->next; + sfree (value_table[i]); + value_table[i] = tmp; + } + } + + sfree (value_table); + sfree (oid_list); + return (0); } /* int csnmp_read_table */ -static int csnmp_read_value (struct snmp_session *sess_ptr, - host_definition_t *host, data_definition_t *data) +static int csnmp_read_value (host_definition_t *host, data_definition_t *data) { struct snmp_pdu *req; struct snmp_pdu *res; @@ -669,6 +1057,8 @@ static int csnmp_read_value (struct snmp_session *sess_ptr, 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; + req = snmp_pdu_create (SNMP_MSG_GET); if (req == NULL) { @@ -679,12 +1069,18 @@ static int csnmp_read_value (struct snmp_session *sess_ptr, for (i = 0; i < data->values_len; i++) snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len); - status = snmp_synch_response (sess_ptr, req, &res); + status = snmp_sess_synch_response (host->sess_handle, req, &res); if (status != STAT_SUCCESS) { - ERROR ("snmp plugin: snmp_synch_response failed."); - sfree (vl.values); + 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); + sfree (errstr); + return (-1); } @@ -707,42 +1103,119 @@ static int csnmp_read_value (struct snmp_session *sess_ptr, DEBUG ("snmp plugin: -> plugin_dispatch_values (%s, &vl);", data->type); plugin_dispatch_values (data->type, &vl); + sfree (vl.values); return (0); } /* int csnmp_read_value */ static int csnmp_read_host (host_definition_t *host) { - struct snmp_session *sess_ptr; int i; DEBUG ("snmp plugin: csnmp_read_host (%s);", host->name); - sess_ptr = snmp_open (&host->sess); - if (sess_ptr == NULL) - { - snmp_perror ("snmp_open"); - ERROR ("snmp plugin: snmp_open failed."); + if (host->sess_handle == NULL) + csnmp_host_open_session (host); + + if (host->sess_handle == NULL) return (-1); - } for (i = 0; i < host->data_list_len; i++) { data_definition_t *data = host->data_list[i]; if (data->is_table) - csnmp_read_table (sess_ptr, host, data); + csnmp_read_table (host, data); else - csnmp_read_value (sess_ptr, host, data); + csnmp_read_value (host, data); } - snmp_close (sess_ptr); 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) + return (-1); + + call_snmp_init_once (); + + threads_num = 0; + for (host = host_head; host != NULL; host = host->next) + { + threads_num++; + /* 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) */ + + /* 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) + 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) { @@ -750,17 +1223,90 @@ static int csnmp_read (void) return (-1); } + now = time (NULL); + + pthread_mutex_lock (&host_lock); for (host = host_head; host != NULL; host = host->next) - csnmp_read_host (host); + { + if (host->state != STATE_IDLE) + continue; + + host->skip_left -= interval_g; + if (host->skip_left >= interval_g) + continue; + + host->state = STATE_WAIT; + + host->skip_left = host->skip_num; + } /* 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 */ /*