]> arthur.barton.de Git - netdata.git/blob - src/proc_net_dev.c
self-cleaning obsolete cgroups and network interfaces from memory; fixes #1163; fixes...
[netdata.git] / src / proc_net_dev.c
1 #include "common.h"
2
3 struct netdev {
4     char *name;
5     uint32_t hash;
6     size_t len;
7
8     // flags
9     int configured;
10     int enabled;
11     int updated;
12
13     int do_bandwidth;
14     int do_packets;
15     int do_errors;
16     int do_drops;
17     int do_fifo;
18     int do_compressed;
19     int do_events;
20
21     // data collected
22     kernel_uint_t rbytes;
23     kernel_uint_t rpackets;
24     kernel_uint_t rerrors;
25     kernel_uint_t rdrops;
26     kernel_uint_t rfifo;
27     kernel_uint_t rframe;
28     kernel_uint_t rcompressed;
29     kernel_uint_t rmulticast;
30
31     kernel_uint_t tbytes;
32     kernel_uint_t tpackets;
33     kernel_uint_t terrors;
34     kernel_uint_t tdrops;
35     kernel_uint_t tfifo;
36     kernel_uint_t tcollisions;
37     kernel_uint_t tcarrier;
38     kernel_uint_t tcompressed;
39
40     // charts
41     RRDSET *st_bandwidth;
42     RRDSET *st_packets;
43     RRDSET *st_errors;
44     RRDSET *st_drops;
45     RRDSET *st_fifo;
46     RRDSET *st_compressed;
47     RRDSET *st_events;
48
49     // dimensions
50     RRDDIM *rd_rbytes;
51     RRDDIM *rd_rpackets;
52     RRDDIM *rd_rerrors;
53     RRDDIM *rd_rdrops;
54     RRDDIM *rd_rfifo;
55     RRDDIM *rd_rframe;
56     RRDDIM *rd_rcompressed;
57     RRDDIM *rd_rmulticast;
58
59     RRDDIM *rd_tbytes;
60     RRDDIM *rd_tpackets;
61     RRDDIM *rd_terrors;
62     RRDDIM *rd_tdrops;
63     RRDDIM *rd_tfifo;
64     RRDDIM *rd_tcollisions;
65     RRDDIM *rd_tcarrier;
66     RRDDIM *rd_tcompressed;
67
68     struct netdev *next;
69 };
70
71 static struct netdev *netdev_root = NULL, *netdev_last_used = NULL;
72
73 static size_t netdev_added = 0, netdev_found = 0;
74
75 static void netdev_free(struct netdev *d) {
76     if(d->st_bandwidth)  rrdset_flag_set(d->st_bandwidth,  RRDSET_FLAG_OBSOLETE);
77     if(d->st_packets)    rrdset_flag_set(d->st_packets,    RRDSET_FLAG_OBSOLETE);
78     if(d->st_errors)     rrdset_flag_set(d->st_errors,     RRDSET_FLAG_OBSOLETE);
79     if(d->st_drops)      rrdset_flag_set(d->st_drops,      RRDSET_FLAG_OBSOLETE);
80     if(d->st_fifo)       rrdset_flag_set(d->st_fifo,       RRDSET_FLAG_OBSOLETE);
81     if(d->st_compressed) rrdset_flag_set(d->st_compressed, RRDSET_FLAG_OBSOLETE);
82     if(d->st_events)     rrdset_flag_set(d->st_events,     RRDSET_FLAG_OBSOLETE);
83
84     freez(d->name);
85     freez(d);
86 }
87
88 static void netdev_cleanup() {
89     if(likely(netdev_found == netdev_added)) return;
90
91     struct netdev *d = netdev_root, *last = NULL;
92     while(d) {
93         if(unlikely(!d->updated)) {
94             // info("Removing network device '%s', linked after '%s'", d->name, last?last->name:"ROOT");
95
96             if(netdev_last_used == d)
97                 netdev_last_used = last;
98
99             struct netdev *t = d;
100
101             if(d == netdev_root || !last)
102                 netdev_root = d = d->next;
103
104             else
105                 last->next = d = d->next;
106
107             t->next = NULL;
108             netdev_free(t);
109         }
110         else {
111             last = d;
112             d->updated = 0;
113             d = d->next;
114         }
115     }
116 }
117
118 static struct netdev *get_netdev(const char *name) {
119     struct netdev *d;
120
121     uint32_t hash = simple_hash(name);
122
123     // search it, from the last position to the end
124     for(d = netdev_last_used ; d ; d = d->next) {
125         if(unlikely(hash == d->hash && !strcmp(name, d->name))) {
126             netdev_last_used = d->next;
127             return d;
128         }
129     }
130
131     // search it from the beginning to the last position we used
132     for(d = netdev_root ; d != netdev_last_used ; d = d->next) {
133         if(unlikely(hash == d->hash && !strcmp(name, d->name))) {
134             netdev_last_used = d->next;
135             return d;
136         }
137     }
138
139     // create a new one
140     d = callocz(1, sizeof(struct netdev));
141     d->name = strdupz(name);
142     d->hash = simple_hash(d->name);
143     d->len = strlen(d->name);
144     netdev_added++;
145
146     // link it to the end
147     if(netdev_root) {
148         struct netdev *e;
149         for(e = netdev_root; e->next ; e = e->next) ;
150         e->next = d;
151     }
152     else
153         netdev_root = d;
154
155     return d;
156 }
157
158 int do_proc_net_dev(int update_every, usec_t dt) {
159     (void)dt;
160     static SIMPLE_PATTERN *disabled_list = NULL;
161     static procfile *ff = NULL;
162     static int enable_new_interfaces = -1;
163     static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
164
165     if(unlikely(enable_new_interfaces == -1)) {
166         enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_AUTO);
167
168         do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_BOOLEAN_AUTO);
169         do_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_BOOLEAN_AUTO);
170         do_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_BOOLEAN_AUTO);
171         do_drops        = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_BOOLEAN_AUTO);
172         do_fifo         = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_BOOLEAN_AUTO);
173         do_compressed   = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_BOOLEAN_AUTO);
174         do_events       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_BOOLEAN_AUTO);
175
176         disabled_list = simple_pattern_create(
177                 config_get("plugin:proc:/proc/net/dev", "disable by default interfaces matching", "lo fireqos* *-ifb")
178                 , SIMPLE_PATTERN_EXACT);
179     }
180
181     if(unlikely(!ff)) {
182         char filename[FILENAME_MAX + 1];
183         snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/dev");
184         ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
185         if(unlikely(!ff)) return 1;
186     }
187
188     ff = procfile_readall(ff);
189     if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
190
191     netdev_found = 0;
192
193     size_t lines = procfile_lines(ff), l;
194     for(l = 2; l < lines ;l++) {
195         // require 17 words on each line
196         if(unlikely(procfile_linewords(ff, l) < 17)) continue;
197
198         struct netdev *d = get_netdev(procfile_lineword(ff, l, 0));
199         d->updated = 1;
200         netdev_found++;
201
202         if(unlikely(!d->configured)) {
203             // this is the first time we see this interface
204
205             // remember we configured it
206             d->configured = 1;
207
208             d->enabled = enable_new_interfaces;
209
210             if(d->enabled)
211                 d->enabled = !simple_pattern_matches(disabled_list, d->name);
212
213             char var_name[512 + 1];
214             snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", d->name);
215             d->enabled = config_get_boolean_ondemand(var_name, "enabled", d->enabled);
216
217             if(d->enabled == CONFIG_BOOLEAN_NO)
218                 continue;
219
220             d->do_bandwidth  = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth);
221             d->do_packets    = config_get_boolean_ondemand(var_name, "packets", do_packets);
222             d->do_errors     = config_get_boolean_ondemand(var_name, "errors", do_errors);
223             d->do_drops      = config_get_boolean_ondemand(var_name, "drops", do_drops);
224             d->do_fifo       = config_get_boolean_ondemand(var_name, "fifo", do_fifo);
225             d->do_compressed = config_get_boolean_ondemand(var_name, "compressed", do_compressed);
226             d->do_events     = config_get_boolean_ondemand(var_name, "events", do_events);
227         }
228
229         if(unlikely(!d->enabled))
230             continue;
231
232         if(likely(d->do_bandwidth != CONFIG_BOOLEAN_NO)) {
233             d->rbytes      = str2kernel_uint_t(procfile_lineword(ff, l, 1));
234             d->tbytes      = str2kernel_uint_t(procfile_lineword(ff, l, 9));
235         }
236
237         if(likely(d->do_packets != CONFIG_BOOLEAN_NO)) {
238             d->rpackets    = str2kernel_uint_t(procfile_lineword(ff, l, 2));
239             d->rmulticast  = str2kernel_uint_t(procfile_lineword(ff, l, 8));
240             d->tpackets    = str2kernel_uint_t(procfile_lineword(ff, l, 10));
241         }
242
243         if(likely(d->do_errors != CONFIG_BOOLEAN_NO)) {
244             d->rerrors     = str2kernel_uint_t(procfile_lineword(ff, l, 3));
245             d->terrors     = str2kernel_uint_t(procfile_lineword(ff, l, 11));
246         }
247
248         if(likely(d->do_drops != CONFIG_BOOLEAN_NO)) {
249             d->rdrops      = str2kernel_uint_t(procfile_lineword(ff, l, 4));
250             d->tdrops      = str2kernel_uint_t(procfile_lineword(ff, l, 12));
251         }
252
253         if(likely(d->do_fifo != CONFIG_BOOLEAN_NO)) {
254             d->rfifo       = str2kernel_uint_t(procfile_lineword(ff, l, 5));
255             d->tfifo       = str2kernel_uint_t(procfile_lineword(ff, l, 13));
256         }
257
258         if(likely(d->do_compressed != CONFIG_BOOLEAN_NO)) {
259             d->rcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 7));
260             d->tcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 16));
261         }
262
263         if(likely(d->do_events != CONFIG_BOOLEAN_NO)) {
264             d->rframe      = str2kernel_uint_t(procfile_lineword(ff, l, 6));
265             d->tcollisions = str2kernel_uint_t(procfile_lineword(ff, l, 14));
266             d->tcarrier    = str2kernel_uint_t(procfile_lineword(ff, l, 15));
267         }
268
269         // --------------------------------------------------------------------
270
271         if(unlikely((d->do_bandwidth == CONFIG_BOOLEAN_AUTO && (d->rbytes || d->tbytes))))
272             d->do_bandwidth = CONFIG_BOOLEAN_YES;
273
274         if(d->do_bandwidth == CONFIG_BOOLEAN_YES) {
275             if(unlikely(!d->st_bandwidth)) {
276
277                 d->st_bandwidth = rrdset_create_localhost(
278                         "net"
279                         , d->name
280                         , NULL
281                         , d->name
282                         , "net.net"
283                         , "Bandwidth"
284                         , "kilobits/s"
285                         , 7000
286                         , update_every
287                         , RRDSET_TYPE_AREA
288                 );
289
290                 d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
291                 d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
292             }
293             else rrdset_next(d->st_bandwidth);
294
295             rrddim_set_by_pointer(d->st_bandwidth, d->rd_rbytes, (collected_number)d->rbytes);
296             rrddim_set_by_pointer(d->st_bandwidth, d->rd_tbytes, (collected_number)d->tbytes);
297             rrdset_done(d->st_bandwidth);
298         }
299
300         // --------------------------------------------------------------------
301
302         if(unlikely((d->do_packets == CONFIG_BOOLEAN_AUTO && (d->rpackets || d->tpackets || d->rmulticast))))
303             d->do_packets = CONFIG_BOOLEAN_YES;
304
305         if(d->do_packets == CONFIG_BOOLEAN_YES) {
306             if(unlikely(!d->st_packets)) {
307
308                 d->st_packets = rrdset_create_localhost(
309                         "net_packets"
310                         , d->name
311                         , NULL
312                         , d->name
313                         , "net.packets"
314                         , "Packets"
315                         , "packets/s"
316                         , 7001
317                         , update_every
318                         , RRDSET_TYPE_LINE
319                 );
320
321                 rrdset_flag_set(d->st_packets, RRDSET_FLAG_DETAIL);
322
323                 d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
324                 d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
325                 d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
326             }
327             else rrdset_next(d->st_packets);
328
329             rrddim_set_by_pointer(d->st_packets, d->rd_rpackets, (collected_number)d->rpackets);
330             rrddim_set_by_pointer(d->st_packets, d->rd_tpackets, (collected_number)d->tpackets);
331             rrddim_set_by_pointer(d->st_packets, d->rd_rmulticast, (collected_number)d->rmulticast);
332             rrdset_done(d->st_packets);
333         }
334
335         // --------------------------------------------------------------------
336
337         if(unlikely((d->do_errors == CONFIG_BOOLEAN_AUTO && (d->rerrors || d->terrors))))
338             d->do_errors = CONFIG_BOOLEAN_YES;
339
340         if(d->do_errors == CONFIG_BOOLEAN_YES) {
341             if(unlikely(!d->st_errors)) {
342
343                 d->st_errors = rrdset_create_localhost(
344                         "net_errors"
345                         , d->name
346                         , NULL
347                         , d->name
348                         , "net.errors"
349                         , "Interface Errors"
350                         , "errors/s"
351                         , 7002
352                         , update_every
353                         , RRDSET_TYPE_LINE
354                 );
355
356                 rrdset_flag_set(d->st_errors, RRDSET_FLAG_DETAIL);
357
358                 d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
359                 d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
360             }
361             else rrdset_next(d->st_errors);
362
363             rrddim_set_by_pointer(d->st_errors, d->rd_rerrors, (collected_number)d->rerrors);
364             rrddim_set_by_pointer(d->st_errors, d->rd_terrors, (collected_number)d->terrors);
365             rrdset_done(d->st_errors);
366         }
367
368         // --------------------------------------------------------------------
369
370         if(unlikely((d->do_drops == CONFIG_BOOLEAN_AUTO && (d->rdrops || d->tdrops))))
371             d->do_drops = CONFIG_BOOLEAN_YES;
372
373         if(d->do_drops == CONFIG_BOOLEAN_YES) {
374             if(unlikely(!d->st_drops)) {
375
376                 d->st_drops = rrdset_create_localhost(
377                         "net_drops"
378                         , d->name
379                         , NULL
380                         , d->name
381                         , "net.drops"
382                         , "Interface Drops"
383                         , "drops/s"
384                         , 7003
385                         , update_every
386                         , RRDSET_TYPE_LINE
387                 );
388
389                 rrdset_flag_set(d->st_drops, RRDSET_FLAG_DETAIL);
390
391                 d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
392                 d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
393             }
394             else rrdset_next(d->st_drops);
395
396             rrddim_set_by_pointer(d->st_drops, d->rd_rdrops, (collected_number)d->rdrops);
397             rrddim_set_by_pointer(d->st_drops, d->rd_tdrops, (collected_number)d->tdrops);
398             rrdset_done(d->st_drops);
399         }
400
401         // --------------------------------------------------------------------
402
403         if(unlikely((d->do_fifo == CONFIG_BOOLEAN_AUTO && (d->rfifo || d->tfifo))))
404             d->do_fifo = CONFIG_BOOLEAN_YES;
405
406         if(d->do_fifo == CONFIG_BOOLEAN_YES) {
407             if(unlikely(!d->st_fifo)) {
408
409                 d->st_fifo = rrdset_create_localhost(
410                         "net_fifo"
411                         , d->name
412                         , NULL
413                         , d->name
414                         , "net.fifo"
415                         , "Interface FIFO Buffer Errors"
416                         , "errors"
417                         , 7004
418                         , update_every
419                         , RRDSET_TYPE_LINE
420                 );
421
422                 rrdset_flag_set(d->st_fifo, RRDSET_FLAG_DETAIL);
423
424                 d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
425                 d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
426             }
427             else rrdset_next(d->st_fifo);
428
429             rrddim_set_by_pointer(d->st_fifo, d->rd_rfifo, (collected_number)d->rfifo);
430             rrddim_set_by_pointer(d->st_fifo, d->rd_tfifo, (collected_number)d->tfifo);
431             rrdset_done(d->st_fifo);
432         }
433
434         // --------------------------------------------------------------------
435
436         if(unlikely((d->do_compressed == CONFIG_BOOLEAN_AUTO && (d->rcompressed || d->tcompressed))))
437             d->do_compressed = CONFIG_BOOLEAN_YES;
438
439         if(d->do_compressed == CONFIG_BOOLEAN_YES) {
440             if(unlikely(!d->st_compressed)) {
441
442                 d->st_compressed = rrdset_create_localhost(
443                         "net_compressed"
444                         , d->name
445                         , NULL
446                         , d->name
447                         , "net.compressed"
448                         , "Compressed Packets"
449                         , "packets/s"
450                         , 7005
451                         , update_every
452                         , RRDSET_TYPE_LINE
453                 );
454
455                 rrdset_flag_set(d->st_compressed, RRDSET_FLAG_DETAIL);
456
457                 d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
458                 d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
459             }
460             else rrdset_next(d->st_compressed);
461
462             rrddim_set_by_pointer(d->st_compressed, d->rd_rcompressed, (collected_number)d->rcompressed);
463             rrddim_set_by_pointer(d->st_compressed, d->rd_tcompressed, (collected_number)d->tcompressed);
464             rrdset_done(d->st_compressed);
465         }
466
467         // --------------------------------------------------------------------
468
469         if(unlikely((d->do_events == CONFIG_BOOLEAN_AUTO && (d->rframe || d->tcollisions || d->tcarrier))))
470             d->do_events = CONFIG_BOOLEAN_YES;
471
472         if(d->do_events == CONFIG_BOOLEAN_YES) {
473             if(unlikely(!d->st_events)) {
474
475                 d->st_events = rrdset_create_localhost(
476                         "net_events"
477                         , d->name
478                         , NULL
479                         , d->name
480                         , "net.events"
481                         , "Network Interface Events"
482                         , "events/s"
483                         , 7006
484                         , update_every
485                         , RRDSET_TYPE_LINE
486                 );
487
488                 rrdset_flag_set(d->st_events, RRDSET_FLAG_DETAIL);
489
490                 d->rd_rframe      = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
491                 d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
492                 d->rd_tcarrier    = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
493             }
494             else rrdset_next(d->st_events);
495
496             rrddim_set_by_pointer(d->st_events, d->rd_rframe,      (collected_number)d->rframe);
497             rrddim_set_by_pointer(d->st_events, d->rd_tcollisions, (collected_number)d->tcollisions);
498             rrddim_set_by_pointer(d->st_events, d->rd_tcarrier,    (collected_number)d->tcarrier);
499             rrdset_done(d->st_events);
500         }
501     }
502
503     netdev_cleanup();
504
505     return 0;
506 }