X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fethstat.c;h=f8bc5b540ab29cbebbe269e3e01b819a83952ca9;hb=88d676f631cd868066b564e83cf2dcadf9c46219;hp=d3343746a13f0aaefded13c801e55af1636e894c;hpb=da3b65f95d6e467c324a81afc62acba33f82e164;p=collectd.git diff --git a/src/ethstat.c b/src/ethstat.c index d3343746..f8bc5b54 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,147 +19,303 @@ * * Authors: * Cyril Feraudet + * Florian "octo" Forster **/ #include "collectd.h" -#include "common.h" + #include "plugin.h" -#include "configfile.h" -#include "ethstat.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils_complain.h" +#if HAVE_SYS_IOCTL_H #include +#endif +#if HAVE_NET_IF_H #include +#endif +#if HAVE_LINUX_SOCKIOS_H #include +#endif +#if HAVE_LINUX_ETHTOOL_H +#include +#endif + +struct value_map_s { + char type[DATA_MAX_NAME_LEN]; + char type_instance[DATA_MAX_NAME_LEN]; +}; +typedef struct value_map_s value_map_t; + +static char **interfaces; +static size_t interfaces_num; -static int ethstat_config (const char *key, const char *value) +static c_avl_tree_t *value_map; + +static bool collect_mapped_only; + +static int ethstat_add_interface(const oconfig_item_t *ci) /* {{{ */ { - if (strcasecmp (key, "Iface") == 0) { - ifacelist[ifacenumber] = malloc(strlen(value) + 1); - strcpy(ifacelist[ifacenumber++], value); - INFO("ethstat: Registred iface %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; -static void ethstat_submit_value (char *devname, char *counter, unsigned long long value) + 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_t values[1]; - value_list_t vl = VALUE_LIST_INIT; + 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 = calloc(1, sizeof(*map)); + if (map == NULL) { + sfree(key); + ERROR("ethstat plugin: calloc failed."); + return ENOMEM; + } + + 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((int (*)(const void *, const 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) /* {{{ */ +{ + for (int 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) { + static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC; + + value_list_t vl = VALUE_LIST_INIT; + value_map_t *map = NULL; - values[0].counter = value; + if (value_map != NULL) + c_avl_get(value_map, type_instance, (void *)&map); - 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, devname, sizeof (vl.plugin_instance)); - sstrncpy (vl.type, "derive", sizeof (vl.type)); - sstrncpy (vl.type_instance, counter, 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); + vl.values = &(value_t){.derive = value}; + vl.values_len = 1; + + 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 ethtool_gstrings *strings; + struct ethtool_stats *stats; + size_t n_stats; + size_t strings_size; + size_t stats_size; + int status; + + fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0); + if (fd < 0) { + ERROR("ethstat plugin: Failed to open control socket: %s", STRERRNO); + return 1; + } + + struct ethtool_drvinfo drvinfo = {.cmd = ETHTOOL_GDRVINFO}; + + struct ifreq req = {.ifr_data = (void *)&drvinfo}; + + sstrncpy(req.ifr_name, device, sizeof(req.ifr_name)); + + status = ioctl(fd, SIOCETHTOOL, &req); + if (status < 0) { + close(fd); + ERROR("ethstat plugin: Failed to get driver information " + "from %s: %s", + device, STRERRNO); + 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 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) { + close(fd); + free(strings); + free(stats); + ERROR("ethstat plugin: Cannot get strings from %s: %s", device, STRERRNO); + return -1; + } + + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = n_stats; + req.ifr_data = (void *)stats; + status = ioctl(fd, SIOCETHTOOL, &req); + if (status < 0) { + close(fd); + free(strings); + free(stats); + ERROR("ethstat plugin: Reading statistics from %s failed: %s", device, + STRERRNO); + return -1; + } -static int getstats(char *devname, struct ifreq *ifr) { - int fd; - struct ethtool_drvinfo drvinfo; - struct ethtool_gstrings *strings; - struct ethtool_stats *stats; - unsigned int n_stats, sz_str, sz_stats, i; - int err; - - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - ERROR("ethstat - %s : Cannot get control socket", devname); - return 1; - } - - drvinfo.cmd = ETHTOOL_GDRVINFO; - ifr->ifr_data = (caddr_t)&drvinfo; - err = ioctl(fd, SIOCETHTOOL, ifr); - if (err < 0) { - ERROR("ethstat - %s : Cannot get driver information", devname); - return 1; - } - - - n_stats = drvinfo.n_stats; - if (n_stats < 1) { - ERROR("ethstat - %s : No stats available", devname); - return 1; - } - - sz_str = n_stats * ETH_GSTRING_LEN; - sz_stats = n_stats * sizeof(u64); - - strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings)); - stats = calloc(1, sz_stats + sizeof(struct ethtool_stats)); - if (!strings || !stats) { - ERROR("ethstat - %s No memory available", devname); - return 1; - } - - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = ETH_SS_STATS; - strings->len = n_stats; - ifr->ifr_data = (caddr_t) strings; - err = ioctl(fd, SIOCETHTOOL, ifr); - if (err < 0) { - ERROR("ethstat - %s : Cannot get stats strings information", devname); - free(strings); - free(stats); - return 96; - } - - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = n_stats; - ifr->ifr_data = (caddr_t) stats; - err = ioctl(fd, SIOCETHTOOL, ifr); - if (err < 0) { - ERROR("ethstat - %s : Cannot get stats information", devname); - free(strings); - free(stats); - return 97; - } - - for (i = 0; i < n_stats; i++) { - DEBUG("ethstat - %s : %s: %llu", - devname, - &strings->data[i * ETH_GSTRING_LEN], - stats->data[i]); - ethstat_submit_value ( - devname, - (char*)&strings->data[i * ETH_GSTRING_LEN], - stats->data[i]); - } - free(strings); - free(stats); - - - return 0; + for (size_t i = 0; i < n_stats; i++) { + char *stat_name; + + stat_name = (void *)&strings->data[i * ETH_GSTRING_LEN]; + /* Remove leading spaces in key name */ + while (isspace((int)*stat_name)) + stat_name++; + + 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) { + for (size_t i = 0; i < interfaces_num; i++) + ethstat_read_interface(interfaces[i]); + + return 0; } +static int ethstat_shutdown(void) { + void *key = NULL; + void *value = NULL; -static int ethstat_read(void) -{ - struct ifreq ifr; - int i; - - for (i = 0 ; i < ifacenumber ; i++) { - DEBUG("ethstat - Processing : %s\n", ifacelist[i]); - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, ifacelist[i]); - getstats(ifacelist[i], &ifr); - } - return 0; + 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) -{ - ifacelist = malloc(sizeof(char*)); - plugin_register_config ("ethstat", ethstat_config, - config_keys, config_keys_num); - plugin_register_read ("ethstat", ethstat_read); +void module_register(void) { + plugin_register_complex_config("ethstat", ethstat_config); + plugin_register_read("ethstat", ethstat_read); + plugin_register_shutdown("ethstat", ethstat_shutdown); }