]> arthur.barton.de Git - netdata.git/blobdiff - src/macos_fw.c
Merge pull request #1998 from ktsaou/master
[netdata.git] / src / macos_fw.c
index 47d70eda7b34f9b442d1b8e4b43dfe890eee79d6..c47da52f1634461a81c74f8b4c7a736ae37013a8 100644 (file)
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/IOBSD.h>
+// NEEDED BY do_space, do_inodes
+#include <sys/mount.h>
+// NEEDED BY: struct ifaddrs, getifaddrs()
+#include <net/if.h>
+#include <ifaddrs.h>
+
+// NEEDED BY: do_bandwidth
+#define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
+
+#define MAXDRIVENAME 31
+
+#define KILO_FACTOR 1024
+#define MEGA_FACTOR 1048576     // 1024 * 1024
+#define GIGA_FACTOR 1073741824  // 1024 * 1024 * 1024
 
 int do_macos_iokit(int update_every, usec_t dt) {
     (void)dt;
 
-    static int do_io = -1;
+    static int do_io = -1, do_space = -1, do_inodes = -1, do_bandwidth = -1;
 
     if (unlikely(do_io == -1)) {
-        do_io                  = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
+        do_io                   = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
+        do_space                = config_get_boolean("plugin:macos:sysctl", "space usage for all disks", 1);
+        do_inodes               = config_get_boolean("plugin:macos:sysctl", "inodes usage for all disks", 1);
+        do_bandwidth            = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1);
     }
 
     RRDSET *st;
 
     mach_port_t         master_port;
-    io_registry_entry_t drive;
+    io_registry_entry_t drive, drive_media;
     io_iterator_t       drive_list;
-    CFNumberRef         number;
     CFDictionaryRef     properties, statistics;
-    UInt64              value;
+    CFStringRef         name;
+    CFNumberRef         number;
+    kern_return_t       status;
     collected_number    total_disk_reads = 0;
     collected_number    total_disk_writes = 0;
+    struct diskstat {
+        char name[MAXDRIVENAME];
+        collected_number bytes_read;
+        collected_number bytes_write;
+        collected_number reads;
+        collected_number writes;
+        collected_number time_read;
+        collected_number time_write;
+        collected_number latency_read;
+        collected_number latency_write;
+    } diskstat;
+    struct cur_diskstat {
+        collected_number duration_read_ns;
+        collected_number duration_write_ns;
+        collected_number busy_time_ns;
+    } cur_diskstat;
+    struct prev_diskstat {
+        collected_number bytes_read;
+        collected_number bytes_write;
+        collected_number operations_read;
+        collected_number operations_write;
+        collected_number duration_read_ns;
+        collected_number duration_write_ns;
+        collected_number busy_time_ns;
+    } prev_diskstat;
+
+    // NEEDED BY: do_space, do_inodes
+    struct statfs *mntbuf;
+    int mntsize, i;
+    char mntonname[MNAMELEN + 1];
+    char title[4096 + 1];
+
+    // NEEDED BY: do_bandwidth
+    struct ifaddrs *ifa, *ifap;
 
     /* Get ports and services for drive statistics. */
-    if (IOMasterPort(bootstrap_port, &master_port)) {
+    if (unlikely(IOMasterPort(bootstrap_port, &master_port))) {
         error("MACOS: IOMasterPort() failed");
         do_io = 0;
         error("DISABLED: system.io");
     /* Get the list of all drive objects. */
-    } else if (IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list)) {
+    } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) {
         error("MACOS: IOServiceGetMatchingServices() failed");
         do_io = 0;
         error("DISABLED: system.io");
     } else {
         while ((drive = IOIteratorNext(drive_list)) != 0) {
-            number = 0;
             properties = 0;
             statistics = 0;
-            value = 0;
+            number = 0;
+            bzero(&diskstat, sizeof(diskstat));
+
+            /* Get drive media object. */
+            status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media);
+            if (unlikely(status != KERN_SUCCESS)) {
+                IOObjectRelease(drive);
+                continue;
+            }
+
+            /* Get drive media properties. */
+            if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
+                /* Get disk name. */
+                if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) {
+                    CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8);
+                }
+            }
+
+            /* Release. */
+            CFRelease(properties);
+            IOObjectRelease(drive_media);
 
             /* Obtain the properties for this drive object. */
-            if (IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0)) {
+            if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
                 error("MACOS: IORegistryEntryCreateCFProperties() failed");
                 do_io = 0;
                 error("DISABLED: system.io");
                 break;
-            } else if (properties != 0) {
+            } else if (likely(properties)) {
                 /* Obtain the statistics from the drive properties. */
-                statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey));
+                if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) {
+
+                    // --------------------------------------------------------------------
 
-                if (statistics != 0) {
                     /* Get bytes read. */
-                    number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
-                    if (number != 0) {
-                        CFNumberGetValue(number, kCFNumberSInt64Type, &value);
-                        total_disk_reads += value;
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read);
+                        total_disk_reads += diskstat.bytes_read;
                     }
 
                     /* Get bytes written. */
-                    number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
-                    if (number != 0) {
-                        CFNumberGetValue(number, kCFNumberSInt64Type, &value);
-                        total_disk_writes += value;
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write);
+                        total_disk_writes += diskstat.bytes_write;
+                    }
+
+                    st = rrdset_find_bytype_localhost("disk", diskstat.name);
+                    if (unlikely(!st)) {
+                        st = rrdset_create_localhost("disk", diskstat.name, NULL, diskstat.name, "disk.io"
+                                                     , "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every
+                                                     , RRDSET_TYPE_AREA);
+
+                        rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    }
+                    else rrdset_next(st);
+
+                    prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
+                    prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
+                    rrdset_done(st);
+
+                    // --------------------------------------------------------------------
+
+                    /* Get number of reads. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
+                    }
+
+                    /* Get number of writes. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
+                    }
+
+                    st = rrdset_find_bytype_localhost("disk_ops", diskstat.name);
+                    if (unlikely(!st)) {
+                        st = rrdset_create_localhost("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops"
+                                                     , "Disk Completed I/O Operations", "operations/s", 2001
+                                                     , update_every, RRDSET_TYPE_LINE);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                        rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    }
+                    else rrdset_next(st);
+
+                    prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
+                    prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
+                    rrdset_done(st);
+
+                    // --------------------------------------------------------------------
+
+                    /* Get reads time. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
+                    }
+
+                    /* Get writes time. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
+                    }
+
+                    st = rrdset_find_bytype_localhost("disk_util", diskstat.name);
+                    if (unlikely(!st)) {
+                        st = rrdset_create_localhost("disk_util", diskstat.name, NULL, diskstat.name, "disk.util"
+                                                     , "Disk Utilization Time", "% of time working", 2004, update_every
+                                                     , RRDSET_TYPE_AREA);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                        rrddim_add(st, "utilization", NULL, 1, 10000000, RRD_ALGORITHM_INCREMENTAL);
+                    }
+                    else rrdset_next(st);
+
+                    cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
+                    prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
+                    rrdset_done(st);
+
+                    // --------------------------------------------------------------------
+
+                    /* Get reads latency. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
+                    }
+
+                    /* Get writes latency. */
+                    if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
+                        CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
+                    }
+
+                    st = rrdset_find_bytype_localhost("disk_iotime", diskstat.name);
+                    if (unlikely(!st)) {
+                        st = rrdset_create_localhost("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime"
+                                                     , "Disk Total I/O Time", "milliseconds/s", 2022, update_every
+                                                     , RRDSET_TYPE_LINE);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                        rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_INCREMENTAL);
+                    }
+                    else rrdset_next(st);
+
+                    cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
+                    cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
+                    prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
+                    prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
+                    rrdset_done(st);
+
+                    // --------------------------------------------------------------------
+                    // calculate differential charts
+                    // only if this is not the first time we run
+
+                    if (likely(dt)) {
+
+                        // --------------------------------------------------------------------
+
+                        st = rrdset_find_bytype_localhost("disk_await", diskstat.name);
+                        if (unlikely(!st)) {
+                            st = rrdset_create_localhost("disk_await", diskstat.name, NULL, diskstat.name, "disk.await"
+                                                         , "Average Completed I/O Operation Time", "ms per operation"
+                                                         , 2005, update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                            rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
+                            rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_ABSOLUTE);
+                        }
+                        else rrdset_next(st);
+
+                        rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
+                            (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
+                        rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
+                            (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
+                        rrdset_done(st);
+
+                        // --------------------------------------------------------------------
+
+                        st = rrdset_find_bytype_localhost("disk_avgsz", diskstat.name);
+                        if (unlikely(!st)) {
+                            st = rrdset_create_localhost("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz"
+                                                         , "Average Completed I/O Operation Bandwidth"
+                                                         , "kilobytes per operation", 2006, update_every
+                                                         , RRDSET_TYPE_AREA);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                            rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+                            rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
+                        }
+                        else rrdset_next(st);
+
+                        rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
+                            (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
+                        rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
+                            (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
+                        rrdset_done(st);
+
+                        // --------------------------------------------------------------------
+
+                        st = rrdset_find_bytype_localhost("disk_svctm", diskstat.name);
+                        if (unlikely(!st)) {
+                            st = rrdset_create_localhost("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm"
+                                                         , "Average Service Time", "ms per operation", 2007
+                                                         , update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                            rrddim_add(st, "svctm", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
+                        }
+                        else rrdset_next(st);
+
+                        rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
+                            (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
+                        rrdset_done(st);
                     }
                 }
 
@@ -79,12 +315,13 @@ int do_macos_iokit(int update_every, usec_t dt) {
         IOObjectRelease(drive_list);
     }
 
-    if (do_io) {
-        st = rrdset_find_bytype("system", "io");
+    if (likely(do_io)) {
+        st = rrdset_find_bytype_localhost("system", "io");
         if (unlikely(!st)) {
-            st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
-            rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150
+                                         , update_every, RRDSET_TYPE_AREA);
+            rrddim_add(st, "in",  NULL,  1, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "out", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -93,5 +330,181 @@ int do_macos_iokit(int update_every, usec_t dt) {
         rrdset_done(st);
     }
 
+    // Can be merged with FreeBSD plugin
+    // --------------------------------------------------------------------------
+
+    if (likely(do_space || do_inodes)) {
+        // there is no mount info in sysctl MIBs
+        if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
+            error("MACOS: getmntinfo() failed");
+            do_space = 0;
+            error("DISABLED: disk_space.X");
+            do_inodes = 0;
+            error("DISABLED: disk_inodes.X");
+        } else {
+            for (i = 0; i < mntsize; i++) {
+                if (mntbuf[i].f_flags == MNT_RDONLY ||
+                        mntbuf[i].f_blocks == 0 ||
+                        // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
+                        strcmp(mntbuf[i].f_fstypename, "autofs") == 0 ||
+                        strcmp(mntbuf[i].f_fstypename, "procfs") == 0 ||
+                        strcmp(mntbuf[i].f_fstypename, "subfs") == 0 ||
+                        strcmp(mntbuf[i].f_fstypename, "devfs") == 0 ||
+                        strcmp(mntbuf[i].f_fstypename, "none") == 0)
+                    continue;
+
+                // --------------------------------------------------------------------------
+
+                if (likely(do_space)) {
+                    st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname);
+                    if (unlikely(!st)) {
+                        snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
+                        st = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
+                                                     , "disk.space", title, "GB", 2023, update_every
+                                                     , RRDSET_TYPE_STACKED);
+
+                        rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
+                                   RRD_ALGORITHM_ABSOLUTE);
+                    } else
+                        rrdset_next(st);
+
+                    rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail);
+                    rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree));
+                    rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail));
+                    rrdset_done(st);
+                }
+
+                // --------------------------------------------------------------------------
+
+                if (likely(do_inodes)) {
+                    st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname);
+                    if (unlikely(!st)) {
+                        snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
+                        st = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
+                                                     , "disk.inodes", title, "Inodes", 2024, update_every
+                                                     , RRDSET_TYPE_STACKED);
+
+                        rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                    } else
+                        rrdset_next(st);
+
+                    rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree);
+                    rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree));
+                    rrdset_done(st);
+                }
+            }
+        }
+    }
+
+    // Can be merged with FreeBSD plugin
+    // --------------------------------------------------------------------
+
+    if (likely(do_bandwidth)) {
+        if (unlikely(getifaddrs(&ifap))) {
+            error("MACOS: getifaddrs()");
+            do_bandwidth = 0;
+            error("DISABLED: system.ipv4");
+        } else {
+            for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+                if (ifa->ifa_addr->sa_family != AF_LINK)
+                        continue;
+
+                // --------------------------------------------------------------------
+
+                st = rrdset_find_bytype_localhost("net", ifa->ifa_name);
+                if (unlikely(!st)) {
+                    st = rrdset_create_localhost("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth"
+                                                 , "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+
+                    rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "received", IFA_DATA(ibytes));
+                rrddim_set(st, "sent", IFA_DATA(obytes));
+                rrdset_done(st);
+
+                // --------------------------------------------------------------------
+
+                st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name);
+                if (unlikely(!st)) {
+                    st = rrdset_create_localhost("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets"
+                                                 , "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "received", IFA_DATA(ipackets));
+                rrddim_set(st, "sent", IFA_DATA(opackets));
+                rrddim_set(st, "multicast_received", IFA_DATA(imcasts));
+                rrddim_set(st, "multicast_sent", IFA_DATA(omcasts));
+                rrdset_done(st);
+
+                // --------------------------------------------------------------------
+
+                st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name);
+                if (unlikely(!st)) {
+                    st = rrdset_create_localhost("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors"
+                                                 , "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "inbound", IFA_DATA(ierrors));
+                rrddim_set(st, "outbound", IFA_DATA(oerrors));
+                rrdset_done(st);
+
+                // --------------------------------------------------------------------
+
+                st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name);
+                if (unlikely(!st)) {
+                    st = rrdset_create_localhost("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops"
+                                                 , "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "inbound", IFA_DATA(iqdrops));
+                rrdset_done(st);
+
+                // --------------------------------------------------------------------
+
+                st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name);
+                if (unlikely(!st)) {
+                    st = rrdset_create_localhost("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events"
+                                                 , "Network Interface Events", "events/s", 7006, update_every
+                                                 , RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "collisions", IFA_DATA(collisions));
+                rrdset_done(st);
+            }
+
+            freeifaddrs(ifap);
+        }
+    }
+
+
     return 0;
 }