]> arthur.barton.de Git - netdata.git/commitdiff
collect disk space utilization from all mounted disks; fixes #747; possibly fixes...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 22 Oct 2016 23:08:33 +0000 (02:08 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 22 Oct 2016 23:08:33 +0000 (02:08 +0300)
src/common.h
src/proc_diskstats.c
src/proc_self_mountinfo.c
src/proc_self_mountinfo.h

index 9ffa8c8bc4efb7b43de058a0cae064c183f43145..b9721824f75f083f6f4475dd716b13ce7ffbb290 100644 (file)
@@ -64,6 +64,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <uuid/uuid.h>
+#include <mntent.h>
 
 #ifdef STORAGE_WITH_MATH
 #include <math.h>
index 6983abdfeb8d0396aae709209dfb141681a0e031..96cece6cbd183f141040a83bb46b2e361491ee8e 100644 (file)
@@ -6,13 +6,19 @@
 #define DISK_TYPE_PARTITION 2
 #define DISK_TYPE_CONTAINER 3
 
+#ifndef NETDATA_RELOAD_MOUNTINFO_EVERY
+#define NETDATA_RELOAD_MOUNTINFO_EVERY 10
+#endif
+
 static struct disk {
     char *disk;             // the name of the disk (sda, sdb, etc)
     unsigned long major;
     unsigned long minor;
     int sector_size;
     int type;
+
     char *mount_point;
+    uint32_t mount_point_hash;
 
     // disk options caching
     int configured;
@@ -29,109 +35,148 @@ static struct disk {
     struct disk *next;
 } *disk_root = NULL;
 
-/* Return true if N is a known integer value.  On many file systems,
-   UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
-   represents unknown.  Use a rule that works on AIX file systems, and
-   that almost-always works on other types.  */
-static inline int known_value (uintmax_t n) { return n < UINTMAX_MAX - 1; }
+static struct mountinfo *disk_mountinfo_root = NULL;
+
+static inline void mountinfo_reload(int force) {
+    static time_t last_loaded = 0;
+    time_t now = time(NULL);
+
+    if(force || now - last_loaded >= NETDATA_RELOAD_MOUNTINFO_EVERY) {
+//#ifdef NETDATA_INTERNAL_CHECKS
+//        info("Reloading mountinfo");
+//#endif
+
+        // mountinfo_free() can be called with NULL disk_mountinfo_root
+        mountinfo_free(disk_mountinfo_root);
+
+        // re-read mountinfo in case something changed
+        disk_mountinfo_root = mountinfo_read();
+
+        last_loaded = now;
+    }
+}
 
-static inline void disk_space_stats(struct disk *d, const char *disk, const char *family, int update_every, unsigned long long dt) {
+static inline void do_disk_space_stats(struct disk *d, const char *mount_point, const char *mount_source, const char *disk, const char *family, int update_every, unsigned long long dt) {
     struct statvfs buff_statvfs;
-    RRDSET *st;
+    if (statvfs(mount_point, &buff_statvfs) < 0) {
+        error("Failed statvfs() for '%s' (disk '%s')", mount_point, disk);
+        return;
+    }
 
-    if (statvfs(d->mount_point, &buff_statvfs) < 0)
-        error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk);
-    else {
-        struct stat buff_stat;
+    int do_space, do_inodes;
 
+    if(d) {
         // verify we collected the metrics for the right disk.
         // if not the mountpoint has changed.
 
-        if(stat(d->mount_point, &buff_stat) == -1)
-            error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk);
-        else {
-            if(major(buff_stat.st_dev) == d->major && minor(buff_stat.st_dev) == d->minor) {
-
-                // http://stackoverflow.com/a/4965511
-                // taken from get_fs_usage() found in coreutils
-                unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize;
-
-                fsblkcnt_t bavail         = buff_statvfs.f_bavail;
-                fsblkcnt_t btotal         = buff_statvfs.f_blocks;
-                fsblkcnt_t bavail_root    = buff_statvfs.f_bfree;
-                fsblkcnt_t breserved_root = bavail_root - bavail;
-                fsblkcnt_t bused;
-                if(likely(btotal >= bavail_root))
-                    bused = btotal - bavail_root;
-                else
-                    bused = bavail_root - btotal;
+        struct stat buff_stat;
+        if(stat(mount_point, &buff_stat) == -1) {
+            error("Failed to stat() for '%s' (disk '%s')", mount_point, disk);
+            return;
+        }
+        else if(major(buff_stat.st_dev) != d->major || minor(buff_stat.st_dev) != d->minor) {
+            error("Disk '%s' (disk '%s') switched major:minor", mount_point, disk);
+            freez(d->mount_point);
+            d->mount_point = NULL;
+            d->mount_point_hash = 0;
+            return;
+        }
+
+        do_space = d->do_space;
+        do_inodes = d->do_inodes;
+    }
+    else {
+        char var_name[4096 + 1];
+        snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", mount_point);
+
+        int def_space = CONFIG_ONDEMAND_ONDEMAND;
+
+        // check the user configuration (this will also show our 'on demand' decision)
+        def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);
+
+        int ddo_space = def_space,
+                ddo_inodes = def_space;
+
+        do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space);
+        do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes);
+    }
+
+    // taken from get_fs_usage() found in coreutils
+    unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize;
+
+    fsblkcnt_t bavail         = buff_statvfs.f_bavail;
+    fsblkcnt_t btotal         = buff_statvfs.f_blocks;
+    fsblkcnt_t bavail_root    = buff_statvfs.f_bfree;
+    fsblkcnt_t breserved_root = bavail_root - bavail;
+    fsblkcnt_t bused;
+    if(likely(btotal >= bavail_root))
+        bused = btotal - bavail_root;
+    else
+        bused = bavail_root - btotal;
 
 #ifdef NETDATA_INTERNAL_CHECKS
-                if(unlikely(btotal != bavail + breserved_root + bused))
-                    error("Disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", d->mount_point, d->disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused);
+    if(unlikely(btotal != bavail + breserved_root + bused))
+        error("Disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mount_point, disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused);
 #endif
 
-                // --------------------------------------------------------------------------
+    // --------------------------------------------------------------------------
 
-                fsfilcnt_t favail         = buff_statvfs.f_favail;
-                fsfilcnt_t ftotal         = buff_statvfs.f_files;
-                fsfilcnt_t favail_root    = buff_statvfs.f_ffree;
-                fsfilcnt_t freserved_root = favail_root - favail;
-                fsfilcnt_t fused          = ftotal - favail_root;
+    fsfilcnt_t favail         = buff_statvfs.f_favail;
+    fsfilcnt_t ftotal         = buff_statvfs.f_files;
+    fsfilcnt_t favail_root    = buff_statvfs.f_ffree;
+    fsfilcnt_t freserved_root = favail_root - favail;
+    fsfilcnt_t fused          = ftotal - favail_root;
 
 #ifdef NETDATA_INTERNAL_CHECKS
-                if(unlikely(btotal != bavail + breserved_root + bused))
-                    error("Disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", d->mount_point, d->disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused);
+    if(unlikely(btotal != bavail + breserved_root + bused))
+        error("Disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused);
 #endif
 
-                // --------------------------------------------------------------------------
-
-                if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) {
-                    d->do_space = CONFIG_ONDEMAND_YES;
+    // --------------------------------------------------------------------------
 
-                    st = rrdset_find_bytype("disk_space", disk);
-                    if(!st) {
-                        st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
-                        st->isdetail = 1;
+    RRDSET *st;
 
-                        rrddim_add(st, "avail", NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "used" , NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "reserved_for_root", "reserved for root", bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                    }
-                    else rrdset_next_usec(st, dt);
+    if(do_space == CONFIG_ONDEMAND_YES || (do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) {
+        st = rrdset_find_bytype("disk_space", disk);
+        if(!st) {
+            char title[4096 + 1];
+            snprintfz(title, 4096, "Disk Space Usage for %s [%s]", family, mount_source);
+            st = rrdset_create("disk_space", disk, NULL, family, "disk.space", title, "GB", 2023, update_every, RRDSET_TYPE_STACKED);
 
-                    rrddim_set(st, "avail", bavail);
-                    rrddim_set(st, "used", bused);
-                    rrddim_set(st, "reserved_for_root", breserved_root);
-                    rrdset_done(st);
-                }
+            rrddim_add(st, "avail", NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "used" , NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "reserved_for_root", "reserved for root", bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next_usec(st, dt);
 
-                // --------------------------------------------------------------------------
+        rrddim_set(st, "avail", bavail);
+        rrddim_set(st, "used", bused);
+        rrddim_set(st, "reserved_for_root", breserved_root);
+        rrdset_done(st);
+    }
 
-                if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) {
-                    st = rrdset_find_bytype("disk_inodes", disk);
-                    if(!st) {
-                        st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
-                        st->isdetail = 1;
+    // --------------------------------------------------------------------------
 
-                        rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
-                    }
-                    else rrdset_next_usec(st, dt);
+    if(do_inodes == CONFIG_ONDEMAND_YES || (do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) {
+        st = rrdset_find_bytype("disk_inodes", disk);
+        if(!st) {
+            char title[4096 + 1];
+            snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", family, mount_source);
+            st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", title, "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
 
-                    rrddim_set(st, "avail", favail);
-                    rrddim_set(st, "used", fused);
-                    rrddim_set(st, "reserved_for_root", freserved_root);
-                    rrdset_done(st);
-                }
-            }
+            rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
         }
+        else rrdset_next_usec(st, dt);
+
+        rrddim_set(st, "avail", favail);
+        rrddim_set(st, "used", fused);
+        rrddim_set(st, "reserved_for_root", freserved_root);
+        rrdset_done(st);
     }
 }
 
-static struct mountinfo *disk_mountinfo_root = NULL;
-
 static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) {
     static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
     static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = "";
@@ -216,21 +261,20 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis
     // mountinfo_find() can be called with NULL disk_mountinfo_root
     struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
     if(unlikely(!mi)) {
-        // mountinfo_free() can be called with NULL disk_mountinfo_root
-        mountinfo_free(disk_mountinfo_root);
-
-        // re-read mountinfo in case something changed
-        disk_mountinfo_root = mountinfo_read();
+        mountinfo_reload(1);
 
         // search again for this disk
         mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
     }
 
-    if(mi)
+    if(mi) {
         d->mount_point = strdupz(mi->mount_point);
-        // no need to check for NULL
-    else
+        d->mount_point_hash = mi->mount_point_hash;
+    }
+    else {
         d->mount_point = NULL;
+        d->mount_point_hash = 0;
+    }
 
     // ------------------------------------------------------------------------
     // find the disk sector size
@@ -344,6 +388,9 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
     uint32_t lines = procfile_lines(ff), l;
     uint32_t words;
 
+    // this is smart enough not to reload it every time
+    mountinfo_reload(0);
+
     for(l = 0; l < lines ;l++) {
         // --------------------------------------------------------------------------
         // Read parameters
@@ -723,12 +770,41 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
             }
         }
 
+        /*
         // --------------------------------------------------------------------------
         // space metrics
 
         if(d->mount_point && (d->do_space || d->do_inodes) ) {
-            disk_space_stats(d, disk, family, update_every, dt);
+            do_disk_space_stats(d, d->mount_point, disk, disk, family, update_every, dt);
         }
+        */
+    }
+
+    // --------------------------------------------------------------------------
+    // space metrics for non-block devices
+
+    struct mountinfo *mi;
+    for(mi = disk_mountinfo_root; mi ;mi = mi->next) {
+        if(unlikely(mi->flags & MOUNTINFO_IS_DUMMY || mi->flags & MOUNTINFO_IS_BIND || mi->flags & MOUNTINFO_IS_SAME_DEV || mi->flags & MOUNTINFO_NO_STAT || mi->flags & MOUNTINFO_NO_SIZE))
+            continue;
+
+        /*
+        // skip the ones with block devices
+        int skip = 0;
+        struct disk *d;
+        for(d = disk_root; d ;d = d->next) {
+            if(unlikely(d->mount_point && mi->mount_point_hash == d->mount_point_hash && strcmp(mi->mount_point, d->mount_point))) {
+                skip = 1;
+                break;
+            }
+        }
+
+        if(unlikely(skip))
+            continue;
+        */
+
+        // fprintf(stderr, "Will process mount point '%s', source '%s', filesystem '%s'\n", mi->mount_point, mi->mount_source, mi->filesystem);
+        do_disk_space_stats(NULL, mi->mount_point, mi->mount_source, mi->persistent_id, mi->mount_point , update_every, dt);
     }
 
     return 0;
index 51aea7aee1a9b7a653dba8f15f16d6777fae5c4f..3c3e5af8922d98c8ebcbba4b7445826fa05daaef 100644 (file)
@@ -1,5 +1,48 @@
 #include "common.h"
 
+// ----------------------------------------------------------------------------
+// taken from gnulib/mountlist.c
+
+#ifndef ME_REMOTE
+/* A file system is "remote" if its Fs_name contains a ':'
+   or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
+   or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).  */
+# define ME_REMOTE(Fs_name, Fs_type)            \
+    (strchr (Fs_name, ':') != NULL              \
+     || ((Fs_name)[0] == '/'                    \
+         && (Fs_name)[1] == '/'                 \
+         && (strcmp (Fs_type, "smbfs") == 0     \
+             || strcmp (Fs_type, "cifs") == 0)) \
+     || (strcmp("-hosts", Fs_name) == 0))
+#endif
+
+#define ME_DUMMY_0(Fs_name, Fs_type)            \
+  (strcmp (Fs_type, "autofs") == 0              \
+   || strcmp (Fs_type, "proc") == 0             \
+   || strcmp (Fs_type, "subfs") == 0            \
+   /* for Linux 2.6/3.x */                      \
+   || strcmp (Fs_type, "debugfs") == 0          \
+   || strcmp (Fs_type, "devpts") == 0           \
+   || strcmp (Fs_type, "fusectl") == 0          \
+   || strcmp (Fs_type, "mqueue") == 0           \
+   || strcmp (Fs_type, "rpc_pipefs") == 0       \
+   || strcmp (Fs_type, "sysfs") == 0            \
+   /* FreeBSD, Linux 2.4 */                     \
+   || strcmp (Fs_type, "devfs") == 0            \
+   /* for NetBSD 3.0 */                         \
+   || strcmp (Fs_type, "kernfs") == 0           \
+   /* for Irix 6.5 */                           \
+   || strcmp (Fs_type, "ignore") == 0)
+
+/* Historically, we have marked as "dummy" any file system of type "none",
+   but now that programs like du need to know about bind-mounted directories,
+   we grant an exception to any with "bind" in its list of mount options.
+   I.e., those are *not* dummy entries.  */
+# define ME_DUMMY(Fs_name, Fs_type)            \
+  (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
+
+// ----------------------------------------------------------------------------
+
 // find the mount info with the given major:minor
 // in the supplied linked list of mountinfo structures
 struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor) {
@@ -72,6 +115,7 @@ void mountinfo_free(struct mountinfo *mi) {
     freez(mi->root);
     freez(mi->mount_point);
     freez(mi->mount_options);
+    freez(mi->persistent_id);
 
 /*
     if(mi->optional_fields_count) {
@@ -138,14 +182,6 @@ struct mountinfo *mountinfo_read() {
 
         mi = mallocz(sizeof(struct mountinfo));
 
-        if(unlikely(!root))
-            root = last = mi;
-        else
-            last->next = mi;
-
-        last = mi;
-        mi->next = NULL;
-
         unsigned long w = 0;
         mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
         mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
@@ -155,12 +191,15 @@ struct mountinfo *mountinfo_read() {
 
         if(!*minor) {
             error("Cannot parse major:minor on '%s' at line %lu of '%s'", major, l + 1, filename);
+            freez(mi);
             continue;
         }
 
         *minor = '\0';
         minor++;
 
+        mi->flags = 0;
+
         mi->major = strtoul(major, NULL, 10);
         mi->minor = strtoul(minor, NULL, 10);
 
@@ -170,6 +209,10 @@ struct mountinfo *mountinfo_read() {
         mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++;
         mi->mount_point_hash = simple_hash(mi->mount_point);
 
+        mi->persistent_id = strdupz(mi->mount_point);
+        netdata_fix_chart_id(mi->persistent_id);
+        mi->persistent_id_hash = simple_hash(mi->persistent_id);
+
         mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++;
 
         // count the optional fields
@@ -214,29 +257,124 @@ struct mountinfo *mountinfo_read() {
             mi->mount_source_hash = simple_hash(mi->mount_source);
 
             mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++;
+
+            if(ME_DUMMY(mi->mount_source, mi->filesystem))
+                mi->flags |= MOUNTINFO_IS_DUMMY;
+
+            if(ME_REMOTE(mi->mount_source, mi->filesystem))
+                mi->flags |= MOUNTINFO_IS_REMOTE;
+
+            // mark as BIND the duplicates (i.e. same filesystem + same source)
+            {
+                struct stat buf;
+                if(unlikely(stat(mi->mount_point, &buf) == -1)) {
+                    mi->st_dev = 0;
+                    mi->flags |= MOUNTINFO_NO_STAT;
+                }
+                else {
+                    mi->st_dev = buf.st_dev;
+
+                    struct mountinfo *mt;
+                    for(mt = root; mt; mt = mt->next) {
+                        if(unlikely(mt->st_dev == mi->st_dev && !(mi->flags & MOUNTINFO_NO_STAT))) {
+                            if(strlen(mi->mount_point) < strlen(mt->mount_point))
+                                mt->flags |= MOUNTINFO_IS_SAME_DEV;
+                            else
+                                mi->flags |= MOUNTINFO_IS_SAME_DEV;
+                        }
+                    }
+                }
+            }
         }
         else {
             mi->filesystem = NULL;
+            mi->filesystem_hash = 0;
+
             mi->mount_source = NULL;
+            mi->mount_source_hash = 0;
+
             mi->super_options = NULL;
+
+            mi->st_dev = 0;
+        }
+
+        // check if it has size
+        {
+            struct statvfs buff_statvfs;
+            if(statvfs(mi->mount_point, &buff_statvfs) < 0) {
+                mi->flags |= MOUNTINFO_NO_STAT;
+            }
+            else if(!buff_statvfs.f_blocks /* || !buff_statvfs.f_files */) {
+                mi->flags |= MOUNTINFO_NO_SIZE;
+            }
         }
 
+        // link it
+        if(unlikely(!root))
+            root = mi;
+        else
+            last->next = mi;
+
+        last = mi;
+        mi->next = NULL;
+
 /*
-        info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
+#ifdef NETDATA_INTERNAL_CHECKS
+        fprintf(stderr, "MOUNTINFO: %ld %ld %lu:%lu root '%s', persistent id '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'%s%s%s%s%s%s\n",
              mi->id,
              mi->parentid,
              mi->major,
              mi->minor,
              mi->root,
-             mi->mount_point,
-             mi->mount_options,
-             mi->filesystem,
-             mi->mount_source,
-             mi->super_options
+             mi->persistent_id,
+                (mi->mount_point)?mi->mount_point:"",
+                (mi->mount_options)?mi->mount_options:"",
+                (mi->filesystem)?mi->filesystem:"",
+                (mi->mount_source)?mi->mount_source:"",
+                (mi->super_options)?mi->super_options:"",
+                (mi->flags & MOUNTINFO_IS_DUMMY)?" DUMMY":"",
+                (mi->flags & MOUNTINFO_IS_BIND)?" BIND":"",
+                (mi->flags & MOUNTINFO_IS_REMOTE)?" REMOTE":"",
+                (mi->flags & MOUNTINFO_NO_STAT)?" NOSTAT":"",
+                (mi->flags & MOUNTINFO_NO_SIZE)?" NOSIZE":"",
+                (mi->flags & MOUNTINFO_IS_SAME_DEV)?" SAMEDEV":""
         );
+#endif
 */
     }
 
+/*
+    {
+        FILE *fp = setmntent(MOUNTED, "r");
+        if (fp != NULL) {
+            struct mntent mntbuf;
+            struct mntent *mnt;
+            char buf[4096 + 1];
+
+            while ((mnt = getmntent_r(fp, &mntbuf, buf, 4096))) {
+                char *bind = hasmntopt(mnt, "bind");
+                if(bind) {
+                    struct mountinfo *mi;
+                    for(mi = root; mi ; mi = mi->next) {
+                        if(strcmp(mnt->mnt_dir, mi->mount_point) == 0) {
+                            fprintf(stderr, "Mount point '%s' is BIND\n", mi->mount_point);
+                            mi->flags |= MOUNTINFO_IS_BIND;
+                            break;
+                        }
+                    }
+
+#ifdef NETDATA_INTERNAL_CHECKS
+                    if(!mi) {
+                        error("Mount point '%s' not found in /proc/self/mountinfo", mnt->mnt_dir);
+                    }
+#endif
+                }
+            }
+            endmntent(fp);
+        }
+    }
+*/
+
     procfile_close(ff);
     return root;
 }
index c2d9688c19e836e221f7e0f445fc961abbad3b69..d47b6112562f70dca15331a1828d1e5bffa2aba8 100644 (file)
@@ -1,12 +1,22 @@
 #ifndef NETDATA_PROC_SELF_MOUNTINFO_H
 #define NETDATA_PROC_SELF_MOUNTINFO_H 1
 
+#define MOUNTINFO_IS_DUMMY      0x00000001
+#define MOUNTINFO_IS_REMOTE     0x00000002
+#define MOUNTINFO_IS_BIND       0x00000004
+#define MOUNTINFO_IS_SAME_DEV   0x00000008
+#define MOUNTINFO_NO_STAT       0x00000010
+#define MOUNTINFO_NO_SIZE       0x00000020
+
 struct mountinfo {
     long id;                // mount ID: unique identifier of the mount (may be reused after umount(2)).
     long parentid;          // parent ID: ID of parent mount (or of self for the top of the mount tree).
     unsigned long major;    // major:minor: value of st_dev for files on filesystem (see stat(2)).
     unsigned long minor;
 
+    char *persistent_id;    // a calculated persistent id for the mount point
+    uint32_t persistent_id_hash;
+
     char *root;             // root: root of the mount within the filesystem.
     uint32_t root_hash;
 
@@ -27,6 +37,10 @@ struct mountinfo {
 
     char *super_options;    // super options: per-superblock options.
 
+    uint32_t flags;
+
+    dev_t st_dev;           // id of device as given by stat()
+
     struct mountinfo *next;
 };