I'd like to monitor disk IO on my workstation, but snmpd doesn't currently export hw.diskstats as far as I can tell. I found net-snmp supports UCD-DISKIO-MIB and it's not too complex, so I went ahead and coded up an implementation.
It's been a while since I've touched SNMP and/or snmpd, but it seems to work correctly: $ snmpwalk -m UCD-DISKIO-MIB -v1 -c public 127.0.0.1 diskIOTable UCD-DISKIO-MIB::diskIOIndex.1 = INTEGER: 1 UCD-DISKIO-MIB::diskIOIndex.2 = INTEGER: 2 UCD-DISKIO-MIB::diskIOIndex.3 = INTEGER: 3 UCD-DISKIO-MIB::diskIODevice.1 = STRING: sd0 UCD-DISKIO-MIB::diskIODevice.2 = STRING: sd1 UCD-DISKIO-MIB::diskIODevice.3 = STRING: cd0 UCD-DISKIO-MIB::diskIONRead.1 = Counter32: 3584 UCD-DISKIO-MIB::diskIONRead.2 = Counter32: 3128056320 UCD-DISKIO-MIB::diskIONRead.3 = Counter32: 0 UCD-DISKIO-MIB::diskIONWritten.1 = Counter32: 0 UCD-DISKIO-MIB::diskIONWritten.2 = Counter32: 1418602496 UCD-DISKIO-MIB::diskIONWritten.3 = Counter32: 0 UCD-DISKIO-MIB::diskIOReads.1 = Counter32: 7 UCD-DISKIO-MIB::diskIOReads.2 = Counter32: 18550342 UCD-DISKIO-MIB::diskIOReads.3 = Counter32: 0 UCD-DISKIO-MIB::diskIOWrites.1 = Counter32: 0 UCD-DISKIO-MIB::diskIOWrites.2 = Counter32: 19485959 UCD-DISKIO-MIB::diskIOWrites.3 = Counter32: 0 UCD-DISKIO-MIB::diskIONReadX.1 = Counter64: 3584 UCD-DISKIO-MIB::diskIONReadX.2 = Counter64: 539998968320 UCD-DISKIO-MIB::diskIONReadX.3 = Counter64: 0 UCD-DISKIO-MIB::diskIONWrittenX.1 = Counter64: 0 UCD-DISKIO-MIB::diskIONWrittenX.2 = Counter64: 357900888064 UCD-DISKIO-MIB::diskIONWrittenX.3 = Counter64: 0 Test reports from regular SNMP users would be great. ok? Index: mib.h =================================================================== RCS file: /home/mdempsky/anoncvs/cvs/src/usr.sbin/snmpd/mib.h,v retrieving revision 1.25 diff -u -p -r1.25 mib.h --- mib.h 20 Mar 2012 03:01:26 -0000 1.25 +++ mib.h 13 Jun 2012 19:19:23 -0000 @@ -397,6 +397,22 @@ #define MIB_vantronix MIB_enterprises, 26766 #define MIB_openBSD MIB_enterprises, 30155 +/* UCD-DISKIO-MIB */ +#define MIB_ucdExperimental MIB_ucDavis, 13 +#define MIB_ucdDiskIOMIB MIB_ucdExperimental, 15 +#define MIB_diskIOTable MIB_ucdDiskIOMIB, 1 +#define MIB_diskIOEntry MIB_diskIOTable, 1 +#define OIDIDX_diskIO 11 +#define OIDIDX_diskIOEntry 12 +#define MIB_diskIOIndex MIB_diskIOEntry, 1 +#define MIB_diskIODevice MIB_diskIOEntry, 2 +#define MIB_diskIONRead MIB_diskIOEntry, 3 +#define MIB_diskIONWritten MIB_diskIOEntry, 4 +#define MIB_diskIOReads MIB_diskIOEntry, 5 +#define MIB_diskIOWrites MIB_diskIOEntry, 6 +#define MIB_diskIONReadX MIB_diskIOEntry, 12 +#define MIB_diskIONWrittenX MIB_diskIOEntry, 13 + /* OPENBSD-MIB */ #define MIB_pfMIBObjects MIB_openBSD, 1 #define MIB_pfInfo MIB_pfMIBObjects, 1 @@ -892,6 +908,19 @@ { MIBDECL(microSystems) }, \ { MIBDECL(vantronix) }, \ { MIBDECL(openBSD) }, \ + \ + { MIBDECL(ucdExperimental) }, \ + { MIBDECL(ucdDiskIOMIB) }, \ + { MIBDECL(diskIOTable) }, \ + { MIBDECL(diskIOEntry) }, \ + { MIBDECL(diskIOIndex) }, \ + { MIBDECL(diskIODevice) }, \ + { MIBDECL(diskIONRead) }, \ + { MIBDECL(diskIONWritten) }, \ + { MIBDECL(diskIOReads) }, \ + { MIBDECL(diskIOWrites) }, \ + { MIBDECL(diskIONReadX) }, \ + { MIBDECL(diskIONWrittenX) }, \ \ { MIBDECL(pfMIBObjects) }, \ { MIBDECL(pfInfo) }, \ Index: mib.c =================================================================== RCS file: /home/mdempsky/anoncvs/cvs/src/usr.sbin/snmpd/mib.c,v retrieving revision 1.52 diff -u -p -r1.52 mib.c --- mib.c 20 Mar 2012 03:01:26 -0000 1.52 +++ mib.c 13 Jun 2012 19:19:09 -0000 @@ -32,6 +32,7 @@ #include <sys/socket.h> #include <sys/mount.h> #include <sys/ioctl.h> +#include <sys/disk.h> #include <net/if.h> #include <net/if_types.h> @@ -3362,6 +3363,99 @@ mib_ipfroute(struct oid *oid, struct ber } /* + * Defined in UCD-DISKIO-MIB.txt. + */ + +int mib_diskio(struct oid *oid, struct ber_oid *o, struct ber_element **elm); + +static struct oid diskio_mib[] = { + { MIB(ucdDiskIOMIB), OID_MIB }, + { MIB(diskIOIndex), OID_TRD, mib_diskio }, + { MIB(diskIODevice), OID_TRD, mib_diskio }, + { MIB(diskIONRead), OID_TRD, mib_diskio }, + { MIB(diskIONWritten), OID_TRD, mib_diskio }, + { MIB(diskIOReads), OID_TRD, mib_diskio }, + { MIB(diskIOWrites), OID_TRD, mib_diskio }, + { MIB(diskIONReadX), OID_TRD, mib_diskio }, + { MIB(diskIONWrittenX), OID_TRD, mib_diskio }, + { MIBEND } +}; + +int +mib_diskio(struct oid *oid, struct ber_oid *o, struct ber_element **elm) +{ + struct ber_element *ber = *elm; + u_int32_t idx; + int mib[] = { CTL_HW, 0 }; + unsigned int diskcount; + struct diskstats *stats; + size_t len; + + len = sizeof(diskcount); + mib[1] = HW_DISKCOUNT; + if (sysctl(mib, sizeofa(mib), &diskcount, &len, NULL, 0) == -1) + return (-1); + + /* Get and verify the current row index */ + idx = o->bo_id[OIDIDX_diskIOEntry]; + if (idx > diskcount) + return (1); + + /* Tables need to prepend the OID on their own */ + o->bo_id[OIDIDX_diskIOEntry] = idx; + ber = ber_add_oid(ber, o); + + len = diskcount * sizeof(*stats); /* XXX: Overflow? */ + stats = malloc(len); + if (stats == NULL) + return (-1); + mib[1] = HW_DISKSTATS; + if (sysctl(mib, sizeofa(mib), stats, &len, NULL, 0) == -1) { + free(stats); + return (-1); + } + + switch (o->bo_id[OIDIDX_diskIO]) { + case 1: /* diskIOIndex */ + ber = ber_add_integer(ber, idx); + break; + case 2: /* diskIODevice */ + ber = ber_add_string(ber, stats[idx - 1].ds_name); /* XXX: NUL terminated? */ + break; + case 3: /* diskIONRead */ + ber = ber_add_integer(ber, (u_int32_t)stats[idx - 1].ds_rbytes); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); + break; + case 4: /* diskIONWritten */ + ber = ber_add_integer(ber, (u_int32_t)stats[idx - 1].ds_wbytes); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); + break; + case 5: /* diskIOReads */ + ber = ber_add_integer(ber, (u_int32_t)stats[idx - 1].ds_rxfer); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); + break; + case 6: /* diskIOWrites */ + ber = ber_add_integer(ber, (u_int32_t)stats[idx - 1].ds_wxfer); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); + break; + case 12: /* diskIONReadX */ + ber = ber_add_integer(ber, stats[idx - 1].ds_rbytes); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64); + break; + case 13: /* diskIONWrittenX */ + ber = ber_add_integer(ber, stats[idx - 1].ds_wbytes); + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64); + break; + default: + free(stats); + return (-1); + } + + free(stats); + return (0); +} + +/* * Defined in BRIDGE-MIB.txt (rfc1493) * * This MIB is required by some NMS to accept the device because @@ -3451,6 +3545,9 @@ mib_init(void) /* BRIDGE-MIB */ smi_mibtree(bridge_mib); + + /* UCD-DISKIO-MIB */ + smi_mibtree(diskio_mib); /* OPENBSD-MIB */ smi_mibtree(openbsd_mib);