summaryrefslogtreecommitdiff
path: root/agent/mibgroup/ucd-snmp/diskio.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/ucd-snmp/diskio.c')
-rw-r--r--agent/mibgroup/ucd-snmp/diskio.c1466
1 files changed, 1466 insertions, 0 deletions
diff --git a/agent/mibgroup/ucd-snmp/diskio.c b/agent/mibgroup/ucd-snmp/diskio.c
new file mode 100644
index 0000000..4d6d05c
--- /dev/null
+++ b/agent/mibgroup/ucd-snmp/diskio.c
@@ -0,0 +1,1466 @@
+/* Portions of this file are subject to the following copyright(s). See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/*
+ * Portions of this file are copyrighted by:
+ * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+
+#include <net-snmp/net-snmp-config.h>
+
+/*
+ * needed by util_funcs.h
+ */
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <math.h>
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include "util_funcs/header_simple_table.h"
+
+/*
+ * include our .h file
+ */
+#include "diskio.h"
+
+#define CACHE_TIMEOUT 1
+static time_t cache_time = 0;
+
+#ifdef solaris2
+#include <kstat.h>
+
+#define MAX_DISKS 128
+
+static kstat_ctl_t *kc;
+static kstat_t *ksp;
+static kstat_io_t kio;
+static int cache_disknr = -1;
+#endif /* solaris2 */
+
+#if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
+/*
+ * handle disk statistics via libperfstat
+ */
+#ifdef HAVE_SYS_PROTOSW_H
+#include <sys/protosw.h>
+#endif
+#include <libperfstat.h>
+static perfstat_disk_t *ps_disk; /* storage for all disk values */
+static int ps_numdisks; /* number of disks in system, may change while running */
+#endif
+
+#if defined(bsdi3) || defined(bsdi4)
+#include <string.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/diskstats.h>
+#endif /* bsdi */
+
+#if defined (freebsd4) || defined(freebsd5)
+#include <sys/param.h>
+#if __FreeBSD_version >= 500101
+#include <sys/resource.h> /* for CPUSTATES in devstat.h */
+#elif !defined(dragonfly)
+#include <sys/dkstat.h>
+#endif
+#include <devstat.h>
+#include <net-snmp/utilities.h>
+
+#include <math.h>
+/* sampling interval, in seconds */
+#define DISKIO_SAMPLE_INTERVAL 5
+
+#endif /* freebsd */
+
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ #define GETDEVS(x) devstat_getdevs(NULL, (x))
+#else
+ #define GETDEVS(x) getdevs((x))
+#endif
+
+#if defined (linux)
+#define DISKIO_SAMPLE_INTERVAL 5
+void devla_getstats(unsigned int regno, void * dummy);
+#endif /* linux */
+
+#if defined (darwin)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/IOBSD.h>
+
+static mach_port_t masterPort; /* to communicate with I/O Kit */
+#endif /* darwin */
+
+#ifndef solaris2
+static int getstats(void);
+#endif
+
+#if defined (freebsd4) || defined(freebsd5)
+void devla_getstats(unsigned int regno, void *dummy);
+#endif
+
+FILE *file;
+
+#if 0
+static void diskio_free_config(void);
+#endif
+
+ /*********************
+ *
+ * Initialisation & common implementation functions
+ *
+ *********************/
+
+
+/*
+ * this is an optional function called at the time the agent starts up
+ * to do any initilizations you might require. You don't have to
+ * create it, as it is optional.
+ */
+
+/*
+ * IMPORTANT: If you add or remove this function, you *must* re-run
+ * the configure script as it checks for its existance.
+ */
+
+void
+init_diskio(void)
+{
+ /*
+ * Define a 'variable' structure that is a representation of our mib.
+ */
+
+ /*
+ * first, we have to pick the variable type. They are all defined in
+ * the var_struct.h file in the agent subdirectory. I'm picking the
+ * variable2 structure since the longest sub-component of the oid I
+ * want to load is .2.1 and .2.2 so I need at most 2 spaces in the
+ * last entry.
+ */
+
+ struct variable2 diskio_variables[] = {
+ {DISKIO_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {1}},
+ {DISKIO_DEVICE, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {2}},
+ {DISKIO_NREAD, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {3}},
+ {DISKIO_NWRITTEN, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {4}},
+ {DISKIO_READS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {5}},
+ {DISKIO_WRITES, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {6}},
+#if defined(freebsd4) || defined(freebsd5) || defined(linux)
+ {DISKIO_LA1, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {9}},
+ {DISKIO_LA5, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {10}},
+ {DISKIO_LA15, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {11}},
+#endif
+ {DISKIO_NREADX, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {12}},
+ {DISKIO_NWRITTENX, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
+ var_diskio, 1, {13}},
+ };
+
+ /*
+ * Define the OID pointer to the top of the mib tree that we're
+ * registering underneath.
+ */
+ oid diskio_variables_oid[] =
+ { 1, 3, 6, 1, 4, 1, 2021, 13, 15, 1, 1 };
+
+ /*
+ * register ourselves with the agent to handle our mib tree
+ *
+ * This is a macro defined in ../../snmp_vars.h. The arguments are:
+ *
+ * descr: A short description of the mib group being loaded.
+ * var: The variable structure to load.
+ * vartype: The variable structure used to define it (variable2, variable4, ...)
+ * theoid: A *initialized* *exact length* oid pointer.
+ * (sizeof(theoid) *must* return the number of elements!)
+ */
+ REGISTER_MIB("diskio", diskio_variables, variable2,
+ diskio_variables_oid);
+
+#ifdef solaris2
+ kc = kstat_open();
+
+ if (kc == NULL)
+ snmp_log(LOG_ERR, "diskio: Couldn't open kstat\n");
+#endif
+
+#ifdef darwin
+ /*
+ * Get the I/O Kit communication handle.
+ */
+ IOMasterPort(bootstrap_port, &masterPort);
+#endif
+
+#if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
+ /*
+ * initialize values to gather information on first request
+ */
+ ps_numdisks = 0;
+ ps_disk = NULL;
+#endif
+
+#if defined (freebsd4) || defined(freebsd5) || defined(linux)
+ devla_getstats(0, NULL);
+ /* collect LA data regularly */
+ snmp_alarm_register(DISKIO_SAMPLE_INTERVAL, SA_REPEAT, devla_getstats, NULL);
+#endif
+
+
+#ifdef linux
+ char *app = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_APPTYPE);
+ netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_fd",
+ NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_FD);
+ netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_loop",
+ NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_LOOP);
+ netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_ram",
+ NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_RAM);
+
+ /* or possible an exclusion pattern? */
+#endif
+}
+
+#if 0
+/* to do: make sure diskio_free_config() gets invoked upon SIGHUP. */
+static void
+diskio_free_config(void)
+{
+ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_FD, 0);
+ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_LOOP, 0);
+ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_RAM, 0);
+}
+#endif
+
+
+#ifdef solaris2
+int
+get_disk(int disknr)
+{
+ time_t now;
+ int i = 0;
+ kstat_t *tksp;
+
+ now = time(NULL);
+ if (disknr == cache_disknr && cache_time + CACHE_TIMEOUT > now) {
+ return 1;
+ }
+
+ /*
+ * could be optimiced by checking if cache_disknr<=disknr
+ * if so, just reread the data - not going through the whole chain
+ * from kc->kc_chain
+ */
+
+ for (tksp = kc->kc_chain; tksp != NULL; tksp = tksp->ks_next) {
+ if (tksp->ks_type == KSTAT_TYPE_IO
+ && !strcmp(tksp->ks_class, "disk")) {
+ if (i == disknr) {
+ if (kstat_read(kc, tksp, &kio) == -1)
+ snmp_log(LOG_ERR, "diskio: kstat_read failed\n");
+ ksp = tksp;
+ cache_time = now;
+ cache_disknr = disknr;
+ return 1;
+ } else {
+ i++;
+ }
+ }
+ }
+ return 0;
+}
+
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ /*
+ * define any variables we might return as static!
+ */
+ static long long_ret;
+ static struct counter64 c64_ret;
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, MAX_DISKS))
+ return NULL;
+
+
+ if (get_disk(name[*length - 1] - 1) == 0)
+ return NULL;
+
+
+ /*
+ * We can now simply test on vp's magic number, defined in diskio.h
+ */
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) name[*length - 1];
+ return (u_char *) & long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(ksp->ks_name);
+ return (u_char *) ksp->ks_name;
+ case DISKIO_NREAD:
+ long_ret = (uint32_t) kio.nread;
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+ long_ret = (uint32_t) kio.nwritten;
+ return (u_char *) & long_ret;
+ case DISKIO_NREADX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = kio.nread & 0xffffffff;;
+ c64_ret.high = kio.nread >> 32;
+ return (u_char *) & c64_ret;
+ case DISKIO_NWRITTENX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = kio.nwritten & 0xffffffff;;
+ c64_ret.high = kio.nwritten >> 32;
+ return (u_char *) & c64_ret;
+ case DISKIO_READS:
+ long_ret = (uint32_t) kio.reads;
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+ long_ret = (uint32_t) kio.writes;
+ return (u_char *) & long_ret;
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+ /*
+ * if we fall to here, fail by returning NULL
+ */
+ return NULL;
+}
+#endif /* solaris2 */
+
+#if defined(bsdi3) || defined(bsdi4)
+static int ndisk;
+static struct diskstats *dk;
+static char **dkname;
+
+static int
+getstats(void)
+{
+ time_t now;
+ int mib[2];
+ char *t, *tp;
+ size_t size, dkn_size;
+ int i;
+
+ now = time(NULL);
+ if (cache_time + CACHE_TIMEOUT > now) {
+ return 1;
+ }
+ mib[0] = CTL_HW;
+ mib[1] = HW_DISKSTATS;
+ size = 0;
+ if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKSTATS mib");
+ return 0;
+ }
+ if (ndisk != size / sizeof(*dk)) {
+ if (dk)
+ free(dk);
+ if (dkname) {
+ for (i = 0; i < ndisk; i++)
+ if (dkname[i])
+ free(dkname[i]);
+ free(dkname);
+ }
+ ndisk = size / sizeof(*dk);
+ if (ndisk == 0)
+ return 0;
+ dkname = malloc(ndisk * sizeof(char *));
+ mib[0] = CTL_HW;
+ mib[1] = HW_DISKNAMES;
+ if (sysctl(mib, 2, NULL, &dkn_size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKNAMES mib");
+ return 0;
+ }
+ tp = t = malloc(dkn_size);
+ if (sysctl(mib, 2, t, &dkn_size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKNAMES mib");
+ return 0;
+ }
+ for (i = 0; i < ndisk; i++) {
+ dkname[i] = strdup(tp);
+ tp += strlen(tp) + 1;
+ }
+ free(t);
+ dk = malloc(ndisk * sizeof(*dk));
+ }
+ mib[0] = CTL_HW;
+ mib[1] = HW_DISKSTATS;
+ if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
+ perror("Can't get HW_DISKSTATS mib");
+ return 0;
+ }
+ cache_time = now;
+ return 1;
+}
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ static long long_ret;
+ unsigned int indx;
+
+ if (getstats() == 0)
+ return 0;
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, ndisk))
+ return NULL;
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+ if (indx >= ndisk)
+ return NULL;
+
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) indx + 1;
+ return (u_char *) & long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(dkname[indx]);
+ return (u_char *) dkname[indx];
+ case DISKIO_NREAD:
+ long_ret =
+ (signed long) (dk[indx].dk_sectors * dk[indx].dk_secsize);
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+ return NULL; /* Sigh... BSD doesn't keep seperate track */
+ case DISKIO_READS:
+ long_ret = (signed long) dk[indx].dk_xfers;
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+ return NULL; /* Sigh... BSD doesn't keep seperate track */
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+ return NULL;
+}
+#endif /* bsdi */
+
+#ifdef __NetBSD__
+#include <sys/sysctl.h>
+static int ndisk;
+#ifdef HW_IOSTATNAMES
+static int nmib[2] = {CTL_HW, HW_IOSTATNAMES};
+#else
+static int nmib[2] = {CTL_HW, HW_DISKNAMES};
+#endif
+#ifdef HW_DISKSTATS
+#include <sys/disk.h>
+static int dmib[3] = {CTL_HW, HW_DISKSTATS, sizeof(struct disk_sysctl)};
+static struct disk_sysctl *dk;
+#endif
+#ifdef HW_IOSTATS
+#include <sys/iostat.h>
+static int dmib[3] = {CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl)};
+static struct io_sysctl *dk;
+#endif
+static char **dkname;
+
+static int
+getstats(void)
+{
+ time_t now;
+ char *t, *tp;
+ size_t size, dkn_size;
+ int i;
+
+ now = time(NULL);
+ if (cache_time + CACHE_TIMEOUT > now) {
+ return 1;
+ }
+ size = 0;
+ if (sysctl(dmib, 3, NULL, &size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKSTATS/HW_IOSTATS mib");
+ return 0;
+ }
+ if (ndisk != size / dmib[2]) {
+ if (dk)
+ free(dk);
+ if (dkname) {
+ for (i = 0; i < ndisk; i++)
+ if (dkname[i])
+ free(dkname[i]);
+ free(dkname);
+ }
+ ndisk = size / dmib[2];
+ if (ndisk == 0)
+ return 0;
+ dkname = malloc(ndisk * sizeof(char *));
+ dkn_size = 0;
+ if (sysctl(nmib, 2, NULL, &dkn_size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKNAMES mib");
+ return 0;
+ }
+ t = malloc(dkn_size);
+ if (sysctl(nmib, 2, t, &dkn_size, NULL, 0) < 0) {
+ perror("Can't get size of HW_DISKNAMES mib");
+ return 0;
+ }
+ for (i = 0, tp = strtok(t, " "); tp && i < ndisk; i++,
+ tp = strtok(NULL, " ")) {
+ dkname[i] = strdup(tp);
+ }
+ free(t);
+ dk = malloc(ndisk * sizeof(*dk));
+ }
+ if (sysctl(dmib, 3, dk, &size, NULL, 0) < 0) {
+ perror("Can't get HW_DISKSTATS/HW_IOSTATS mib");
+ return 0;
+ }
+ cache_time = now;
+ return 1;
+}
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ static long long_ret;
+ unsigned int indx;
+
+ if (getstats() == 0)
+ return 0;
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, ndisk))
+ return NULL;
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+ if (indx >= ndisk)
+ return NULL;
+
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) indx + 1;
+ return (u_char *) & long_ret;
+
+ case DISKIO_DEVICE:
+ *var_len = strlen(dkname[indx]);
+ return (u_char *) dkname[indx];
+
+ case DISKIO_NREAD:
+#ifdef HW_DISKSTATS
+ long_ret = dk[indx].dk_rbytes;
+#endif
+#ifdef HW_IOSTATS
+ if (dk[indx].type == IOSTAT_DISK)
+ long_ret = dk[indx].rbytes;
+#endif
+ return (u_char *) & long_ret;
+
+ case DISKIO_NWRITTEN:
+#ifdef HW_DISKSTATS
+ long_ret = dk[indx].dk_wbytes;
+#endif
+#ifdef HW_IOSTATS
+ if (dk[indx].type == IOSTAT_DISK)
+ long_ret = dk[indx].wbytes;
+#endif
+ return (u_char *) & long_ret;
+
+ case DISKIO_READS:
+#ifdef HW_DISKSTATS
+ long_ret = dk[indx].dk_rxfer;
+#endif
+#ifdef HW_IOSTATS
+ if (dk[indx].type == IOSTAT_DISK)
+ long_ret = dk[indx].rxfer;
+#endif
+ return (u_char *) & long_ret;
+
+ case DISKIO_WRITES:
+#ifdef HW_DISKSTATS
+ long_ret = dk[indx].dk_wxfer;
+#endif
+#ifdef HW_IOSTATS
+ if (dk[indx].type == IOSTAT_DISK)
+ long_ret = dk[indx].wxfer;
+#endif
+ return (u_char *) & long_ret;
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+ return NULL;
+}
+#endif /* __NetBSD__ */
+
+#if defined(freebsd4) || defined(freebsd5)
+
+/* disk load average patch by Rojer */
+
+struct dev_la {
+#if ( defined(freebsd5) && __FreeBSD_version >= 500107 )
+ struct bintime prev;
+#else
+ struct timeval prev;
+#endif
+ double la1,la5,la15;
+ char name[DEVSTAT_NAME_LEN+5];
+ };
+
+static struct dev_la *devloads = NULL;
+static int ndevs = 0;
+
+#if ! ( defined(freebsd5) && __FreeBSD_version >= 500107 )
+double devla_timeval_diff(struct timeval *t1, struct timeval *t2) {
+
+ double dt1 = (double) t1->tv_sec + (double) t1->tv_usec * 0.000001;
+ double dt2 = (double) t2->tv_sec + (double) t2->tv_usec * 0.000001;
+
+ return dt2-dt1;
+
+ }
+#endif
+
+void devla_getstats(unsigned int regno, void *dummy) {
+
+ static struct statinfo *lastat = NULL;
+ int i;
+ double busy_time, busy_percent;
+ static double expon1, expon5, expon15;
+ char current_name[DEVSTAT_NAME_LEN+5];
+
+ if (lastat == NULL) {
+ lastat = (struct statinfo *) malloc(sizeof(struct statinfo));
+ if (lastat != NULL)
+ lastat->dinfo = (struct devinfo *) calloc(sizeof(struct devinfo), 1);
+ if (lastat == NULL || lastat->dinfo == NULL) {
+ SNMP_FREE(lastat);
+ ERROR_MSG("Memory alloc failure - devla_getstats()\n");
+ return;
+ }
+ }
+
+ if ((GETDEVS(lastat)) == -1) {
+ ERROR_MSG("can't do getdevs()\n");
+ return;
+ }
+
+ if (ndevs != 0) {
+ for (i=0; i < ndevs; i++) {
+ snprintf(current_name, sizeof(current_name), "%s%d",
+ lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number);
+ if (strcmp(current_name, devloads[i].name)) {
+ ndevs = 0;
+ free(devloads);
+ }
+ }
+ }
+
+ if (ndevs == 0) {
+ ndevs = lastat->dinfo->numdevs;
+ devloads = (struct dev_la *) malloc(ndevs * sizeof(struct dev_la));
+ memset(devloads, '\0', ndevs * sizeof(struct dev_la));
+ for (i=0; i < ndevs; i++) {
+ devloads[i].la1 = devloads[i].la5 = devloads[i].la15 = 0;
+ memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev));
+ snprintf(devloads[i].name, sizeof(devloads[i].name), "%s%d",
+ lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number);
+ }
+ expon1 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)60)));
+ expon5 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)300)));
+ expon15 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)900)));
+ }
+
+ for (i=0; i<ndevs; i++) {
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ busy_time = devstat_compute_etime(&lastat->dinfo->devices[i].busy_time, &devloads[i].prev);
+#else
+ busy_time = devla_timeval_diff(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time);
+#endif
+ if ( busy_time < 0 )
+ busy_time = 0; /* Account for possible FP loss of precision near zero */
+ busy_percent = busy_time * 100 / DISKIO_SAMPLE_INTERVAL;
+ devloads[i].la1 = devloads[i].la1 * expon1 + busy_percent * (1 - expon1);
+/* fprintf(stderr, "(%d) %s: update la1=%.2lf%%\n", i, devloads[i].name, expon1); */
+ devloads[i].la5 = devloads[i].la5 * expon5 + busy_percent * (1 - expon5);
+ devloads[i].la15 = devloads[i].la15 * expon15 + busy_percent * (1 - expon15);
+ memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev));
+ }
+
+ }
+
+/* end of disk LA patch */
+
+static int ndisk;
+static struct statinfo *stat;
+FILE *file;
+
+static int
+getstats(void)
+{
+ time_t now;
+ int i;
+
+ now = time(NULL);
+ if (cache_time + CACHE_TIMEOUT > now) {
+ return 0;
+ }
+ if (stat == NULL) {
+ stat = (struct statinfo *) malloc(sizeof(struct statinfo));
+ if (stat != NULL)
+ stat->dinfo = (struct devinfo *) calloc(sizeof(struct devinfo), 1);
+ if (stat == NULL || stat->dinfo == NULL) {
+ SNMP_FREE(stat);
+ ERROR_MSG("Memory alloc failure - getstats\n");
+ return 1;
+ }
+ }
+
+ if (GETDEVS(stat) == -1) {
+ fprintf(stderr, "Can't get devices:%s\n", devstat_errbuf);
+ return 1;
+ }
+ ndisk = stat->dinfo->numdevs;
+ /* Gross hack to include device numbers in the device name array */
+ for (i = 0; i < ndisk; i++) {
+ char *cp = stat->dinfo->devices[i].device_name;
+ int len = strlen(cp);
+ if (len > DEVSTAT_NAME_LEN - 3)
+ len -= 3;
+ cp += len;
+ sprintf(cp, "%d", stat->dinfo->devices[i].unit_number);
+ }
+ cache_time = now;
+ return 0;
+}
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ static long long_ret;
+ static struct counter64 c64_ret;
+ long long longlong_ret;
+ unsigned int indx;
+
+ if (getstats() == 1) {
+ return NULL;
+ }
+
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, ndisk)) {
+ return NULL;
+ }
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+
+ if (indx >= ndisk)
+ return NULL;
+
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) indx + 1;;
+ return (u_char *) & long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(stat->dinfo->devices[indx].device_name);
+ return (u_char *) stat->dinfo->devices[indx].device_name;
+ case DISKIO_NREAD:
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_READ];
+#else
+ long_ret = (signed long) stat->dinfo->devices[indx].bytes_read;
+#endif
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_WRITE];
+#else
+ long_ret = (signed long) stat->dinfo->devices[indx].bytes_written;
+#endif
+ return (u_char *) & long_ret;
+ case DISKIO_NREADX:
+ *var_len = sizeof(struct counter64);
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ longlong_ret = stat->dinfo->devices[indx].bytes[DEVSTAT_READ];
+#else
+ longlong_ret = stat->dinfo->devices[indx].bytes_read;
+#endif
+ c64_ret.low = longlong_ret & 0xffffffff;
+ c64_ret.high = longlong_ret >> 32;
+ return (u_char *) & c64_ret;
+ case DISKIO_NWRITTENX:
+ *var_len = sizeof(struct counter64);
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ longlong_ret = stat->dinfo->devices[indx].bytes[DEVSTAT_WRITE];
+#else
+ longlong_ret = stat->dinfo->devices[indx].bytes_written;
+#endif
+ c64_ret.low = longlong_ret & 0xffffffff;
+ c64_ret.high = longlong_ret >> 32;
+ return (u_char *) & c64_ret;
+ case DISKIO_READS:
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_READ];
+#else
+ long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
+#endif
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+#if defined(freebsd5) && __FreeBSD_version >= 500107
+ long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_WRITE];
+#else
+ long_ret = (signed long) stat->dinfo->devices[indx].num_writes;
+#endif
+ return (u_char *) & long_ret;
+ case DISKIO_LA1:
+ long_ret = devloads[indx].la1;
+ return (u_char *) & long_ret;
+ case DISKIO_LA5:
+ long_ret = devloads[indx].la5;
+ return (u_char *) & long_ret;
+ case DISKIO_LA15:
+ long_ret = devloads[indx].la15;
+ return (u_char *) & long_ret;
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+ return NULL;
+}
+#endif /* freebsd4 */
+
+
+#ifdef linux
+
+#define DISK_INCR 2
+
+typedef struct linux_diskio
+{
+ int major;
+ int minor;
+ unsigned long blocks;
+ char name[256];
+ unsigned long rio;
+ unsigned long rmerge;
+ unsigned long rsect;
+ unsigned long ruse;
+ unsigned long wio;
+ unsigned long wmerge;
+ unsigned long wsect;
+ unsigned long wuse;
+ unsigned long running;
+ unsigned long use;
+ unsigned long aveq;
+} linux_diskio;
+
+/* disk load averages */
+typedef struct linux_diskio_la
+{
+ unsigned long use_prev;
+ double la1, la5, la15;
+} linux_diskio_la;
+
+typedef struct linux_diskio_header
+{
+ linux_diskio* indices;
+ int length;
+ int alloc;
+} linux_diskio_header;
+
+typedef struct linux_diskio_la_header
+{
+ linux_diskio_la * indices;
+ int length;
+} linux_diskio_la_header;
+
+static linux_diskio_header head;
+static linux_diskio_la_header la_head;
+
+void devla_getstats(unsigned int regno, void * dummy) {
+
+ static double expon1, expon5, expon15;
+ double busy_time, busy_percent;
+ int idx;
+
+ if (getstats() == 1) {
+ ERROR_MSG("can't do diskio getstats()\n");
+ return;
+ }
+
+ if (!la_head.length) {
+ la_head.indices = (linux_diskio_la *) malloc(head.length * sizeof(linux_diskio_la));
+ for (idx=0; idx<head.length; idx++) {
+ la_head.indices[idx].la1 = la_head.indices[idx].la5 = la_head.indices[idx].la15 = 0.;
+ la_head.indices[idx].use_prev = head.indices[idx].use;
+ }
+ la_head.length = head.length;
+ expon1 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)60)));
+ expon5 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)300)));
+ expon15 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)900)));
+ }
+ else if (head.length - la_head.length) {
+ la_head.indices = (linux_diskio_la *) realloc(la_head.indices, head.length * sizeof(linux_diskio_la));
+ for (idx=la_head.length; idx<head.length; idx++) {
+ la_head.indices[idx].la1 = la_head.indices[idx].la5 = la_head.indices[idx].la15 = 0.;
+ la_head.indices[idx].use_prev = head.indices[idx].use;
+ }
+ la_head.length = head.length;
+ }
+
+ for (idx=0; idx<head.length; idx++) {
+ busy_time = head.indices[idx].use - la_head.indices[idx].use_prev;
+ busy_percent = busy_time * 100. / ((double) DISKIO_SAMPLE_INTERVAL) / 1000.;
+ la_head.indices[idx].la1 = la_head.indices[idx].la1 * expon1 + busy_percent * (1. - expon1);
+ la_head.indices[idx].la5 = la_head.indices[idx].la5 * expon5 + busy_percent * (1. - expon5);
+ la_head.indices[idx].la15 = la_head.indices[idx].la15 * expon15 + busy_percent * (1. - expon15);
+ /*
+ fprintf(stderr, "(%d) update la1=%f la5=%f la15=%f\n",
+ idx, la_head.indices[idx].la1, la_head.indices[idx].la5, la_head.indices[idx].la15);
+ */
+ la_head.indices[idx].use_prev = head.indices[idx].use;
+ }
+}
+
+int is_excluded(const char *name)
+{
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_FD)
+ && !(strncmp(name, "fd", 2)))
+ return 1;
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_LOOP)
+ && !(strncmp(name, "loop", 4)))
+ return 1;
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_DISKIO_NO_RAM)
+ && !(strncmp(name, "ram", 3)))
+ return 1;
+ return 0;
+}
+
+static int
+getstats(void)
+{
+ FILE* parts;
+ time_t now;
+
+ now = time(NULL);
+ if (cache_time + CACHE_TIMEOUT > now) {
+ return 0;
+ }
+
+ if (!head.indices) {
+ head.alloc = DISK_INCR;
+ head.indices = (linux_diskio *)malloc(head.alloc*sizeof(linux_diskio));
+ }
+ head.length = 0;
+
+ memset(head.indices, 0, head.alloc*sizeof(linux_diskio));
+
+ /* Is this a 2.6 kernel? */
+ parts = fopen("/proc/diskstats", "r");
+ if (parts) {
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), parts)) {
+ linux_diskio* pTemp;
+ if (head.length == head.alloc) {
+ head.alloc += DISK_INCR;
+ head.indices = (linux_diskio *)realloc(head.indices, head.alloc*sizeof(linux_diskio));
+ }
+ pTemp = &head.indices[head.length];
+ sscanf (buffer, "%d %d", &pTemp->major, &pTemp->minor);
+ if (sscanf (buffer, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ &pTemp->major, &pTemp->minor, pTemp->name,
+ &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
+ &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
+ &pTemp->running, &pTemp->use, &pTemp->aveq) != 14)
+ sscanf (buffer, "%d %d %s %lu %lu %lu %lu\n",
+ &pTemp->major, &pTemp->minor, pTemp->name,
+ &pTemp->rio, &pTemp->rsect,
+ &pTemp->wio, &pTemp->wsect);
+ if (!is_excluded(pTemp->name))
+ head.length++;
+ }
+ }
+ else {
+ /* See if a 2.4 kernel */
+ char buffer[1024];
+ int rc;
+ parts = fopen("/proc/partitions", "r");
+ if (!parts) {
+ snmp_log_perror("/proc/partitions");
+ return 1;
+ }
+
+ /*
+ * first few fscanfs are garbage we don't care about. skip it.
+ */
+ fgets(buffer, sizeof(buffer), parts);
+ fgets(buffer, sizeof(buffer), parts);
+
+ while (! feof(parts)) {
+ linux_diskio* pTemp;
+
+ if (head.length == head.alloc) {
+ head.alloc += DISK_INCR;
+ head.indices = (linux_diskio *)realloc(head.indices, head.alloc*sizeof(linux_diskio));
+ }
+ pTemp = &head.indices[head.length];
+
+ rc = fscanf (parts, "%d %d %lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ &pTemp->major, &pTemp->minor, &pTemp->blocks, pTemp->name,
+ &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
+ &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
+ &pTemp->running, &pTemp->use, &pTemp->aveq);
+ if (rc != 15) {
+ snmp_log(LOG_ERR, "diskio.c: cannot find statistics in /proc/partitions\n");
+ fclose(parts);
+ return 1;
+ }
+ if (!is_excluded(pTemp->name))
+ head.length++;
+ }
+ }
+
+ fclose(parts);
+ cache_time = now;
+ return 0;
+}
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact,
+ size_t * var_len,
+ WriteMethod ** write_method)
+{
+ unsigned int indx;
+ static unsigned long long_ret;
+ static struct counter64 c64_ret;
+
+ if (getstats() == 1) {
+ return NULL;
+ }
+
+ if (header_simple_table(vp, name, length, exact, var_len, write_method, head.length))
+ {
+ return NULL;
+ }
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+
+ if (indx >= head.length)
+ return NULL;
+
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = indx+1;
+ return (u_char *) &long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(head.indices[indx].name);
+ return (u_char *) head.indices[indx].name;
+ case DISKIO_NREAD:
+ long_ret = (head.indices[indx].rsect*512) & 0xffffffff;
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+ long_ret = (head.indices[indx].wsect*512) & 0xffffffff;
+ return (u_char *) & long_ret;
+ case DISKIO_READS:
+ long_ret = head.indices[indx].rio & 0xffffffff;
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+ long_ret = head.indices[indx].wio & 0xffffffff;
+ return (u_char *) & long_ret;
+ case DISKIO_LA1:
+ long_ret = la_head.indices[indx].la1;
+ return (u_char *) & long_ret;
+ case DISKIO_LA5:
+ long_ret = la_head.indices[indx].la5;
+ return (u_char *) & long_ret;
+ case DISKIO_LA15:
+ long_ret = la_head.indices[indx].la15;
+ return (u_char *) & long_ret;
+ case DISKIO_NREADX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = head.indices[indx].rsect * 512 & 0xffffffff;
+ c64_ret.high = head.indices[indx].rsect >> (32 - 9);
+ return (u_char *) & c64_ret;
+ case DISKIO_NWRITTENX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = head.indices[indx].wsect * 512 & 0xffffffff;
+ c64_ret.high = head.indices[indx].wsect >> (32 - 9);
+ return (u_char *) & c64_ret;
+ default:
+ snmp_log(LOG_ERR, "don't know how to handle %d request\n", vp->magic);
+ }
+ return NULL;
+}
+#endif /* linux */
+
+#if defined(darwin)
+
+#define MAXDRIVES 16 /* most drives we will record */
+#define MAXDRIVENAME 31 /* largest drive name we allow */
+
+#define kIDXBytesRead 0 /* used as index into the stats array in a drivestats struct */
+#define kIDXBytesWritten 1
+#define kIDXNumReads 2
+#define kIDXNumWrites 3
+#define kIDXLast 3
+
+struct drivestats {
+ char name[MAXDRIVENAME + 1];
+ long bsd_unit_number;
+ long stats[kIDXLast+1];
+};
+
+static struct drivestats drivestat[MAXDRIVES];
+
+static mach_port_t masterPort; /* to communicate with I/O Kit */
+
+static int num_drives; /* number of drives detected */
+
+static int
+collect_drive_stats(io_registry_entry_t driver, long *stats)
+{
+ CFNumberRef number;
+ CFDictionaryRef properties;
+ CFDictionaryRef statistics;
+ long value;
+ kern_return_t status;
+ int i;
+
+
+ /*
+ * If the drive goes away, we may not get any properties
+ * for it. So take some defaults. Nb: use memset ??
+ */
+ for (i = 0; i < kIDXLast; i++) {
+ stats[i] = 0;
+ }
+
+ /* retrieve the properties */
+ status = IORegistryEntryCreateCFProperties(driver, (CFMutableDictionaryRef *)&properties,
+ kCFAllocatorDefault, kNilOptions);
+ if (status != KERN_SUCCESS) {
+ snmp_log(LOG_ERR, "diskio: device has no properties\n");
+/* fprintf(stderr, "device has no properties\n"); */
+ return (1);
+ }
+
+ /* retrieve statistics from properties */
+ statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBlockStorageDriverStatisticsKey));
+ if (statistics) {
+
+ /* Now hand me the crystals. */
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt32Type, &value);
+ stats[kIDXBytesRead] = value;
+ }
+
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt32Type, &value);
+ stats[kIDXBytesWritten] = value;
+ }
+
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt32Type, &value);
+ stats[kIDXNumReads] = value;
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt32Type, &value);
+ stats[kIDXNumWrites] = value;
+ }
+ }
+ /* we're done with the properties, release them */
+ CFRelease(properties);
+ return (0);
+}
+
+/*
+ * Check whether an IORegistryEntry refers to a valid
+ * I/O device, and if so, collect the information.
+ */
+static int
+handle_drive(io_registry_entry_t drive, struct drivestats * dstat)
+{
+ io_registry_entry_t parent;
+ CFMutableDictionaryRef properties;
+ CFStringRef name;
+ CFNumberRef number;
+ kern_return_t status;
+
+ /* get drive's parent */
+ status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
+ if (status != KERN_SUCCESS) {
+ snmp_log(LOG_ERR, "diskio: device has no parent\n");
+/* fprintf(stderr, "device has no parent\n"); */
+ return(1);
+ }
+
+ if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
+
+ /* get drive properties */
+ status = IORegistryEntryCreateCFProperties(drive, &properties,
+ kCFAllocatorDefault, kNilOptions);
+ if (status != KERN_SUCCESS) {
+ snmp_log(LOG_ERR, "diskio: device has no properties\n");
+/* fprintf(stderr, "device has no properties\n"); */
+ return(1);
+ }
+
+ /* get BSD name and unitnumber from properties */
+ name = (CFStringRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBSDNameKey));
+ number = (CFNumberRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBSDUnitKey));
+
+ /* Collect stats and if succesful store them with the name and unitnumber */
+ if (name && number && !collect_drive_stats(parent, dstat->stats)) {
+
+ CFStringGetCString(name, dstat->name, MAXDRIVENAME, CFStringGetSystemEncoding());
+ CFNumberGetValue(number, kCFNumberSInt32Type, &dstat->bsd_unit_number);
+ num_drives++;
+ }
+
+ /* clean up, return success */
+ CFRelease(properties);
+ return(0);
+ }
+
+ /* failed, don't keep parent */
+ IOObjectRelease(parent);
+ return(1);
+}
+
+static int
+getstats(void)
+{
+ time_t now;
+ io_iterator_t drivelist;
+ io_registry_entry_t drive;
+ CFMutableDictionaryRef match;
+ kern_return_t status;
+
+ now = time(NULL); /* register current time and check wether cache can be used */
+ if (cache_time + CACHE_TIMEOUT > now) {
+ return 0;
+ }
+
+ /* Retrieve a list of drives. */
+ match = IOServiceMatching("IOMedia");
+ CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
+ status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
+ if (status != KERN_SUCCESS) {
+ snmp_log(LOG_ERR, "diskio: couldn't match whole IOMedia devices\n");
+/* fprintf(stderr,"Couldn't match whole IOMedia devices\n"); */
+ return(1);
+ }
+
+ num_drives = 0; /* NB: Incremented by handle_drive */
+ while ((drive = IOIteratorNext(drivelist)) && (num_drives < MAXDRIVES)) {
+ handle_drive(drive, &drivestat[num_drives]);
+ IOObjectRelease(drive);
+ }
+ IOObjectRelease(drivelist);
+
+ cache_time = now;
+ return (0);
+}
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ static long long_ret;
+ unsigned int indx;
+
+ if (getstats() == 1) {
+ return NULL;
+ }
+
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, num_drives)) {
+ return NULL;
+ }
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+
+ if (indx >= num_drives)
+ return NULL;
+
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) drivestat[indx].bsd_unit_number;
+ return (u_char *) & long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(drivestat[indx].name);
+ return (u_char *) drivestat[indx].name;
+ case DISKIO_NREAD:
+ long_ret = (signed long) drivestat[indx].stats[kIDXBytesRead];
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+ long_ret = (signed long) drivestat[indx].stats[kIDXBytesWritten];
+ return (u_char *) & long_ret;
+ case DISKIO_READS:
+ long_ret = (signed long) drivestat[indx].stats[kIDXNumReads];
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+ long_ret = (signed long) drivestat[indx].stats[kIDXNumWrites];
+ return (u_char *) & long_ret;
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+ return NULL;
+}
+#endif /* darwin */
+
+
+#if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
+/*
+ * collect statistics for all disks
+ */
+int
+collect_disks(void)
+{
+ time_t now;
+ int i;
+ perfstat_id_t first;
+
+ /* cache valid? if yes, just return */
+ now = time(NULL);
+ if (ps_disk != NULL && cache_time + CACHE_TIMEOUT > now) {
+ return 0;
+ }
+
+ /* get number of disks we have */
+ i = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
+ if(i <= 0) return 1;
+
+ /* if number of disks differs or structures are uninitialized, init them */
+ if(i != ps_numdisks || ps_disk == NULL) {
+ if(ps_disk != NULL) free(ps_disk);
+ ps_numdisks = i;
+ ps_disk = malloc(sizeof(perfstat_disk_t) * ps_numdisks);
+ if(ps_disk == NULL) return 1;
+ }
+
+ /* gather statistics about all disks we have */
+ strcpy(first.name, "");
+ i = perfstat_disk(&first, ps_disk, sizeof(perfstat_disk_t), ps_numdisks);
+ if(i != ps_numdisks) return 1;
+
+ cache_time = now;
+ return 0;
+}
+
+
+u_char *
+var_diskio(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ static long long_ret;
+ static struct counter64 c64_ret;
+ unsigned int indx;
+
+ /* get disk statistics */
+ if (collect_disks())
+ return NULL;
+
+ if (header_simple_table
+ (vp, name, length, exact, var_len, write_method, ps_numdisks))
+ return NULL;
+
+ indx = (unsigned int) (name[*length - 1] - 1);
+ if (indx >= ps_numdisks)
+ return NULL;
+
+ /* deliver requested data on requested disk */
+ switch (vp->magic) {
+ case DISKIO_INDEX:
+ long_ret = (long) indx;
+ return (u_char *) & long_ret;
+ case DISKIO_DEVICE:
+ *var_len = strlen(ps_disk[indx].name);
+ return (u_char *) ps_disk[indx].name;
+ case DISKIO_NREAD:
+ long_ret = (signed long) ps_disk[indx].rblks * ps_disk[indx].bsize;
+ return (u_char *) & long_ret;
+ case DISKIO_NWRITTEN:
+ long_ret = (signed long) ps_disk[indx].wblks * ps_disk[indx].bsize;
+ return (u_char *) & long_ret;
+ case DISKIO_READS:
+ long_ret = (signed long) ps_disk[indx].xfers;
+ return (u_char *) & long_ret;
+ case DISKIO_WRITES:
+ long_ret = (signed long) 0; /* AIX has just one value for read/write transfers */
+ return (u_char *) & long_ret;
+ case DISKIO_NREADX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = (ps_disk[indx].rblks * ps_disk[indx].bsize) & 0xffffffff;;
+ c64_ret.high = (ps_disk[indx].rblks * ps_disk[indx].bsize) >> 32;
+ return (u_char *) & c64_ret;
+ case DISKIO_NWRITTENX:
+ *var_len = sizeof(struct counter64);
+ c64_ret.low = (ps_disk[indx].wblks * ps_disk[indx].bsize) & 0xffffffff;;
+ c64_ret.high = (ps_disk[indx].wblks * ps_disk[indx].bsize) >> 32;
+ return (u_char *) & c64_ret;
+
+ default:
+ ERROR_MSG("diskio.c: don't know how to handle this request.");
+ }
+
+ /* return NULL in case of error */
+ return NULL;
+}
+#endif /* aix 4/5 */