]> arthur.barton.de Git - netdata.git/blob - src/macos_fw.c
7917855d3a292487edc78a906de0b435f7560762
[netdata.git] / src / macos_fw.c
1 #include "common.h"
2 #include <CoreFoundation/CoreFoundation.h>
3 #include <IOKit/IOKitLib.h>
4 #include <IOKit/storage/IOBlockStorageDriver.h>
5 #include <IOKit/IOBSD.h>
6 // NEEDED BY do_space, do_inodes
7 #include <sys/mount.h>
8 // NEEDED BY: struct ifaddrs, getifaddrs()
9 #include <net/if.h>
10 #include <ifaddrs.h>
11
12 // NEEDED BY: do_bandwidth
13 #define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
14
15 #define MAXDRIVENAME 31
16
17 #define KILO_FACTOR 1024
18 #define MEGA_FACTOR 1048576     // 1024 * 1024
19 #define GIGA_FACTOR 1073741824  // 1024 * 1024 * 1024
20
21 int do_macos_iokit(int update_every, usec_t dt) {
22     (void)dt;
23
24     static int do_io = -1, do_space = -1, do_inodes = -1, do_bandwidth = -1;
25
26     if (unlikely(do_io == -1)) {
27         do_io                   = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
28         do_space                = config_get_boolean("plugin:macos:sysctl", "space usage for all disks", 1);
29         do_inodes               = config_get_boolean("plugin:macos:sysctl", "inodes usage for all disks", 1);
30         do_bandwidth            = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1);
31     }
32
33     RRDSET *st;
34
35     mach_port_t         master_port;
36     io_registry_entry_t drive, drive_media;
37     io_iterator_t       drive_list;
38     CFDictionaryRef     properties, statistics;
39     CFStringRef         name;
40     CFNumberRef         number;
41     kern_return_t       status;
42     collected_number    total_disk_reads = 0;
43     collected_number    total_disk_writes = 0;
44     struct diskstat {
45         char name[MAXDRIVENAME];
46         collected_number bytes_read;
47         collected_number bytes_write;
48         collected_number reads;
49         collected_number writes;
50         collected_number time_read;
51         collected_number time_write;
52         collected_number latency_read;
53         collected_number latency_write;
54     } diskstat;
55     struct cur_diskstat {
56         collected_number duration_read_ns;
57         collected_number duration_write_ns;
58         collected_number busy_time_ns;
59     } cur_diskstat;
60     struct prev_diskstat {
61         collected_number bytes_read;
62         collected_number bytes_write;
63         collected_number operations_read;
64         collected_number operations_write;
65         collected_number duration_read_ns;
66         collected_number duration_write_ns;
67         collected_number busy_time_ns;
68     } prev_diskstat;
69
70     // NEEDED BY: do_space, do_inodes
71     struct statfs *mntbuf;
72     int mntsize, i;
73     char mntonname[MNAMELEN + 1];
74     char title[4096 + 1];
75
76     // NEEDED BY: do_bandwidth
77     struct ifaddrs *ifa, *ifap;
78
79     /* Get ports and services for drive statistics. */
80     if (unlikely(IOMasterPort(bootstrap_port, &master_port))) {
81         error("MACOS: IOMasterPort() failed");
82         do_io = 0;
83         error("DISABLED: system.io");
84     /* Get the list of all drive objects. */
85     } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) {
86         error("MACOS: IOServiceGetMatchingServices() failed");
87         do_io = 0;
88         error("DISABLED: system.io");
89     } else {
90         while ((drive = IOIteratorNext(drive_list)) != 0) {
91             properties = 0;
92             statistics = 0;
93             number = 0;
94             bzero(&diskstat, sizeof(diskstat));
95
96             /* Get drive media object. */
97             status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media);
98             if (unlikely(status != KERN_SUCCESS)) {
99                 IOObjectRelease(drive);
100                 continue;
101             }
102
103             /* Get drive media properties. */
104             if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
105                 /* Get disk name. */
106                 if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) {
107                     CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8);
108                 }
109             }
110
111             /* Release. */
112             CFRelease(properties);
113             IOObjectRelease(drive_media);
114
115             /* Obtain the properties for this drive object. */
116             if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
117                 error("MACOS: IORegistryEntryCreateCFProperties() failed");
118                 do_io = 0;
119                 error("DISABLED: system.io");
120                 break;
121             } else if (likely(properties)) {
122                 /* Obtain the statistics from the drive properties. */
123                 if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) {
124
125                     // --------------------------------------------------------------------
126
127                     /* Get bytes read. */
128                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
129                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read);
130                         total_disk_reads += diskstat.bytes_read;
131                     }
132
133                     /* Get bytes written. */
134                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
135                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write);
136                         total_disk_writes += diskstat.bytes_write;
137                     }
138
139                     st = rrdset_find_bytype_localhost("disk", diskstat.name);
140                     if (unlikely(!st)) {
141                         st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
142
143                         rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
144                         rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
145                     }
146                     else rrdset_next(st);
147
148                     prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
149                     prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
150                     rrdset_done(st);
151
152                     // --------------------------------------------------------------------
153
154                     /* Get number of reads. */
155                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
156                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
157                     }
158
159                     /* Get number of writes. */
160                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
161                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
162                     }
163
164                     st = rrdset_find_bytype_localhost("disk_ops", diskstat.name);
165                     if (unlikely(!st)) {
166                         st = rrdset_create("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
167                         st->isdetail = 1;
168
169                         rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
170                         rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
171                     }
172                     else rrdset_next(st);
173
174                     prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
175                     prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
176                     rrdset_done(st);
177
178                     // --------------------------------------------------------------------
179
180                     /* Get reads time. */
181                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
182                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
183                     }
184
185                     /* Get writes time. */
186                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
187                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
188                     }
189
190                     st = rrdset_find_bytype_localhost("disk_util", diskstat.name);
191                     if (unlikely(!st)) {
192                         st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
193                         st->isdetail = 1;
194
195                         rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_ALGORITHM_INCREMENTAL);
196                     }
197                     else rrdset_next(st);
198
199                     cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
200                     prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
201                     rrdset_done(st);
202
203                     // --------------------------------------------------------------------
204
205                     /* Get reads latency. */
206                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
207                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
208                     }
209
210                     /* Get writes latency. */
211                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
212                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
213                     }
214
215                     st = rrdset_find_bytype_localhost("disk_iotime", diskstat.name);
216                     if (unlikely(!st)) {
217                         st = rrdset_create("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
218                         st->isdetail = 1;
219
220                         rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ALGORITHM_INCREMENTAL);
221                         rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ALGORITHM_INCREMENTAL);
222                     }
223                     else rrdset_next(st);
224
225                     cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
226                     cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
227                     prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
228                     prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
229                     rrdset_done(st);
230
231                     // --------------------------------------------------------------------
232                     // calculate differential charts
233                     // only if this is not the first time we run
234
235                     if (likely(dt)) {
236
237                         // --------------------------------------------------------------------
238
239                         st = rrdset_find_bytype_localhost("disk_await", diskstat.name);
240                         if (unlikely(!st)) {
241                             st = rrdset_create("disk_await", diskstat.name, NULL, diskstat.name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
242                             st->isdetail = 1;
243
244                             rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ALGORITHM_ABSOLUTE);
245                             rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ALGORITHM_ABSOLUTE);
246                         }
247                         else rrdset_next(st);
248
249                         rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
250                             (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
251                         rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
252                             (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
253                         rrdset_done(st);
254
255                         // --------------------------------------------------------------------
256
257                         st = rrdset_find_bytype_localhost("disk_avgsz", diskstat.name);
258                         if (unlikely(!st)) {
259                             st = rrdset_create("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
260                             st->isdetail = 1;
261
262                             rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ALGORITHM_ABSOLUTE);
263                             rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ALGORITHM_ABSOLUTE);
264                         }
265                         else rrdset_next(st);
266
267                         rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
268                             (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
269                         rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
270                             (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
271                         rrdset_done(st);
272
273                         // --------------------------------------------------------------------
274
275                         st = rrdset_find_bytype_localhost("disk_svctm", diskstat.name);
276                         if (unlikely(!st)) {
277                             st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
278                             st->isdetail = 1;
279
280                             rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ALGORITHM_ABSOLUTE);
281                         }
282                         else rrdset_next(st);
283
284                         rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
285                             (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
286                         rrdset_done(st);
287                     }
288                 }
289
290                 /* Release. */
291                 CFRelease(properties);
292             }
293
294             /* Release. */
295             IOObjectRelease(drive);
296         }
297         IOIteratorReset(drive_list);
298
299         /* Release. */
300         IOObjectRelease(drive_list);
301     }
302
303     if (likely(do_io)) {
304         st = rrdset_find_bytype_localhost("system", "io");
305         if (unlikely(!st)) {
306             st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
307             rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
308             rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
309         }
310         else rrdset_next(st);
311
312         rrddim_set(st, "in", total_disk_reads);
313         rrddim_set(st, "out", total_disk_writes);
314         rrdset_done(st);
315     }
316
317     // Can be merged with FreeBSD plugin
318     // --------------------------------------------------------------------------
319
320     if (likely(do_space || do_inodes)) {
321         // there is no mount info in sysctl MIBs
322         if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
323             error("MACOS: getmntinfo() failed");
324             do_space = 0;
325             error("DISABLED: disk_space.X");
326             do_inodes = 0;
327             error("DISABLED: disk_inodes.X");
328         } else {
329             for (i = 0; i < mntsize; i++) {
330                 if (mntbuf[i].f_flags == MNT_RDONLY ||
331                         mntbuf[i].f_blocks == 0 ||
332                         // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
333                         strcmp(mntbuf[i].f_fstypename, "autofs") == 0 ||
334                         strcmp(mntbuf[i].f_fstypename, "procfs") == 0 ||
335                         strcmp(mntbuf[i].f_fstypename, "subfs") == 0 ||
336                         strcmp(mntbuf[i].f_fstypename, "devfs") == 0 ||
337                         strcmp(mntbuf[i].f_fstypename, "none") == 0)
338                     continue;
339
340                 // --------------------------------------------------------------------------
341
342                 if (likely(do_space)) {
343                     st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname);
344                     if (unlikely(!st)) {
345                         snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
346                         st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
347                                            update_every,
348                                            RRDSET_TYPE_STACKED);
349
350                         rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ALGORITHM_ABSOLUTE);
351                         rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ALGORITHM_ABSOLUTE);
352                         rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
353                                    RRDDIM_ALGORITHM_ABSOLUTE);
354                     } else
355                         rrdset_next(st);
356
357                     rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail);
358                     rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree));
359                     rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail));
360                     rrdset_done(st);
361                 }
362
363                 // --------------------------------------------------------------------------
364
365                 if (likely(do_inodes)) {
366                     st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname);
367                     if (unlikely(!st)) {
368                         snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
369                         st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024,
370                                            update_every, RRDSET_TYPE_STACKED);
371
372                         rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ALGORITHM_ABSOLUTE);
373                         rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ALGORITHM_ABSOLUTE);
374                         rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ALGORITHM_ABSOLUTE);
375                     } else
376                         rrdset_next(st);
377
378                     rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree);
379                     rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree));
380                     rrdset_done(st);
381                 }
382             }
383         }
384     }
385
386     // Can be merged with FreeBSD plugin
387     // --------------------------------------------------------------------
388
389     if (likely(do_bandwidth)) {
390         if (unlikely(getifaddrs(&ifap))) {
391             error("MACOS: getifaddrs()");
392             do_bandwidth = 0;
393             error("DISABLED: system.ipv4");
394         } else {
395             for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
396                 if (ifa->ifa_addr->sa_family != AF_LINK)
397                         continue;
398
399                 // --------------------------------------------------------------------
400
401                 st = rrdset_find_bytype_localhost("net", ifa->ifa_name);
402                 if (unlikely(!st)) {
403                     st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
404
405                     rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
406                     rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_ALGORITHM_INCREMENTAL);
407                 }
408                 else rrdset_next(st);
409
410                 rrddim_set(st, "received", IFA_DATA(ibytes));
411                 rrddim_set(st, "sent", IFA_DATA(obytes));
412                 rrdset_done(st);
413
414                 // --------------------------------------------------------------------
415
416                 st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name);
417                 if (unlikely(!st)) {
418                     st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
419                     st->isdetail = 1;
420
421                     rrddim_add(st, "received", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
422                     rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
423                     rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
424                     rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
425                 }
426                 else rrdset_next(st);
427
428                 rrddim_set(st, "received", IFA_DATA(ipackets));
429                 rrddim_set(st, "sent", IFA_DATA(opackets));
430                 rrddim_set(st, "multicast_received", IFA_DATA(imcasts));
431                 rrddim_set(st, "multicast_sent", IFA_DATA(omcasts));
432                 rrdset_done(st);
433
434                 // --------------------------------------------------------------------
435
436                 st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name);
437                 if (unlikely(!st)) {
438                     st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
439                     st->isdetail = 1;
440
441                     rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
442                     rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
443                 }
444                 else rrdset_next(st);
445
446                 rrddim_set(st, "inbound", IFA_DATA(ierrors));
447                 rrddim_set(st, "outbound", IFA_DATA(oerrors));
448                 rrdset_done(st);
449
450                 // --------------------------------------------------------------------
451
452                 st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name);
453                 if (unlikely(!st)) {
454                     st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
455                     st->isdetail = 1;
456
457                     rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
458                 }
459                 else rrdset_next(st);
460
461                 rrddim_set(st, "inbound", IFA_DATA(iqdrops));
462                 rrdset_done(st);
463
464                 // --------------------------------------------------------------------
465
466                 st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name);
467                 if (unlikely(!st)) {
468                     st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
469                     st->isdetail = 1;
470
471                     rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
472                     rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
473                     rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_ALGORITHM_INCREMENTAL);
474                 }
475                 else rrdset_next(st);
476
477                 rrddim_set(st, "collisions", IFA_DATA(collisions));
478                 rrdset_done(st);
479             }
480
481             freeifaddrs(ifap);
482         }
483     }
484
485
486     return 0;
487 }