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 **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
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
41 static const char *config_keys[] =
42 {
43 "Interface"
44 };
45 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
47 static char **interfaces = NULL;
48 static size_t interfaces_num = 0;
50 static int ethstat_config (const char *key, const char *value)
51 {
52 if (strcasecmp ("Interface", key) == 0)
53 {
54 char **tmp;
56 tmp = realloc (interfaces,
57 sizeof (*interfaces) * (interfaces_num + 1));
58 if (tmp == NULL)
59 return (-1);
60 interfaces = tmp;
62 interfaces[interfaces_num] = strdup (value);
63 if (interfaces[interfaces_num] == NULL)
64 {
65 ERROR ("ethstat plugin: strdup() failed.");
66 return (-1);
67 }
69 interfaces_num++;
70 INFO("ethstat plugin: Registred interface %s", value);
71 }
72 return (0);
73 }
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;
81 values[0].derive = value;
82 vl.values = values;
83 vl.values_len = 1;
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));
91 plugin_dispatch_values (&vl);
92 }
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;
107 memset (&req, 0, sizeof (req));
108 sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
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 }
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 }
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 }
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));
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 }
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 }
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 }
190 for (i = 0; i < n_stats; i++)
191 {
192 const char *stat_name;
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 }
202 close (fd);
203 sfree (strings);
204 sfree (stats);
206 return (0);
207 } /* }}} ethstat_read_interface */
209 static int ethstat_read(void)
210 {
211 size_t i;
213 for (i = 0; i < interfaces_num; i++)
214 ethstat_read_interface (interfaces[i]);
216 return 0;
217 }
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 }