X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fethstat.c;h=dec14f3d148962df3b1aaf41eb1f50f0a7a5f92c;hb=9b2419163f4a850bff9ddef9c9d50cb2b56e1fbf;hp=b280a585ba69c52e8194d9e576265c58f4163714;hpb=8ae25a973cdc30083959fcc1c422504c75d355b9;p=collectd.git diff --git a/src/ethstat.c b/src/ethstat.c index b280a585..dec14f3d 100644 --- a/src/ethstat.c +++ b/src/ethstat.c @@ -1,6 +1,7 @@ /** * collectd - src/ethstat.c * Copyright (C) 2011 Cyril Feraudet + * Copyright (C) 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 @@ -18,12 +19,15 @@ * * Authors: * Cyril Feraudet + * Florian "octo" Forster **/ #include "collectd.h" #include "common.h" #include "plugin.h" #include "configfile.h" +#include "utils_avltree.h" +#include "utils_complain.h" #if HAVE_SYS_IOCTL_H # include @@ -38,187 +42,327 @@ # include #endif -static const char *config_keys[] = +struct value_map_s { - "Interface" + char type[DATA_MAX_NAME_LEN]; + char type_instance[DATA_MAX_NAME_LEN]; }; -static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); +typedef struct value_map_s value_map_t; static char **interfaces = NULL; static size_t interfaces_num = 0; -static int ethstat_config (const char *key, const char *value) +static c_avl_tree_t *value_map = NULL; + +static _Bool collect_mapped_only = 0; + +static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */ { - if (strcasecmp ("Interface", key) == 0) - { - char **tmp; - - tmp = realloc (interfaces, - sizeof (*interfaces) * (interfaces_num + 1)); - if (tmp == NULL) - return (-1); - interfaces = tmp; - - interfaces[interfaces_num] = strdup (value); - if (interfaces[interfaces_num] == NULL) - { - ERROR ("ethstat plugin: strdup() failed."); - return (-1); - } - - interfaces_num++; - INFO("ethstat plugin: Registred interface %s", value); - } - return (0); -} + char **tmp; + int status; + + tmp = realloc (interfaces, + sizeof (*interfaces) * (interfaces_num + 1)); + if (tmp == NULL) + return (-1); + interfaces = tmp; + interfaces[interfaces_num] = NULL; + + status = cf_util_get_string (ci, interfaces + interfaces_num); + if (status != 0) + return (status); + + interfaces_num++; + INFO("ethstat plugin: Registered interface %s", + interfaces[interfaces_num - 1]); + + return (0); +} /* }}} int ethstat_add_interface */ + +static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */ +{ + value_map_t *map; + int status; + char *key; + + if ((ci->values_num < 2) + || (ci->values_num > 3) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || (ci->values[1].type != OCONFIG_TYPE_STRING) + || ((ci->values_num == 3) + && (ci->values[2].type != OCONFIG_TYPE_STRING))) + { + ERROR ("ethstat plugin: The %s option requires " + "two or three string arguments.", ci->key); + return (-1); + } + + key = strdup (ci->values[0].value.string); + if (key == NULL) + { + ERROR ("ethstat plugin: strdup(3) failed."); + return (ENOMEM); + } + + map = malloc (sizeof (*map)); + if (map == NULL) + { + sfree (key); + ERROR ("ethstat plugin: malloc(3) failed."); + return (ENOMEM); + } + memset (map, 0, sizeof (*map)); + + sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type)); + if (ci->values_num == 3) + sstrncpy (map->type_instance, ci->values[2].value.string, + sizeof (map->type_instance)); + + if (value_map == NULL) + { + value_map = c_avl_create ((void *) strcmp); + if (value_map == NULL) + { + sfree (map); + sfree (key); + ERROR ("ethstat plugin: c_avl_create() failed."); + return (-1); + } + } + + status = c_avl_insert (value_map, + /* key = */ key, + /* value = */ map); + if (status != 0) + { + if (status > 0) + ERROR ("ethstat plugin: Multiple mappings for \"%s\".", key); + else + ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.", key); + + sfree (map); + sfree (key); + return (-1); + } + + return (0); +} /* }}} int ethstat_add_map */ + +static int ethstat_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Interface", child->key) == 0) + ethstat_add_interface (child); + else if (strcasecmp ("Map", child->key) == 0) + ethstat_add_map (child); + else if (strcasecmp ("MappedOnly", child->key) == 0) + (void) cf_util_get_boolean (child, &collect_mapped_only); + else + WARNING ("ethstat plugin: The config option \"%s\" is unknown.", + child->key); + } + + return (0); +} /* }}} */ static void ethstat_submit_value (const char *device, - const char *type_instance, derive_t value) + const char *type_instance, derive_t value) { - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; + static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC; + + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + value_map_t *map = NULL; - values[0].derive = value; - vl.values = values; - vl.values_len = 1; + if (value_map != NULL) + c_avl_get (value_map, type_instance, (void *) &map); - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin)); - sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance)); - sstrncpy (vl.type, "derive", sizeof (vl.type)); - sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + /* If the "MappedOnly" option is specified, ignore unmapped values. */ + if (collect_mapped_only && (map == NULL)) + { + if (value_map == NULL) + c_complain (LOG_WARNING, &complain_no_map, + "ethstat plugin: The \"MappedOnly\" option has been set to true, " + "but no mapping has been configured. All values will be ignored!"); + return; + } - plugin_dispatch_values (&vl); + values[0].derive = value; + vl.values = values; + vl.values_len = 1; + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance)); + if (map != NULL) + { + sstrncpy (vl.type, map->type, sizeof (vl.type)); + sstrncpy (vl.type_instance, map->type_instance, + sizeof (vl.type_instance)); + } + else + { + sstrncpy (vl.type, "derive", sizeof (vl.type)); + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + } + + plugin_dispatch_values (&vl); } static int ethstat_read_interface (char *device) { - int fd; - struct ifreq req; - struct ethtool_drvinfo drvinfo; - struct ethtool_gstrings *strings; - struct ethtool_stats *stats; - size_t n_stats; - size_t strings_size; - size_t stats_size; - size_t i; - int status; - - memset (&req, 0, sizeof (req)); - sstrncpy(req.ifr_name, device, sizeof (req.ifr_name)); - - fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0); - if (fd < 0) - { - char errbuf[1024]; - ERROR("ethstat plugin: Failed to open control socket: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - return 1; - } - - memset (&drvinfo, 0, sizeof (drvinfo)); - drvinfo.cmd = ETHTOOL_GDRVINFO; - req.ifr_data = (void *) &drvinfo; - status = ioctl (fd, SIOCETHTOOL, &req); - if (status < 0) - { - char errbuf[1024]; - close (fd); - ERROR ("ethstat plugin: Failed to get driver information " - "from %s: %s", device, - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); - } - - n_stats = (size_t) drvinfo.n_stats; - if (n_stats < 1) - { - close (fd); - ERROR("ethstat plugin: No stats available for %s", device); - return (-1); - } - - strings_size = sizeof (struct ethtool_gstrings) - + (n_stats * ETH_GSTRING_LEN); - stats_size = sizeof (struct ethtool_stats) - + (n_stats * sizeof (uint64_t)); - - strings = malloc (strings_size); - stats = malloc (stats_size); - if ((strings == NULL) || (stats == NULL)) - { - close (fd); - sfree (strings); - sfree (stats); - ERROR("ethstat plugin: malloc(3) failed."); - return (-1); - } - - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = ETH_SS_STATS; - strings->len = n_stats; - req.ifr_data = (void *) strings; - status = ioctl (fd, SIOCETHTOOL, &req); - if (status < 0) - { - char errbuf[1024]; - close (fd); - free (strings); - free (stats); - ERROR ("ethstat plugin: Cannot get strings from %s: %s", - device, - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); - } - - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = n_stats; - req.ifr_data = (void *) stats; - status = ioctl (fd, SIOCETHTOOL, &req); - if (status < 0) - { - char errbuf[1024]; - close (fd); - free(strings); - free(stats); - ERROR("ethstat plugin: Reading statistics from %s failed: %s", - device, - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); - } - - for (i = 0; i < n_stats; i++) - { - const char *stat_name; - - stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN], - DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64, - device, stat_name, - (uint64_t) stats->data[i]); - ethstat_submit_value (device, - stat_name, (derive_t) stats->data[i]); - } - - close (fd); - sfree (strings); - sfree (stats); - - return (0); + int fd; + struct ifreq req; + struct ethtool_drvinfo drvinfo; + struct ethtool_gstrings *strings; + struct ethtool_stats *stats; + size_t n_stats; + size_t strings_size; + size_t stats_size; + size_t i; + int status; + + memset (&req, 0, sizeof (req)); + sstrncpy(req.ifr_name, device, sizeof (req.ifr_name)); + + fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0); + if (fd < 0) + { + char errbuf[1024]; + ERROR("ethstat plugin: Failed to open control socket: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + memset (&drvinfo, 0, sizeof (drvinfo)); + drvinfo.cmd = ETHTOOL_GDRVINFO; + req.ifr_data = (void *) &drvinfo; + status = ioctl (fd, SIOCETHTOOL, &req); + if (status < 0) + { + char errbuf[1024]; + close (fd); + ERROR ("ethstat plugin: Failed to get driver information " + "from %s: %s", device, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + n_stats = (size_t) drvinfo.n_stats; + if (n_stats < 1) + { + close (fd); + ERROR("ethstat plugin: No stats available for %s", device); + return (-1); + } + + strings_size = sizeof (struct ethtool_gstrings) + + (n_stats * ETH_GSTRING_LEN); + stats_size = sizeof (struct ethtool_stats) + + (n_stats * sizeof (uint64_t)); + + strings = malloc (strings_size); + stats = malloc (stats_size); + if ((strings == NULL) || (stats == NULL)) + { + close (fd); + sfree (strings); + sfree (stats); + ERROR("ethstat plugin: malloc(3) failed."); + return (-1); + } + + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_STATS; + strings->len = n_stats; + req.ifr_data = (void *) strings; + status = ioctl (fd, SIOCETHTOOL, &req); + if (status < 0) + { + char errbuf[1024]; + close (fd); + free (strings); + free (stats); + ERROR ("ethstat plugin: Cannot get strings from %s: %s", + device, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = n_stats; + req.ifr_data = (void *) stats; + status = ioctl (fd, SIOCETHTOOL, &req); + if (status < 0) + { + char errbuf[1024]; + close (fd); + free(strings); + free(stats); + ERROR("ethstat plugin: Reading statistics from %s failed: %s", + device, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + for (i = 0; i < n_stats; i++) + { + const char *stat_name; + + stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN]; + DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64, + device, stat_name, (uint64_t) stats->data[i]); + ethstat_submit_value (device, + stat_name, (derive_t) stats->data[i]); + } + + close (fd); + sfree (strings); + sfree (stats); + + return (0); } /* }}} ethstat_read_interface */ static int ethstat_read(void) { - size_t i; + size_t i; - for (i = 0; i < interfaces_num; i++) - ethstat_read_interface (interfaces[i]); + for (i = 0; i < interfaces_num; i++) + ethstat_read_interface (interfaces[i]); - return 0; + return 0; +} + +static int ethstat_shutdown (void) +{ + void *key = NULL; + void *value = NULL; + + if (value_map == NULL) + return (0); + + while (c_avl_pick (value_map, &key, &value) == 0) + { + sfree (key); + sfree (value); + } + + c_avl_destroy (value_map); + value_map = NULL; + + return (0); } void module_register (void) { - plugin_register_config ("ethstat", ethstat_config, - config_keys, config_keys_num); - plugin_register_read ("ethstat", ethstat_read); + plugin_register_complex_config ("ethstat", ethstat_config); + plugin_register_read ("ethstat", ethstat_read); + plugin_register_shutdown ("ethstat", ethstat_shutdown); } + +/* vim: set sw=2 sts=2 et fdm=marker : */