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 = NULL;
52 static size_t interfaces_num = 0;
54 static c_avl_tree_t *value_map = NULL;
56 static _Bool collect_mapped_only = 0;
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);
208 ERROR("ethstat plugin: Failed to open control socket: %s",
209 sstrerror(errno, errbuf, sizeof(errbuf)));
213 struct ethtool_drvinfo drvinfo = {.cmd = ETHTOOL_GDRVINFO};
215 struct ifreq req = {.ifr_data = (void *)&drvinfo};
217 sstrncpy(req.ifr_name, device, sizeof(req.ifr_name));
219 status = ioctl(fd, SIOCETHTOOL, &req);
223 ERROR("ethstat plugin: Failed to get driver information "
225 device, sstrerror(errno, errbuf, sizeof(errbuf)));
229 n_stats = (size_t)drvinfo.n_stats;
232 ERROR("ethstat plugin: No stats available for %s", device);
236 strings_size = sizeof(struct ethtool_gstrings) + (n_stats * ETH_GSTRING_LEN);
237 stats_size = sizeof(struct ethtool_stats) + (n_stats * sizeof(uint64_t));
239 strings = malloc(strings_size);
240 stats = malloc(stats_size);
241 if ((strings == NULL) || (stats == NULL)) {
245 ERROR("ethstat plugin: malloc failed.");
249 strings->cmd = ETHTOOL_GSTRINGS;
250 strings->string_set = ETH_SS_STATS;
251 strings->len = n_stats;
252 req.ifr_data = (void *)strings;
253 status = ioctl(fd, SIOCETHTOOL, &req);
259 ERROR("ethstat plugin: Cannot get strings from %s: %s", device,
260 sstrerror(errno, errbuf, sizeof(errbuf)));
264 stats->cmd = ETHTOOL_GSTATS;
265 stats->n_stats = n_stats;
266 req.ifr_data = (void *)stats;
267 status = ioctl(fd, SIOCETHTOOL, &req);
273 ERROR("ethstat plugin: Reading statistics from %s failed: %s", device,
274 sstrerror(errno, errbuf, sizeof(errbuf)));
278 for (size_t i = 0; i < n_stats; i++) {
281 stat_name = (void *)&strings->data[i * ETH_GSTRING_LEN];
282 /* Remove leading spaces in key name */
283 while (isspace((int)*stat_name))
286 DEBUG("ethstat plugin: device = \"%s\": %s = %" PRIu64, device, stat_name,
287 (uint64_t)stats->data[i]);
288 ethstat_submit_value(device, stat_name, (derive_t)stats->data[i]);
296 } /* }}} ethstat_read_interface */
298 static int ethstat_read(void) {
299 for (size_t i = 0; i < interfaces_num; i++)
300 ethstat_read_interface(interfaces[i]);
305 static int ethstat_shutdown(void) {
309 if (value_map == NULL)
312 while (c_avl_pick(value_map, &key, &value) == 0) {
317 c_avl_destroy(value_map);
323 void module_register(void) {
324 plugin_register_complex_config("ethstat", ethstat_config);
325 plugin_register_read("ethstat", ethstat_read);
326 plugin_register_shutdown("ethstat", ethstat_shutdown);