ethstat plugin: Fix indentation. It was a mess, sorry.
[collectd.git] / src / ethstat.c
1 /**
2  * collectd - src/ethstat.c
3  * Copyright (C) 2011       Cyril Feraudet
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Cyril Feraudet <cyril at feraudet.com>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_avltree.h"
28
29 #if HAVE_SYS_IOCTL_H
30 # include <sys/ioctl.h>
31 #endif
32 #if HAVE_NET_IF_H
33 # include <net/if.h>
34 #endif
35 #if HAVE_LINUX_SOCKIOS_H
36 # include <linux/sockios.h>
37 #endif
38 #if HAVE_LINUX_ETHTOOL_H
39 # include <linux/ethtool.h>
40 #endif
41
42 struct value_map_s
43 {
44   char type[DATA_MAX_NAME_LEN];
45   char type_instance[DATA_MAX_NAME_LEN];
46 };
47 typedef struct value_map_s value_map_t;
48
49 static char **interfaces = NULL;
50 static size_t interfaces_num = 0;
51
52 static c_avl_tree_t *value_map = NULL;
53
54 static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
55 {
56   char **tmp;
57   int status;
58
59   tmp = realloc (interfaces,
60       sizeof (*interfaces) * (interfaces_num + 1));
61   if (tmp == NULL)
62     return (-1);
63   interfaces = tmp;
64
65   status = cf_util_get_string (ci, interfaces + interfaces_num);
66   if (status != 0)
67     return (status);
68
69   interfaces_num++;
70   INFO("ethstat plugin: Registred interface %s",
71       interfaces[interfaces_num - 1]);
72
73   return (0);
74 } /* }}} int ethstat_add_interface */
75
76 static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
77 {
78   value_map_t *map;
79   int status;
80
81   if ((ci->values_num < 2)
82       || (ci->values_num > 3)
83       || (ci->values[0].type != OCONFIG_TYPE_STRING)
84       || (ci->values[1].type != OCONFIG_TYPE_STRING)
85       || ((ci->values_num == 3)
86         && (ci->values[2].type != OCONFIG_TYPE_STRING)))
87   {
88     ERROR ("ethstat plugin: The %s option requires "
89         "two or three string arguments.", ci->key);
90     return (-1);
91   }
92
93   map = malloc (sizeof (*map));
94   if (map == NULL)
95   {
96     ERROR ("ethstat plugin: malloc(3) failed.");
97     return (ENOMEM);
98   }
99   memset (map, 0, sizeof (*map));
100
101   sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type));
102   if (ci->values_num == 2)
103     sstrncpy (map->type_instance, ci->values[2].value.string,
104         sizeof (map->type_instance));
105
106   if (value_map == NULL)
107   {
108     value_map = c_avl_create ((void *) strcmp);
109     if (value_map == NULL)
110     {
111       sfree (map);
112       ERROR ("ethstat plugin: c_avl_create() failed.");
113       return (-1);
114     }
115   }
116
117   status = c_avl_insert (value_map,
118       /* key = */ ci->values[0].value.string,
119       /* value = */ map);
120   if (status != 0)
121   {
122     sfree (map);
123     if (status > 0)
124       ERROR ("ethstat plugin: Multiple mappings for \"%s\".",
125           ci->values[0].value.string);
126     else
127       ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.",
128           ci->values[0].value.string);
129     return (-1);
130   }
131
132   return (0);
133 } /* }}} int ethstat_add_map */
134
135 static int ethstat_config (oconfig_item_t *ci) /* {{{ */
136 {
137   int i;
138
139   for (i = 0; i < ci->children_num; i++)
140   {
141     oconfig_item_t *child = ci->children + i;
142
143     if (strcasecmp ("Interface", child->key) == 0)
144       ethstat_add_interface (child);
145     else if (strcasecmp ("Map", child->key) == 0)
146       ethstat_add_map (child);
147     else
148       WARNING ("ethstat plugin: The config option \"%s\" is unknown.",
149           child->key);
150   }
151
152   return (0);
153 } /* }}} */
154
155 static void ethstat_submit_value (const char *device,
156     const char *type_instance, derive_t value)
157 {
158   value_t values[1];
159   value_list_t vl = VALUE_LIST_INIT;
160   value_map_t *map = NULL;
161
162   if (value_map != NULL)
163     c_avl_get (value_map, type_instance, (void *) &map);
164
165   values[0].derive = value;
166   vl.values = values;
167   vl.values_len = 1;
168
169   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
170   sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
171   sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance));
172   if (map != NULL)
173   {
174     sstrncpy (vl.type, map->type, sizeof (vl.type));
175     sstrncpy (vl.type_instance, map->type_instance,
176         sizeof (vl.type_instance));
177   }
178   else
179   {
180     sstrncpy (vl.type, "derive", sizeof (vl.type));
181     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
182   }
183
184   plugin_dispatch_values (&vl);
185 }
186
187 static int ethstat_read_interface (char *device)
188 {
189   int fd;
190   struct ifreq req;
191   struct ethtool_drvinfo drvinfo;
192   struct ethtool_gstrings *strings;
193   struct ethtool_stats *stats;
194   size_t n_stats;
195   size_t strings_size;
196   size_t stats_size;
197   size_t i;
198   int status;
199
200   memset (&req, 0, sizeof (req));
201   sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
202
203   fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
204   if (fd < 0)
205   {
206     char errbuf[1024];
207     ERROR("ethstat plugin: Failed to open control socket: %s",
208         sstrerror (errno, errbuf, sizeof (errbuf)));
209     return 1;
210   }
211
212   memset (&drvinfo, 0, sizeof (drvinfo));
213   drvinfo.cmd = ETHTOOL_GDRVINFO;
214   req.ifr_data = (void *) &drvinfo;
215   status = ioctl (fd, SIOCETHTOOL, &req);
216   if (status < 0)
217   {
218     char errbuf[1024];
219     close (fd);
220     ERROR ("ethstat plugin: Failed to get driver information "
221         "from %s: %s", device,
222         sstrerror (errno, errbuf, sizeof (errbuf)));
223     return (-1);
224   }
225
226   n_stats = (size_t) drvinfo.n_stats;
227   if (n_stats < 1)
228   {
229     close (fd);
230     ERROR("ethstat plugin: No stats available for %s", device);
231     return (-1);
232   }
233
234   strings_size = sizeof (struct ethtool_gstrings)
235     + (n_stats * ETH_GSTRING_LEN);
236   stats_size = sizeof (struct ethtool_stats)
237     + (n_stats * sizeof (uint64_t));
238
239   strings = malloc (strings_size);
240   stats = malloc (stats_size);
241   if ((strings == NULL) || (stats == NULL))
242   {
243     close (fd);
244     sfree (strings);
245     sfree (stats);
246     ERROR("ethstat plugin: malloc(3) failed.");
247     return (-1);
248   }
249
250   strings->cmd = ETHTOOL_GSTRINGS;
251   strings->string_set = ETH_SS_STATS;
252   strings->len = n_stats;
253   req.ifr_data = (void *) strings;
254   status = ioctl (fd, SIOCETHTOOL, &req);
255   if (status < 0)
256   {
257     char errbuf[1024];
258     close (fd);
259     free (strings);
260     free (stats);
261     ERROR ("ethstat plugin: Cannot get strings from %s: %s",
262         device,
263         sstrerror (errno, errbuf, sizeof (errbuf)));
264     return (-1);
265   }
266
267   stats->cmd = ETHTOOL_GSTATS;
268   stats->n_stats = n_stats;
269   req.ifr_data = (void *) stats;
270   status = ioctl (fd, SIOCETHTOOL, &req);
271   if (status < 0)
272   {
273     char errbuf[1024];
274     close (fd);
275     free(strings);
276     free(stats);
277     ERROR("ethstat plugin: Reading statistics from %s failed: %s",
278         device,
279         sstrerror (errno, errbuf, sizeof (errbuf)));
280     return (-1);
281   }
282
283   for (i = 0; i < n_stats; i++)
284   {
285     const char *stat_name;
286
287     stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN],
288               DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
289                   device, stat_name,
290                   (uint64_t) stats->data[i]);
291     ethstat_submit_value (device,
292         stat_name, (derive_t) stats->data[i]);
293   }
294
295   close (fd);
296   sfree (strings);
297   sfree (stats);
298
299   return (0);
300 } /* }}} ethstat_read_interface */
301
302 static int ethstat_read(void)
303 {
304   size_t i;
305
306   for (i = 0; i < interfaces_num; i++)
307     ethstat_read_interface (interfaces[i]);
308
309   return 0;
310 }
311
312 void module_register (void)
313 {
314   plugin_register_complex_config ("ethstat", ethstat_config);
315   plugin_register_read ("ethstat", ethstat_read);
316 }
317
318 /* vim: set sw=2 sts=2 et fdm=marker : */