2 * collectd - src/ethstat.c
3 * Copyright (C) 2011 Cyril Feraudet
4 * Copyright (C) 2012 Florian "octo" Forster
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Cyril Feraudet <cyril at feraudet.com>
22 * Florian "octo" Forster <octo@collectd.org>
29 #include "utils_avltree.h"
30 #include "utils_complain.h"
33 #include <sys/ioctl.h>
38 #if HAVE_LINUX_SOCKIOS_H
39 #include <linux/sockios.h>
41 #if HAVE_LINUX_ETHTOOL_H
42 #include <linux/ethtool.h>
46 char type[DATA_MAX_NAME_LEN];
47 char type_instance[DATA_MAX_NAME_LEN];
49 typedef struct value_map_s value_map_t;
51 static char **interfaces;
52 static size_t interfaces_num;
54 static c_avl_tree_t *value_map;
56 static bool collect_mapped_only;
58 static int ethstat_add_interface(const oconfig_item_t *ci) /* {{{ */
63 tmp = realloc(interfaces, sizeof(*interfaces) * (interfaces_num + 1));
67 interfaces[interfaces_num] = NULL;
69 status = cf_util_get_string(ci, interfaces + interfaces_num);
74 INFO("ethstat plugin: Registered interface %s",
75 interfaces[interfaces_num - 1]);
78 } /* }}} int ethstat_add_interface */
80 static int ethstat_add_map(const oconfig_item_t *ci) /* {{{ */
86 if ((ci->values_num < 2) || (ci->values_num > 3) ||
87 (ci->values[0].type != OCONFIG_TYPE_STRING) ||
88 (ci->values[1].type != OCONFIG_TYPE_STRING) ||
89 ((ci->values_num == 3) && (ci->values[2].type != OCONFIG_TYPE_STRING))) {
90 ERROR("ethstat plugin: The %s option requires "
91 "two or three string arguments.",
96 key = strdup(ci->values[0].value.string);
98 ERROR("ethstat plugin: strdup(3) failed.");
102 map = calloc(1, sizeof(*map));
105 ERROR("ethstat plugin: calloc failed.");
109 sstrncpy(map->type, ci->values[1].value.string, sizeof(map->type));
110 if (ci->values_num == 3)
111 sstrncpy(map->type_instance, ci->values[2].value.string,
112 sizeof(map->type_instance));
114 if (value_map == NULL) {
115 value_map = c_avl_create((int (*)(const void *, const void *))strcmp);
116 if (value_map == NULL) {
119 ERROR("ethstat plugin: c_avl_create() failed.");
124 status = c_avl_insert(value_map,
129 ERROR("ethstat plugin: Multiple mappings for \"%s\".", key);
131 ERROR("ethstat plugin: c_avl_insert(\"%s\") failed.", key);
139 } /* }}} int ethstat_add_map */
141 static int ethstat_config(oconfig_item_t *ci) /* {{{ */
143 for (int i = 0; i < ci->children_num; i++) {
144 oconfig_item_t *child = ci->children + i;
146 if (strcasecmp("Interface", child->key) == 0)
147 ethstat_add_interface(child);
148 else if (strcasecmp("Map", child->key) == 0)
149 ethstat_add_map(child);
150 else if (strcasecmp("MappedOnly", child->key) == 0)
151 (void)cf_util_get_boolean(child, &collect_mapped_only);
153 WARNING("ethstat plugin: The config option \"%s\" is unknown.",
160 static void ethstat_submit_value(const char *device, const char *type_instance,
162 static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC;
164 value_list_t vl = VALUE_LIST_INIT;
165 value_map_t *map = NULL;
167 if (value_map != NULL)
168 c_avl_get(value_map, type_instance, (void *)&map);
170 /* If the "MappedOnly" option is specified, ignore unmapped values. */
171 if (collect_mapped_only && (map == NULL)) {
172 if (value_map == NULL)
174 LOG_WARNING, &complain_no_map,
175 "ethstat plugin: The \"MappedOnly\" option has been set to true, "
176 "but no mapping has been configured. All values will be ignored!");
180 vl.values = &(value_t){.derive = value};
183 sstrncpy(vl.plugin, "ethstat", sizeof(vl.plugin));
184 sstrncpy(vl.plugin_instance, device, sizeof(vl.plugin_instance));
186 sstrncpy(vl.type, map->type, sizeof(vl.type));
187 sstrncpy(vl.type_instance, map->type_instance, sizeof(vl.type_instance));
189 sstrncpy(vl.type, "derive", sizeof(vl.type));
190 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
193 plugin_dispatch_values(&vl);
196 static int ethstat_read_interface(char *device) {
198 struct ethtool_gstrings *strings;
199 struct ethtool_stats *stats;
205 fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
207 ERROR("ethstat plugin: Failed to open control socket: %s", STRERRNO);
211 struct ethtool_drvinfo drvinfo = {.cmd = ETHTOOL_GDRVINFO};
213 struct ifreq req = {.ifr_data = (void *)&drvinfo};
215 sstrncpy(req.ifr_name, device, sizeof(req.ifr_name));
217 status = ioctl(fd, SIOCETHTOOL, &req);
220 ERROR("ethstat plugin: Failed to get driver information "
226 n_stats = (size_t)drvinfo.n_stats;
229 ERROR("ethstat plugin: No stats available for %s", device);
233 strings_size = sizeof(struct ethtool_gstrings) + (n_stats * ETH_GSTRING_LEN);
234 stats_size = sizeof(struct ethtool_stats) + (n_stats * sizeof(uint64_t));
236 strings = malloc(strings_size);
237 stats = malloc(stats_size);
238 if ((strings == NULL) || (stats == NULL)) {
242 ERROR("ethstat plugin: malloc failed.");
246 strings->cmd = ETHTOOL_GSTRINGS;
247 strings->string_set = ETH_SS_STATS;
248 strings->len = n_stats;
249 req.ifr_data = (void *)strings;
250 status = ioctl(fd, SIOCETHTOOL, &req);
255 ERROR("ethstat plugin: Cannot get strings from %s: %s", device, STRERRNO);
259 stats->cmd = ETHTOOL_GSTATS;
260 stats->n_stats = n_stats;
261 req.ifr_data = (void *)stats;
262 status = ioctl(fd, SIOCETHTOOL, &req);
267 ERROR("ethstat plugin: Reading statistics from %s failed: %s", device,
272 for (size_t i = 0; i < n_stats; i++) {
275 stat_name = (void *)&strings->data[i * ETH_GSTRING_LEN];
276 /* Remove leading spaces in key name */
277 while (isspace((int)*stat_name))
280 DEBUG("ethstat plugin: device = \"%s\": %s = %" PRIu64, device, stat_name,
281 (uint64_t)stats->data[i]);
282 ethstat_submit_value(device, stat_name, (derive_t)stats->data[i]);
290 } /* }}} ethstat_read_interface */
292 static int ethstat_read(void) {
293 for (size_t i = 0; i < interfaces_num; i++)
294 ethstat_read_interface(interfaces[i]);
299 static int ethstat_shutdown(void) {
303 if (value_map == NULL)
306 while (c_avl_pick(value_map, &key, &value) == 0) {
311 c_avl_destroy(value_map);
317 void module_register(void) {
318 plugin_register_complex_config("ethstat", ethstat_config);
319 plugin_register_read("ethstat", ethstat_read);
320 plugin_register_shutdown("ethstat", ethstat_shutdown);