ethstat plugin: Use the system header files if available.
[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
28 #if HAVE_SYS_IOCTL_H
29 # include <sys/ioctl.h>
30 #endif
31 #if HAVE_NET_IF_H
32 # include <net/if.h>
33 #endif
34 #if HAVE_LINUX_SOCKIOS_H
35 # include <linux/sockios.h>
36 #endif
37 #if HAVE_LINUX_ETHTOOL_H
38 # include <linux/ethtool.h>
39 #endif
40
41 static const char *config_keys[] =
42 {
43         "Interface"
44 };
45 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
46
47 static char **interfaces = NULL;
48 static size_t interfaces_num = 0;
49
50 static int ethstat_config (const char *key, const char *value)
51 {
52         if (strcasecmp ("Interface", key) == 0)
53         {
54                 char **tmp;
55
56                 tmp = realloc (interfaces,
57                                 sizeof (*interfaces) * (interfaces_num + 1));
58                 if (tmp == NULL)
59                         return (-1);
60                 interfaces = tmp;
61
62                 interfaces[interfaces_num] = strdup (value);
63                 if (interfaces[interfaces_num] == NULL)
64                 {
65                         ERROR ("ethstat plugin: strdup() failed.");
66                         return (-1);
67                 }
68
69                 interfaces_num++;
70                 INFO("ethstat plugin: Registred interface %s", value);
71         }
72         return (0);
73 }
74
75 static void ethstat_submit_value (const char *device,
76                 const char *type_instance, derive_t value)
77 {
78         value_t values[1];
79         value_list_t vl = VALUE_LIST_INIT;
80
81         values[0].derive = value;
82         vl.values = values;
83         vl.values_len = 1;
84
85         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
86         sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
87         sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance));
88         sstrncpy (vl.type, "derive", sizeof (vl.type));
89         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
90
91         plugin_dispatch_values (&vl);
92 }
93
94 static int ethstat_read_interface (char *device)
95 {
96         int fd;
97         struct ifreq req;
98         struct ethtool_drvinfo drvinfo;
99         struct ethtool_gstrings *strings;
100         struct ethtool_stats *stats;
101         size_t n_stats;
102         size_t strings_size;
103         size_t stats_size;
104         size_t i;
105         int status;
106
107         memset (&req, 0, sizeof (req));
108         sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
109
110         fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
111         if (fd < 0)
112         {
113                 char errbuf[1024];
114                 ERROR("ethstat plugin: Failed to open control socket: %s",
115                                 sstrerror (errno, errbuf, sizeof (errbuf)));
116                 return 1;
117         }
118
119         memset (&drvinfo, 0, sizeof (drvinfo));
120         drvinfo.cmd = ETHTOOL_GDRVINFO;
121         req.ifr_data = (void *) &drvinfo;
122         status = ioctl (fd, SIOCETHTOOL, &req);
123         if (status < 0)
124         {
125                 char errbuf[1024];
126                 close (fd);
127                 ERROR ("ethstat plugin: Failed to get driver information "
128                                 "from %s: %s", device,
129                                 sstrerror (errno, errbuf, sizeof (errbuf)));
130                 return (-1);
131         }
132
133         n_stats = (size_t) drvinfo.n_stats;
134         if (n_stats < 1)
135         {
136                 close (fd);
137                 ERROR("ethstat plugin: No stats available for %s", device);
138                 return (-1);
139         }
140
141         strings_size = sizeof (struct ethtool_gstrings)
142                 + (n_stats * ETH_GSTRING_LEN);
143         stats_size = sizeof (struct ethtool_stats)
144                 + (n_stats * sizeof (uint64_t));
145
146         strings = malloc (strings_size);
147         stats = malloc (stats_size);
148         if ((strings == NULL) || (stats == NULL))
149         {
150                 close (fd);
151                 sfree (strings);
152                 sfree (stats);
153                 ERROR("ethstat plugin: malloc(3) failed.");
154                 return (-1);
155         }
156
157         strings->cmd = ETHTOOL_GSTRINGS;
158         strings->string_set = ETH_SS_STATS;
159         strings->len = n_stats;
160         req.ifr_data = (void *) strings;
161         status = ioctl (fd, SIOCETHTOOL, &req);
162         if (status < 0)
163         {
164                 char errbuf[1024];
165                 close (fd);
166                 free (strings);
167                 free (stats);
168                 ERROR ("ethstat plugin: Cannot get strings from %s: %s",
169                                 device,
170                                 sstrerror (errno, errbuf, sizeof (errbuf)));
171                 return (-1);
172         }
173
174         stats->cmd = ETHTOOL_GSTATS;
175         stats->n_stats = n_stats;
176         req.ifr_data = (void *) stats;
177         status = ioctl (fd, SIOCETHTOOL, &req);
178         if (status < 0)
179         {
180                 char errbuf[1024];
181                 close (fd);
182                 free(strings);
183                 free(stats);
184                 ERROR("ethstat plugin: Reading statistics from %s failed: %s",
185                                 device,
186                                 sstrerror (errno, errbuf, sizeof (errbuf)));
187                 return (-1);
188         }
189
190         for (i = 0; i < n_stats; i++)
191         {
192                 const char *stat_name;
193
194                 stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN],
195                 DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
196                                 device, stat_name,
197                                 (uint64_t) stats->data[i]);
198                 ethstat_submit_value (device,
199                                 stat_name, (derive_t) stats->data[i]);
200         }
201
202         close (fd);
203         sfree (strings);
204         sfree (stats);
205
206         return (0);
207 } /* }}} ethstat_read_interface */
208
209 static int ethstat_read(void)
210 {
211         size_t i;
212
213         for (i = 0; i < interfaces_num; i++)
214                 ethstat_read_interface (interfaces[i]);
215
216         return 0;
217 }
218
219 void module_register (void)
220 {
221         plugin_register_config ("ethstat", ethstat_config,
222                         config_keys, config_keys_num);
223         plugin_register_read ("ethstat", ethstat_read);
224 }