Blame SOURCES/net-snmp-5.7.2-diskio-whitelist.patch

34d7f3
1092308 - backport diskio device filtering
34d7f3
34d7f3
Backported from:
34d7f3
34d7f3
commit 5be210c90870ff6bab193d497d401b92c1d50db9
34d7f3
Author: Jan Safranek <jsafranek@users.sourceforge.net>
34d7f3
Date:   Thu Mar 6 13:26:30 2014 +0100
34d7f3
34d7f3
    CHANGES: snmpd: add new snmpd.conf option 'diskio' to monitor only selected disks.
34d7f3
34d7f3
    On machines with thousands of block devices, parsing /proc/diskstats is really
34d7f3
    slow. The new option enables monitoring of selected devices, saving lot of CPU
34d7f3
    time.
34d7f3
    
34d7f3
diff -up net-snmp-5.7.2/agent/mibgroup/ucd-snmp/diskio.c.test net-snmp-5.7.2/agent/mibgroup/ucd-snmp/diskio.c
34d7f3
--- net-snmp-5.7.2/agent/mibgroup/ucd-snmp/diskio.c.test	2012-10-10 00:28:58.000000000 +0200
34d7f3
+++ net-snmp-5.7.2/agent/mibgroup/ucd-snmp/diskio.c	2015-06-18 15:14:57.164891695 +0200
34d7f3
@@ -27,11 +27,18 @@
34d7f3
 
34d7f3
 #include <math.h>
34d7f3
 
34d7f3
+#if defined (linux)
34d7f3
+/* for stat() */
34d7f3
+#include <ctype.h>
34d7f3
+#include <sys/stat.h>
34d7f3
+#endif
34d7f3
+
34d7f3
 #include <net-snmp/net-snmp-includes.h>
34d7f3
 #include <net-snmp/agent/net-snmp-agent-includes.h>
34d7f3
 
34d7f3
 #include "util_funcs/header_simple_table.h"
34d7f3
 
34d7f3
+#include "struct.h"
34d7f3
 /*
34d7f3
  * include our .h file 
34d7f3
  */
34d7f3
@@ -95,6 +102,66 @@ static int ps_numdisks;			/* number of d
34d7f3
 #if defined (linux)
34d7f3
 #define DISKIO_SAMPLE_INTERVAL 5
34d7f3
 void devla_getstats(unsigned int regno, void * dummy);
34d7f3
+static void diskio_parse_config_disks(const char *token, char *cptr);
34d7f3
+static void diskio_free_config(void);
34d7f3
+static int get_sysfs_stats(void);
34d7f3
+
34d7f3
+struct diskiopart {
34d7f3
+    char            syspath[STRMAX];	/* full stat path */
34d7f3
+    char            name[STRMAX];	/* name as provided */
34d7f3
+    char            shortname[STRMAX];	/* short name for output */
34d7f3
+    int             major;
34d7f3
+    int             minor;
34d7f3
+};
34d7f3
+
34d7f3
+static int             numdisks;
34d7f3
+static int             maxdisks = 0;
34d7f3
+static struct diskiopart *disks;
34d7f3
+
34d7f3
+#define DISK_INCR 2
34d7f3
+
34d7f3
+typedef struct linux_diskio
34d7f3
+{
34d7f3
+    int major;
34d7f3
+    int  minor;
34d7f3
+    unsigned long  blocks;
34d7f3
+    char name[256];
34d7f3
+    unsigned long  rio;
34d7f3
+    unsigned long  rmerge;
34d7f3
+    unsigned long  rsect;
34d7f3
+    unsigned long  ruse;
34d7f3
+    unsigned long  wio;
34d7f3
+    unsigned long  wmerge;
34d7f3
+    unsigned long  wsect;
34d7f3
+    unsigned long  wuse;
34d7f3
+    unsigned long  running;
34d7f3
+    unsigned long  use;
34d7f3
+    unsigned long  aveq;
34d7f3
+} linux_diskio;
34d7f3
+
34d7f3
+/* disk load averages */
34d7f3
+typedef struct linux_diskio_la
34d7f3
+{
34d7f3
+    unsigned long use_prev;
34d7f3
+    double la1, la5, la15;
34d7f3
+} linux_diskio_la;
34d7f3
+
34d7f3
+typedef struct linux_diskio_header
34d7f3
+{
34d7f3
+    linux_diskio* indices;
34d7f3
+    int length;
34d7f3
+    int alloc;
34d7f3
+} linux_diskio_header;
34d7f3
+
34d7f3
+typedef struct linux_diskio_la_header
34d7f3
+{
34d7f3
+    linux_diskio_la * indices;
34d7f3
+    int length;
34d7f3
+} linux_diskio_la_header;
34d7f3
+
34d7f3
+static linux_diskio_header head;
34d7f3
+static linux_diskio_la_header la_head;
34d7f3
+
34d7f3
 #endif /* linux */
34d7f3
 
34d7f3
 #if defined (darwin)
34d7f3
@@ -228,6 +295,8 @@ init_diskio(void)
34d7f3
     devla_getstats(0, NULL);
34d7f3
     /* collect LA data regularly */
34d7f3
     snmp_alarm_register(DISKIO_SAMPLE_INTERVAL, SA_REPEAT, devla_getstats, NULL);
34d7f3
+    snmpd_register_config_handler("diskio", diskio_parse_config_disks,
34d7f3
+        diskio_free_config, "path | device");
34d7f3
 #endif
34d7f3
 
34d7f3
 
34d7f3
@@ -870,49 +939,134 @@ var_diskio(struct variable * vp,
34d7f3
 
34d7f3
 #ifdef linux
34d7f3
 
34d7f3
-#define DISK_INCR 2
34d7f3
-
34d7f3
-typedef struct linux_diskio
34d7f3
+static void
34d7f3
+diskio_free_config()
34d7f3
+ {
34d7f3
+    if (la_head.length) {
34d7f3
+        /* reset any usage stats, we may get different list of devices from config */
34d7f3
+        free(la_head.indices);
34d7f3
+        la_head.length = 0;
34d7f3
+        la_head.indices = NULL;
34d7f3
+    }
34d7f3
+    if (numdisks > 0) {
34d7f3
+        int i;
34d7f3
+        head.length = 0;
34d7f3
+        numdisks = 0;
34d7f3
+        for (i = 0; i < maxdisks; i++) {    /* init/erase disk db */
34d7f3
+            disks[i].syspath[0] = 0;
34d7f3
+            disks[i].name[0] = 0;
34d7f3
+            disks[i].shortname[0] = 0;
34d7f3
+            disks[i].major = -1;
34d7f3
+            disks[i].minor = -1;
34d7f3
+        }
34d7f3
+    }
34d7f3
+}
34d7f3
+static int
34d7f3
+disk_exists(char *path) 
34d7f3
 {
34d7f3
-    int major;
34d7f3
-    int  minor;
34d7f3
-    unsigned long  blocks;
34d7f3
-    char name[256];
34d7f3
-    unsigned long  rio;
34d7f3
-    unsigned long  rmerge;
34d7f3
-    unsigned long  rsect;
34d7f3
-    unsigned long  ruse;
34d7f3
-    unsigned long  wio;
34d7f3
-    unsigned long  wmerge;
34d7f3
-    unsigned long  wsect;
34d7f3
-    unsigned long  wuse;
34d7f3
-    unsigned long  running;
34d7f3
-    unsigned long  use;
34d7f3
-    unsigned long  aveq;
34d7f3
-} linux_diskio;
34d7f3
+    int index;
34d7f3
+    for(index = 0; index < numdisks; index++) {
34d7f3
+        DEBUGMSGTL(("ucd-snmp/disk", "Checking for %s. Found %s at %d\n", path, disks[index].syspath, index));
34d7f3
+        if(strcmp(path, disks[index].syspath) == 0) {
34d7f3
+            return index;
34d7f3
+        }
34d7f3
+    }
34d7f3
+    return -1;
34d7f3
+}
34d7f3
 
34d7f3
-/* disk load averages */
34d7f3
-typedef struct linux_diskio_la
34d7f3
-{
34d7f3
-    unsigned long use_prev;
34d7f3
-    double la1, la5, la15;
34d7f3
-} linux_diskio_la;
34d7f3
+static void
34d7f3
+add_device(char *path, int addNewDisks ) 
34d7f3
+ {
34d7f3
+    int index;
34d7f3
+    char device[STRMAX];
34d7f3
+    char syspath[STRMAX];
34d7f3
+    char *basename;
34d7f3
+    struct stat stbuf;
34d7f3
 
34d7f3
-typedef struct linux_diskio_header
34d7f3
-{
34d7f3
-    linux_diskio* indices;
34d7f3
-    int length;
34d7f3
-    int alloc;
34d7f3
-} linux_diskio_header;
34d7f3
+    if (!path || !strcmp(path, "none")) {
34d7f3
+        DEBUGMSGTL(("ucd-snmp/diskio", "Skipping null path device (%s)\n", path));
34d7f3
+        return;
34d7f3
+    }
34d7f3
+    if (numdisks == maxdisks) {
34d7f3
+        if (maxdisks == 0) {
34d7f3
+            maxdisks = 50;
34d7f3
+            disks = malloc(maxdisks * sizeof(struct diskiopart));
34d7f3
+            if (!disks) {
34d7f3
+                config_perror("malloc failed for new disko allocation.");
34d7f3
+	            netsnmp_config_error("\tignoring:  %s", path);
34d7f3
+                return;
34d7f3
+            }
34d7f3
+            memset(disks, 0, maxdisks * sizeof(struct diskiopart));
34d7f3
+        } else {
34d7f3
+            maxdisks *= 2;
34d7f3
+            disks = realloc(disks, maxdisks * sizeof(struct diskiopart));
34d7f3
+            if (!disks) {
34d7f3
+                config_perror("malloc failed for new disko allocation.");
34d7f3
+	            netsnmp_config_error("\tignoring:  %s", path);
34d7f3
+                return;
34d7f3
+            }
34d7f3
+            memset(disks + maxdisks/2, 0, maxdisks/2 * sizeof(struct diskiopart));
34d7f3
+        }
34d7f3
+    }
34d7f3
 
34d7f3
-typedef struct linux_diskio_la_header
34d7f3
-{
34d7f3
-    linux_diskio_la * indices;   
34d7f3
-    int length;
34d7f3
-} linux_diskio_la_header;
34d7f3
+    /* first find the path for this device */
34d7f3
+    device[0]='\0';
34d7f3
+    if ( *path != '/' ) {
34d7f3
+        strlcpy(device, "/dev/", STRMAX - 1 );
34d7f3
+    }
34d7f3
+    strncat(device, path, STRMAX - 1 );
34d7f3
+
34d7f3
+    /* check for /dev existence */
34d7f3
+    if ( stat(device,&stbuf)!=0 ) { /* ENOENT */
34d7f3
+        config_perror("diskio path does not exist.");
34d7f3
+        netsnmp_config_error("\tignoring:  %s", path);
34d7f3
+        return;
34d7f3
+    }
34d7f3
+    else if ( ! S_ISBLK(stbuf.st_mode) ) { /* ENODEV */
34d7f3
+        config_perror("diskio path is not a device.");
34d7f3
+        netsnmp_config_error("\tignoring:  %s", path);
34d7f3
+        return;
34d7f3
+    }
34d7f3
 
34d7f3
-static linux_diskio_header head;
34d7f3
-static linux_diskio_la_header la_head;
34d7f3
+    /* either came with a slash or we just put one there, so the following always works */
34d7f3
+    basename = strrchr(device, '/' )+1;
34d7f3
+    /* construct a sys path using the device numbers to avoid having to disambiguate the various text forms */
34d7f3
+    snprintf( syspath, STRMAX - 1, "/sys/dev/block/%d:%d/stat", major(stbuf.st_rdev), minor(stbuf.st_rdev) );
34d7f3
+    DEBUGMSGTL(("ucd-snmp/diskio", " monitoring sys path (%s)\n", syspath));
34d7f3
+
34d7f3
+    index = disk_exists(syspath);
34d7f3
+
34d7f3
+    if(index == -1 && addNewDisks){
34d7f3
+        /* The following buffers are cleared above, no need to add '\0' */
34d7f3
+        strlcpy(disks[numdisks].syspath, syspath, sizeof(disks[numdisks].syspath) - 1);
34d7f3
+        strlcpy(disks[numdisks].name, path, sizeof(disks[numdisks].name) - 1);
34d7f3
+        strlcpy(disks[numdisks].shortname, basename, sizeof(disks[numdisks].shortname) - 1);
34d7f3
+        disks[numdisks].major = major(stbuf.st_rdev);
34d7f3
+        disks[numdisks].minor = minor(stbuf.st_rdev);
34d7f3
+        numdisks++;  
34d7f3
+    }
34d7f3
+}
34d7f3
+
34d7f3
+static void 
34d7f3
+diskio_parse_config_disks(const char *token, char *cptr)
34d7f3
+ {
34d7f3
+#if HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS
34d7f3
+    char path[STRMAX];
34d7f3
+
34d7f3
+
34d7f3
+    /*
34d7f3
+     * read disk path (eg, /1 or /usr) 
34d7f3
+     */
34d7f3
+    copy_nword(cptr, path, sizeof(path));
34d7f3
+
34d7f3
+    /* TODO: we may include regular expressions in future */
34d7f3
+    /*
34d7f3
+     * check if the disk already exists, if so then modify its
34d7f3
+     * parameters. if it does not exist then add it
34d7f3
+     */
34d7f3
+    add_device(path, 1);
34d7f3
+#endif /* HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS */
34d7f3
+}
34d7f3
 
34d7f3
 void devla_getstats(unsigned int regno, void * dummy) {
34d7f3
 
34d7f3
@@ -976,6 +1130,47 @@ int is_excluded(const char *name)
34d7f3
     return 0;
34d7f3
 }
34d7f3
 
34d7f3
+static int get_sysfs_stats()
34d7f3
+{
34d7f3
+    int i;
34d7f3
+    char buffer[1024];
34d7f3
+
34d7f3
+    head.length  = 0;
34d7f3
+
34d7f3
+    for(i = 0; i < numdisks; i++) {
34d7f3
+        FILE *f = fopen(disks[i].syspath, "r");
34d7f3
+        if ( f == NULL ) {
34d7f3
+            DEBUGMSGTL(("ucd-snmp/diskio", "Can't open %s, skipping", disks[i].syspath));
34d7f3
+            continue;
34d7f3
+        }
34d7f3
+        if (fgets(buffer, sizeof(buffer), f) == NULL) {
34d7f3
+            DEBUGMSGTL(("ucd-snmp/diskio", "Can't read %s, skipping", disks[i].syspath));
34d7f3
+            fclose(f);
34d7f3
+            continue;
34d7f3
+        }
34d7f3
+
34d7f3
+        linux_diskio* pTemp;
34d7f3
+        if (head.length == head.alloc) {
34d7f3
+            head.alloc += DISK_INCR;
34d7f3
+            head.indices = (linux_diskio *) realloc(head.indices, head.alloc*sizeof(linux_diskio));
34d7f3
+        }
34d7f3
+        pTemp = &head.indices[head.length];
34d7f3
+        pTemp->major = disks[i].major;
34d7f3
+        pTemp->minor = disks[i].minor;
34d7f3
+        strlcpy( pTemp->name, disks[i].shortname, sizeof(pTemp->name) - 1 );
34d7f3
+        if (sscanf (buffer, "%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu\n",
34d7f3
+                &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
34d7f3
+                &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
34d7f3
+                &pTemp->running, &pTemp->use, &pTemp->aveq) != 11)
34d7f3
+            sscanf (buffer, "%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu\n",
34d7f3
+                &pTemp->rio, &pTemp->rsect,
34d7f3
+                &pTemp->wio, &pTemp->wsect);
34d7f3
+        head.length++;
34d7f3
+        fclose(f);
34d7f3
+    }
34d7f3
+    return 0;
34d7f3
+}
34d7f3
+
34d7f3
 static int
34d7f3
 getstats(void)
34d7f3
 {
34d7f3
@@ -995,6 +1189,14 @@ getstats(void)
34d7f3
 
34d7f3
     memset(head.indices, 0, head.alloc*sizeof(linux_diskio));
34d7f3
 
34d7f3
+    if (numdisks>0) {
34d7f3
+        /* 'diskio' configuration is used - go through the whitelist only and
34d7f3
+         * read /sys/dev/block/xxx */
34d7f3
+        cache_time = now;
34d7f3
+        return get_sysfs_stats();
34d7f3
+    }
34d7f3
+    /* 'diskio' configuration is not used - report all devices */
34d7f3
+
34d7f3
     /* Is this a 2.6 kernel? */
34d7f3
     parts = fopen("/proc/diskstats", "r");
34d7f3
     if (parts) {
34d7f3
@@ -1111,13 +1313,22 @@ var_diskio(struct variable * vp,
34d7f3
       long_ret = head.indices[indx].wio & 0xffffffff;
34d7f3
       return (u_char *) & long_ret;
34d7f3
     case DISKIO_LA1:
34d7f3
-      long_ret = la_head.indices[indx].la1;
34d7f3
+      if (la_head.length > indx)
34d7f3
+          long_ret = la_head.indices[indx].la1;
34d7f3
+      else
34d7f3
+          long_ret = 0;
34d7f3
       return (u_char *) & long_ret;
34d7f3
     case DISKIO_LA5:
34d7f3
-      long_ret = la_head.indices[indx].la5;
34d7f3
+      if (la_head.length > indx)
34d7f3
+          long_ret = la_head.indices[indx].la5;
34d7f3
+      else
34d7f3
+          long_ret = 0;
34d7f3
       return (u_char *) & long_ret;
34d7f3
     case DISKIO_LA15:
34d7f3
-      long_ret = la_head.indices[indx].la15;
34d7f3
+      if (la_head.length > indx)
34d7f3
+          long_ret = la_head.indices[indx].la15;
34d7f3
+      else
34d7f3
+          long_ret = 0;
34d7f3
       return (u_char *) & long_ret;
34d7f3
     case DISKIO_NREADX:
34d7f3
       *var_len = sizeof(struct counter64);
34d7f3
diff -up net-snmp-5.7.2/man/snmpd.conf.5.def.test net-snmp-5.7.2/man/snmpd.conf.5.def
34d7f3
--- net-snmp-5.7.2/man/snmpd.conf.5.def.test	2015-06-18 15:13:31.249470179 +0200
34d7f3
+++ net-snmp-5.7.2/man/snmpd.conf.5.def	2015-06-18 15:16:45.481423115 +0200
34d7f3
@@ -715,6 +715,15 @@ e.g. "loop0"
34d7f3
 .IP "diskio_exclude_ram yes"
34d7f3
 Excludes all LInux ramdisk block devices, whose names start with "ram", e.g.
34d7f3
 "ram0"
34d7f3
+.PP
34d7f3
+On Linux systems, it is possible to report only explicitly whitelisted
34d7f3
+devices. It may take significant amount of time to process diskIOTable data
34d7f3
+on systems with tens of thousands of block devices and whitelisting only the
34d7f3
+important ones avoids large CPU consumption.
34d7f3
+.IP "diskio <device>"
34d7f3
+Enables whitelisting of devices and adds the device to the whitelist. Only
34d7f3
+explicitly whitelisted devices will be reported. This option may be used
34d7f3
+multiple times.
34d7f3
 .SS System Load Monitoring
34d7f3
 This requires that the agent was built with support for either the
34d7f3
 \fIucd\-snmp/loadave\fR module or the \fIucd\-snmp/memory\fR module
34d7f3
34d7f3
34d7f3
commit 59f9f3387dab4238114804a0be9e4c15667d868c
34d7f3
Author: Jan Safranek <jsafranek@users.sourceforge.net>
34d7f3
Date:   Fri Jun 19 09:29:06 2015 +0200
34d7f3
34d7f3
    Fixed memory leak on realloc failure.
34d7f3
    
34d7f3
    Found by Coverity.
34d7f3
34d7f3
diff --git a/agent/mibgroup/ucd-snmp/diskio.c b/agent/mibgroup/ucd-snmp/diskio.c
34d7f3
index f04d5c5..58163d8 100644
34d7f3
--- a/agent/mibgroup/ucd-snmp/diskio.c
34d7f3
+++ b/agent/mibgroup/ucd-snmp/diskio.c
34d7f3
@@ -405,13 +405,17 @@ add_device(char *path, int addNewDisks )
34d7f3
             }
34d7f3
             memset(disks, 0, maxdisks * sizeof(struct diskiopart));
34d7f3
         } else {
34d7f3
+            struct diskiopart *newdisks;
34d7f3
             maxdisks *= 2;
34d7f3
-            disks = realloc(disks, maxdisks * sizeof(struct diskiopart));
34d7f3
-            if (!disks) {
34d7f3
+            newdisks = realloc(disks, maxdisks * sizeof(struct diskiopart));
34d7f3
+            if (!newdisks) {
34d7f3
+                free(disks);
34d7f3
+                disks = NULL;
34d7f3
                 config_perror("malloc failed for new disko allocation.");
34d7f3
 	            netsnmp_config_error("\tignoring:  %s", path);
34d7f3
                 return;
34d7f3
             }
34d7f3
+            disks = newdisks;
34d7f3
             memset(disks + maxdisks/2, 0, maxdisks/2 * sizeof(struct diskiopart));
34d7f3
         }
34d7f3
     }