Code

Merge branch 'jr/varnish'
[collectd.git] / src / ethstat.c
1 /**
2  * collectd - src/ethstat.c
3  * Copyright (C) 2011       Cyril Feraudet
4  * Copyright (C) 2012       Florian "octo" Forster
5  *
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.
10  *
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.
15  *
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
19  *
20  * Authors:
21  *   Cyril Feraudet <cyril at feraudet.com>
22  *   Florian "octo" Forster <octo@collectd.org>
23  **/
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
28 #include "configfile.h"
29 #include "utils_avltree.h"
30 #include "utils_complain.h"
32 #if HAVE_SYS_IOCTL_H
33 # include <sys/ioctl.h>
34 #endif
35 #if HAVE_NET_IF_H
36 # include <net/if.h>
37 #endif
38 #if HAVE_LINUX_SOCKIOS_H
39 # include <linux/sockios.h>
40 #endif
41 #if HAVE_LINUX_ETHTOOL_H
42 # include <linux/ethtool.h>
43 #endif
45 struct value_map_s
46 {
47   char type[DATA_MAX_NAME_LEN];
48   char type_instance[DATA_MAX_NAME_LEN];
49 };
50 typedef struct value_map_s value_map_t;
52 static char **interfaces = NULL;
53 static size_t interfaces_num = 0;
55 static c_avl_tree_t *value_map = NULL;
57 static _Bool collect_mapped_only = 0;
59 static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
60 {
61   char **tmp;
62   int status;
64   tmp = realloc (interfaces,
65       sizeof (*interfaces) * (interfaces_num + 1));
66   if (tmp == NULL)
67     return (-1);
68   interfaces = tmp;
70   status = cf_util_get_string (ci, interfaces + interfaces_num);
71   if (status != 0)
72     return (status);
74   interfaces_num++;
75   INFO("ethstat plugin: Registred interface %s",
76       interfaces[interfaces_num - 1]);
78   return (0);
79 } /* }}} int ethstat_add_interface */
81 static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
82 {
83   value_map_t *map;
84   int status;
86   if ((ci->values_num < 2)
87       || (ci->values_num > 3)
88       || (ci->values[0].type != OCONFIG_TYPE_STRING)
89       || (ci->values[1].type != OCONFIG_TYPE_STRING)
90       || ((ci->values_num == 3)
91         && (ci->values[2].type != OCONFIG_TYPE_STRING)))
92   {
93     ERROR ("ethstat plugin: The %s option requires "
94         "two or three string arguments.", ci->key);
95     return (-1);
96   }
98   map = malloc (sizeof (*map));
99   if (map == NULL)
100   {
101     ERROR ("ethstat plugin: malloc(3) failed.");
102     return (ENOMEM);
103   }
104   memset (map, 0, sizeof (*map));
106   sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type));
107   if (ci->values_num == 2)
108     sstrncpy (map->type_instance, ci->values[2].value.string,
109         sizeof (map->type_instance));
111   if (value_map == NULL)
112   {
113     value_map = c_avl_create ((void *) strcmp);
114     if (value_map == NULL)
115     {
116       sfree (map);
117       ERROR ("ethstat plugin: c_avl_create() failed.");
118       return (-1);
119     }
120   }
122   status = c_avl_insert (value_map,
123       /* key = */ ci->values[0].value.string,
124       /* value = */ map);
125   if (status != 0)
126   {
127     sfree (map);
128     if (status > 0)
129       ERROR ("ethstat plugin: Multiple mappings for \"%s\".",
130           ci->values[0].value.string);
131     else
132       ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.",
133           ci->values[0].value.string);
134     return (-1);
135   }
137   return (0);
138 } /* }}} int ethstat_add_map */
140 static int ethstat_config (oconfig_item_t *ci) /* {{{ */
142   int i;
144   for (i = 0; i < ci->children_num; i++)
145   {
146     oconfig_item_t *child = ci->children + i;
148     if (strcasecmp ("Interface", child->key) == 0)
149       ethstat_add_interface (child);
150     else if (strcasecmp ("Map", child->key) == 0)
151       ethstat_add_map (child);
152     else if (strcasecmp ("MappedOnly", child->key) == 0)
153       (void) cf_util_get_boolean (child, &collect_mapped_only);
154     else
155       WARNING ("ethstat plugin: The config option \"%s\" is unknown.",
156           child->key);
157   }
159   return (0);
160 } /* }}} */
162 static void ethstat_submit_value (const char *device,
163     const char *type_instance, derive_t value)
165   static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC;
167   value_t values[1];
168   value_list_t vl = VALUE_LIST_INIT;
169   value_map_t *map = NULL;
171   if (value_map != NULL)
172     c_avl_get (value_map, type_instance, (void *) &map);
174   /* If the "MappedOnly" option is specified, ignore unmapped values. */
175   if (collect_mapped_only && (map == NULL))
176   {
177     if (value_map == NULL)
178       c_complain (LOG_WARNING, &complain_no_map,
179           "ethstat plugin: The \"MappedOnly\" option has been set to true, "
180           "but no mapping has been configured. All values will be ignored!");
181     return;
182   }
184   values[0].derive = value;
185   vl.values = values;
186   vl.values_len = 1;
188   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
189   sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
190   sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance));
191   if (map != NULL)
192   {
193     sstrncpy (vl.type, map->type, sizeof (vl.type));
194     sstrncpy (vl.type_instance, map->type_instance,
195         sizeof (vl.type_instance));
196   }
197   else
198   {
199     sstrncpy (vl.type, "derive", sizeof (vl.type));
200     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
201   }
203   plugin_dispatch_values (&vl);
206 static int ethstat_read_interface (char *device)
208   int fd;
209   struct ifreq req;
210   struct ethtool_drvinfo drvinfo;
211   struct ethtool_gstrings *strings;
212   struct ethtool_stats *stats;
213   size_t n_stats;
214   size_t strings_size;
215   size_t stats_size;
216   size_t i;
217   int status;
219   memset (&req, 0, sizeof (req));
220   sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
222   fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
223   if (fd < 0)
224   {
225     char errbuf[1024];
226     ERROR("ethstat plugin: Failed to open control socket: %s",
227         sstrerror (errno, errbuf, sizeof (errbuf)));
228     return 1;
229   }
231   memset (&drvinfo, 0, sizeof (drvinfo));
232   drvinfo.cmd = ETHTOOL_GDRVINFO;
233   req.ifr_data = (void *) &drvinfo;
234   status = ioctl (fd, SIOCETHTOOL, &req);
235   if (status < 0)
236   {
237     char errbuf[1024];
238     close (fd);
239     ERROR ("ethstat plugin: Failed to get driver information "
240         "from %s: %s", device,
241         sstrerror (errno, errbuf, sizeof (errbuf)));
242     return (-1);
243   }
245   n_stats = (size_t) drvinfo.n_stats;
246   if (n_stats < 1)
247   {
248     close (fd);
249     ERROR("ethstat plugin: No stats available for %s", device);
250     return (-1);
251   }
253   strings_size = sizeof (struct ethtool_gstrings)
254     + (n_stats * ETH_GSTRING_LEN);
255   stats_size = sizeof (struct ethtool_stats)
256     + (n_stats * sizeof (uint64_t));
258   strings = malloc (strings_size);
259   stats = malloc (stats_size);
260   if ((strings == NULL) || (stats == NULL))
261   {
262     close (fd);
263     sfree (strings);
264     sfree (stats);
265     ERROR("ethstat plugin: malloc(3) failed.");
266     return (-1);
267   }
269   strings->cmd = ETHTOOL_GSTRINGS;
270   strings->string_set = ETH_SS_STATS;
271   strings->len = n_stats;
272   req.ifr_data = (void *) strings;
273   status = ioctl (fd, SIOCETHTOOL, &req);
274   if (status < 0)
275   {
276     char errbuf[1024];
277     close (fd);
278     free (strings);
279     free (stats);
280     ERROR ("ethstat plugin: Cannot get strings from %s: %s",
281         device,
282         sstrerror (errno, errbuf, sizeof (errbuf)));
283     return (-1);
284   }
286   stats->cmd = ETHTOOL_GSTATS;
287   stats->n_stats = n_stats;
288   req.ifr_data = (void *) stats;
289   status = ioctl (fd, SIOCETHTOOL, &req);
290   if (status < 0)
291   {
292     char errbuf[1024];
293     close (fd);
294     free(strings);
295     free(stats);
296     ERROR("ethstat plugin: Reading statistics from %s failed: %s",
297         device,
298         sstrerror (errno, errbuf, sizeof (errbuf)));
299     return (-1);
300   }
302   for (i = 0; i < n_stats; i++)
303   {
304     const char *stat_name;
306     stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN],
307               DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
308                   device, stat_name,
309                   (uint64_t) stats->data[i]);
310     ethstat_submit_value (device,
311         stat_name, (derive_t) stats->data[i]);
312   }
314   close (fd);
315   sfree (strings);
316   sfree (stats);
318   return (0);
319 } /* }}} ethstat_read_interface */
321 static int ethstat_read(void)
323   size_t i;
325   for (i = 0; i < interfaces_num; i++)
326     ethstat_read_interface (interfaces[i]);
328   return 0;
331 void module_register (void)
333   plugin_register_complex_config ("ethstat", ethstat_config);
334   plugin_register_read ("ethstat", ethstat_read);
337 /* vim: set sw=2 sts=2 et fdm=marker : */