Merged stuff from branches/collectd-3.9: After release 3.9.2 the ChangeLog additions...
[collectd.git] / src / traffic.c
1 /**
2  * collectd - src/traffic.c
3  * Copyright (C) 2005,2006  Florian octo Forster
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  *   Florian octo Forster <octo at verplant.org>
21  *   Sune Marcher <sm at flork.dk>
22  **/
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "configfile.h"
28
29 #if HAVE_SYS_TYPES_H
30 #  include <sys/types.h>
31 #endif
32 #if HAVE_SYS_SOCKET_H
33 #  include <sys/socket.h>
34 #endif
35
36 /* One cannot include both. This sucks. */
37 #if HAVE_LINUX_IF_H
38 #  include <linux/if.h>
39 #elif HAVE_NET_IF_H
40 #  include <net/if.h>
41 #endif
42
43 #if HAVE_LINUX_NETDEVICE_H
44 #  include <linux/netdevice.h>
45 #endif
46 #if HAVE_IFADDRS_H
47 #  include <ifaddrs.h>
48 #endif
49
50 #define MODULE_NAME "traffic"
51
52 #if HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT || HAVE_LIBSTATGRAB
53 # define TRAFFIC_HAVE_READ 1
54 #else
55 # define TRAFFIC_HAVE_READ 0
56 #endif
57
58 #define BUFSIZE 512
59
60 /*
61  * (Module-)Global variables
62  */
63 /* TODO: Move this to `interface-%s/<blah>.rrd' in version 4. */
64 static char *bytes_file   = "traffic-%s.rrd";
65 static char *packets_file = "if_packets-%s.rrd";
66 static char *errors_file  = "if_errors-%s.rrd";
67 /* TODO: Maybe implement multicast and broadcast counters */
68
69 static char *config_keys[] =
70 {
71         "Ignore",
72         NULL
73 };
74 static int config_keys_num = 1;
75
76 static char *bytes_ds_def[] =
77 {
78         "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
79         "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
80         NULL
81 };
82 static int bytes_ds_num = 2;
83
84 static char *packets_ds_def[] =
85 {
86         "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
87         "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
88         NULL
89 };
90 static int packets_ds_num = 2;
91
92 static char *errors_ds_def[] =
93 {
94         "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
95         "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
96         NULL
97 };
98 static int errors_ds_num = 2;
99
100 static char **if_ignore_list = NULL;
101 static int    if_ignore_list_num = 0;
102
103 #ifdef HAVE_LIBKSTAT
104 #define MAX_NUMIF 256
105 extern kstat_ctl_t *kc;
106 static kstat_t *ksp[MAX_NUMIF];
107 static int numif = 0;
108 #endif /* HAVE_LIBKSTAT */
109
110 static int traffic_config (char *key, char *value)
111 {
112         char **temp;
113
114         if (strcasecmp (key, "Ignore") != 0)
115                 return (-1);
116
117         temp = (char **) realloc (if_ignore_list, (if_ignore_list_num + 1) * sizeof (char *));
118         if (temp == NULL)
119         {
120                 syslog (LOG_EMERG, "Cannot allocate more memory.");
121                 return (1);
122         }
123         if_ignore_list = temp;
124
125         if ((if_ignore_list[if_ignore_list_num] = strdup (value)) == NULL)
126         {
127                 syslog (LOG_EMERG, "Cannot allocate memory.");
128                 return (1);
129         }
130         if_ignore_list_num++;
131
132         syslog (LOG_NOTICE, "traffic: Ignoring interface `%s'", value);
133
134         return (0);
135 }
136
137 static void traffic_init (void)
138 {
139 #if HAVE_GETIFADDRS
140         /* nothing */
141 /* #endif HAVE_GETIFADDRS */
142
143 #elif KERNEL_LINUX
144         /* nothing */
145 /* #endif KERNEL_LINUX */
146
147 #elif HAVE_LIBKSTAT
148         kstat_t *ksp_chain;
149         unsigned long long val;
150
151         numif = 0;
152
153         if (kc == NULL)
154                 return;
155
156         for (numif = 0, ksp_chain = kc->kc_chain;
157                         (numif < MAX_NUMIF) && (ksp_chain != NULL);
158                         ksp_chain = ksp_chain->ks_next)
159         {
160                 if (strncmp (ksp_chain->ks_class, "net", 3))
161                         continue;
162                 if (ksp_chain->ks_type != KSTAT_TYPE_NAMED)
163                         continue;
164                 if (kstat_read (kc, ksp_chain, NULL) == -1)
165                         continue;
166                 if ((val = get_kstat_value (ksp_chain, "obytes")) == -1LL)
167                         continue;
168                 ksp[numif++] = ksp_chain;
169         }
170 /* #endif HAVE_LIBKSTAT */
171
172 #elif HAVE_LIBSTATG
173         /* nothing */
174 #endif /* HAVE_LIBSTATG */
175
176         return;
177 }
178
179 /*
180  * Check if this interface/instance should be ignored. This is called from
181  * both, `submit' and `write' to give client and server the ability to ignore
182  * certain stuff..
183  */
184 static int check_ignore_if (const char *interface)
185 {
186         int i;
187
188         for (i = 0; i < if_ignore_list_num; i++)
189                 if (strcasecmp (interface, if_ignore_list[i]) == 0)
190                         return (1);
191         return (0);
192 }
193
194 static void generic_write (char *host, char *inst, char *val,
195                 char *file_template,
196                 char **ds_def, int ds_num)
197 {
198         char file[512];
199         int status;
200
201         if (check_ignore_if (inst))
202                 return;
203
204         status = snprintf (file, BUFSIZE, file_template, inst);
205         if (status < 1)
206                 return;
207         else if (status >= 512)
208                 return;
209
210         rrd_update_file (host, file, val, ds_def, ds_num);
211 }
212
213 static void bytes_write (char *host, char *inst, char *val)
214 {
215         generic_write (host, inst, val, bytes_file, bytes_ds_def, bytes_ds_num);
216 }
217
218 static void packets_write (char *host, char *inst, char *val)
219 {
220         generic_write (host, inst, val, packets_file, packets_ds_def, packets_ds_num);
221 }
222
223 static void errors_write (char *host, char *inst, char *val)
224 {
225         generic_write (host, inst, val, errors_file, errors_ds_def, errors_ds_num);
226 }
227
228 #if TRAFFIC_HAVE_READ
229 static void bytes_submit (char *dev,
230                 unsigned long long rx,
231                 unsigned long long tx)
232 {
233         char buf[512];
234         int  status;
235
236         if (check_ignore_if (dev))
237                 return;
238
239         status = snprintf (buf, 512, "%u:%lld:%lld",
240                                 (unsigned int) curtime,
241                                 rx, tx);
242         if ((status >= 512) || (status < 1))
243                 return;
244
245         plugin_submit (MODULE_NAME, dev, buf);
246 }
247
248 #if HAVE_GETIFADDRS || HAVE_LIBKSTAT
249 static void packets_submit (char *dev,
250                 unsigned long long rx,
251                 unsigned long long tx)
252 {
253         char buf[512];
254         int  status;
255
256         if (check_ignore_if (dev))
257                 return;
258
259         status = snprintf (buf, 512, "%u:%lld:%lld",
260                         (unsigned int) curtime,
261                         rx, tx);
262         if ((status >= 512) || (status < 1))
263                 return;
264         plugin_submit ("if_packets", dev, buf);
265 }
266
267 static void errors_submit (char *dev,
268                 unsigned long long rx,
269                 unsigned long long tx)
270 {
271         char buf[512];
272         int  status;
273
274         if (check_ignore_if (dev))
275                 return;
276
277         status = snprintf (buf, 512, "%u:%lld:%lld",
278                         (unsigned int) curtime,
279                         rx, tx);
280         if ((status >= 512) || (status < 1))
281                 return;
282         plugin_submit ("if_errors", dev, buf);
283 }
284 #endif /* HAVE_GETIFADDRS || HAVE_LIBKSTAT */
285
286 static void traffic_read (void)
287 {
288 #if HAVE_GETIFADDRS
289         struct ifaddrs *if_list;
290         struct ifaddrs *if_ptr;
291
292 /* Darin/Mac OS X and possible other *BSDs */
293 #if HAVE_STRUCT_IF_DATA
294 #  define IFA_DATA if_data
295 #  define IFA_RX_BYTES ifi_ibytes
296 #  define IFA_TX_BYTES ifi_obytes
297 #  define IFA_RX_PACKT ifi_ipackets
298 #  define IFA_TX_PACKT ifi_opackets
299 #  define IFA_RX_ERROR ifi_ierrors
300 #  define IFA_TX_ERROR ifi_oerrors
301 /* #endif HAVE_STRUCT_IF_DATA */
302
303 #elif HAVE_STRUCT_NET_DEVICE_STATS
304 #  define IFA_DATA net_device_stats
305 #  define IFA_RX_BYTES rx_bytes
306 #  define IFA_TX_BYTES tx_bytes
307 #  define IFA_RX_PACKT rx_packets
308 #  define IFA_TX_PACKT tx_packets
309 #  define IFA_RX_ERROR rx_errors
310 #  define IFA_TX_ERROR tx_errors
311 #else
312 #  error "No suitable type for `struct ifaddrs->ifa_data' found."
313 #endif
314
315         struct IFA_DATA *if_data;
316
317         if (getifaddrs (&if_list) != 0)
318                 return;
319
320         for (if_ptr = if_list; if_ptr != NULL; if_ptr = if_ptr->ifa_next)
321         {
322                 if ((if_data = (struct IFA_DATA *) if_ptr->ifa_data) == NULL)
323                         continue;
324
325                 bytes_submit (if_ptr->ifa_name,
326                                 if_data->IFA_RX_BYTES,
327                                 if_data->IFA_TX_BYTES);
328                 packets_submit (if_ptr->ifa_name,
329                                 if_data->IFA_RX_PACKT,
330                                 if_data->IFA_TX_PACKT);
331                 errors_submit (if_ptr->ifa_name,
332                                 if_data->IFA_RX_ERROR,
333                                 if_data->IFA_TX_ERROR);
334         }
335
336         freeifaddrs (if_list);
337 /* #endif HAVE_GETIFADDRS */
338
339 #elif KERNEL_LINUX
340         FILE *fh;
341         char buffer[1024];
342         unsigned long long incoming, outgoing;
343         char *device;
344         
345         char *dummy;
346         char *fields[16];
347         int numfields;
348
349         if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
350         {
351                 syslog (LOG_WARNING, "traffic: fopen: %s", strerror (errno));
352                 return;
353         }
354
355         while (fgets (buffer, 1024, fh) != NULL)
356         {
357                 if (!(dummy = strchr(buffer, ':')))
358                         continue;
359                 dummy[0] = '\0';
360                 dummy++;
361
362                 device = buffer;
363                 while (device[0] == ' ')
364                         device++;
365
366                 if (device[0] == '\0')
367                         continue;
368                 
369                 numfields = strsplit (dummy, fields, 16);
370
371                 if (numfields < 9)
372                         continue;
373
374                 incoming = atoll (fields[0]);
375                 outgoing = atoll (fields[8]);
376
377                 bytes_submit (device, incoming, outgoing);
378         }
379
380         fclose (fh);
381 /* #endif KERNEL_LINUX */
382
383 #elif HAVE_LIBKSTAT
384         int i;
385         unsigned long long rx;
386         unsigned long long tx;
387
388         if (kc == NULL)
389                 return;
390
391         for (i = 0; i < numif; i++)
392         {
393                 if (kstat_read (kc, ksp[i], NULL) == -1)
394                         continue;
395
396                 rx = get_kstat_value (ksp[i], "rbytes");
397                 tx = get_kstat_value (ksp[i], "obytes");
398                 if ((rx != -1LL) || (tx != -1LL))
399                         bytes_submit (ksp[i]->ks_name, rx, tx);
400
401                 rx = get_kstat_value (ksp[i], "ipackets");
402                 tx = get_kstat_value (ksp[i], "opackets");
403                 if ((rx != -1LL) || (tx != -1LL))
404                         packets_submit (ksp[i]->ks_name, rx, tx);
405
406                 rx = get_kstat_value (ksp[i], "ierrors");
407                 tx = get_kstat_value (ksp[i], "oerrors");
408                 if ((rx != -1LL) || (tx != -1LL))
409                         errors_submit (ksp[i]->ks_name, rx, tx);
410         }
411 /* #endif HAVE_LIBKSTAT */
412
413 #elif defined(HAVE_LIBSTATGRAB)
414         sg_network_io_stats *ios;
415         int i, num;
416
417         ios = sg_get_network_io_stats (&num);
418
419         for (i = 0; i < num; i++)
420                 bytes_submit (ios[i].interface_name, ios[i].rx, ios[i].tx);
421 #endif /* HAVE_LIBSTATGRAB */
422 }
423 #else
424 #define traffic_read NULL
425 #endif /* TRAFFIC_HAVE_READ */
426
427 void module_register (void)
428 {
429         plugin_register (MODULE_NAME, traffic_init, traffic_read, bytes_write);
430         plugin_register ("if_packets", NULL, NULL, packets_write);
431         plugin_register ("if_errors",  NULL, NULL, errors_write);
432         cf_register (MODULE_NAME, traffic_config, config_keys, config_keys_num);
433 }
434
435 #undef BUFSIZE
436 #undef MODULE_NAME