diff --git a/src/ethstat.c b/src/ethstat.c
index 8bc47ad4a071d3cfafa7eba77014352d7fa71807..a6635c5761905a1963ed9d74e3f471c5c436378d 100644 (file)
--- a/src/ethstat.c
+++ b/src/ethstat.c
/**
* 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
*
* Authors:
* Cyril Feraudet <cyril at feraudet.com>
+ * Florian "octo" Forster <octo@collectd.org>
**/
#include "collectd.h"
+
#include "common.h"
#include "plugin.h"
-#include "configfile.h"
-#include "ethstat.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
#include <net/if.h>
+#endif
+#if HAVE_LINUX_SOCKIOS_H
#include <linux/sockios.h>
+#endif
+#if HAVE_LINUX_ETHTOOL_H
+#include <linux/ethtool.h>
+#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 = NULL;
+static size_t interfaces_num = 0;
+
+static c_avl_tree_t *value_map = NULL;
-static int ethstat_config (const char *key, const char *value)
+static _Bool collect_mapped_only = 0;
+
+static int ethstat_add_interface(const oconfig_item_t *ci) /* {{{ */
{
- if (strcasecmp ("Iface", key) == 0)
- {
- char **tmp;
-
- tmp = realloc (ifacelist,
- sizeof (*ifacelist) * (ifacenumber + 1));
- if (tmp == NULL)
- return (-1);
- ifacelist = tmp;
-
- ifacelist[ifacenumber] = strdup (value);
- if (ifacelist[ifacenumber] == NULL)
- {
- ERROR ("ethstat plugin: strdup() failed.");
- return (-1);
- }
-
- ifacenumber++;
- 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;
-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_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+ value_map_t *map = NULL;
+
+ if (value_map != NULL)
+ c_avl_get(value_map, type_instance, (void *)&map);
- values[0].counter = value;
+ /* 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;
+ }
- 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));
+ values[0].derive = value;
+ vl.values = values;
+ vl.values_len = 1;
- plugin_dispatch_values (&vl);
+ 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 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) {
+ char errbuf[1024];
+ ERROR("ethstat plugin: Failed to open control socket: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ 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) {
+ 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 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 (size_t i = 0; i < n_stats; i++) {
+ char *stat_name;
-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;
+ 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_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));
- sstrncpy(ifr.ifr_name, ifacelist[i], sizeof (ifr.ifr_name));
- getstats(ifacelist[i], &ifr);
- }
- 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);
+void module_register(void) {
+ 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 : */