net-snmp/agent/mibgroup/ucd-snmp/diskio.c

1903 lines
56 KiB
C
Raw Normal View History

2022-06-27 15:01:12 +08:00
/* 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 <EFBFBD> 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>
#if defined (linux)
/* for stat() */
#include <ctype.h>
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h> /* major() */
#endif
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/agent_callbacks.h>
#include "util_funcs/header_simple_table.h"
#include "struct.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) || defined(openbsd4)
#include <string.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#ifdef openbsd4
#include <sys/disk.h>
#else
#include <sys/diskstats.h>
#endif
#endif /* bsdi */
#if defined(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
#include <sys/param.h>
#if HAVE_DEVSTAT_GETDEVS
#include <sys/resource.h> /* for CPUSTATES in devstat.h */
#elif HAVE_SYS_DKSTAT_H
#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 HAVE_DEVSTAT_GETDEVS
#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);
static void diskio_parse_config_disks(const char *token, char *cptr);
static int diskio_pre_update_config(int, int, void *, void *);
static void diskio_free_config(void);
#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;
#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 */
#if !defined(solaris2) && !(defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7))
static int getstats(void);
#endif
#if defined (HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
void devla_getstats(unsigned int regno, void *dummy);
#endif
#ifdef linux
struct diskiopart {
char syspath[STRMAX]; /* full stat path */
char name[STRMAX]; /* name as provided */
char shortname[STRMAX]; /* short name for output */
int major;
int minor;
};
static int numdisks;
static int maxdisks = 0;
static struct diskiopart *disks;
#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(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS) || 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}},
{DISKIO_BUSYTIME, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
var_diskio, 1, {14}},
};
/*
* 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 (HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS) || 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);
snmpd_register_config_handler("diskio", diskio_parse_config_disks,
diskio_free_config, "path | device");
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
diskio_pre_update_config, NULL);
#endif
}
#ifdef linux
/* to do: make sure diskio_free_config() gets invoked upon SIGHUP. */
static int
diskio_pre_update_config(int major, int minor, void *serverarg, void *clientarg)
{
diskio_free_config();
return 0;
}
static void
diskio_free_config(void)
{
int i;
DEBUGMSGTL(("diskio", "free config %d\n",
netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_DISKIO_NO_RAM)));
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);
if (la_head.length) {
/* reset any usage stats, we may get different list of devices from config */
free(la_head.indices);
la_head.length = 0;
la_head.indices = NULL;
}
if (numdisks > 0) {
head.length = 0;
numdisks = 0;
for (i = 0; i < maxdisks; i++) { /* init/erase disk db */
disks[i].syspath[0] = 0;
disks[i].name[0] = 0;
disks[i].shortname[0] = 0;
disks[i].major = -1;
disks[i].minor = -1;
}
}
}
static int
disk_exists(char *path)
{
int index;
for(index = 0; index < numdisks; index++) {
DEBUGMSGTL(("ucd-snmp/disk", "Checking for %s. Found %s at %d\n", path, disks[index].syspath, index));
if(strcmp(path, disks[index].syspath) == 0) {
return index;
}
}
return -1;
}
static void
add_device(char *path, int addNewDisks )
{
int index;
char device[STRMAX];
char syspath[STRMAX];
char *basename;
struct stat stbuf;
if (!path || !strcmp(path, "none")) {
DEBUGMSGTL(("ucd-snmp/diskio", "Skipping null path device (%s)\n", path));
return;
}
if (numdisks == maxdisks) {
if (maxdisks == 0) {
maxdisks = 50;
disks = malloc(maxdisks * sizeof(struct diskiopart));
if (!disks) {
config_perror("malloc failed for new disko allocation.");
netsnmp_config_error("\tignoring: %s", path);
return;
}
memset(disks, 0, maxdisks * sizeof(struct diskiopart));
} else {
struct diskiopart *newdisks;
maxdisks *= 2;
newdisks = realloc(disks, maxdisks * sizeof(struct diskiopart));
if (!newdisks) {
free(disks);
disks = NULL;
config_perror("malloc failed for new disko allocation.");
netsnmp_config_error("\tignoring: %s", path);
return;
}
disks = newdisks;
memset(disks + maxdisks/2, 0, maxdisks/2 * sizeof(struct diskiopart));
}
}
/* first find the path for this device */
device[0]='\0';
if ( *path != '/' ) {
strlcpy(device, "/dev/", STRMAX - 1 );
}
strncat(device, path, STRMAX - 1 );
/* check for /dev existence */
if ( stat(device,&stbuf)!=0 ) { /* ENOENT */
config_perror("diskio path does not exist.");
netsnmp_config_error("\tignoring: %s", path);
return;
}
else if ( ! S_ISBLK(stbuf.st_mode) ) { /* ENODEV */
config_perror("diskio path is not a device.");
netsnmp_config_error("\tignoring: %s", path);
return;
}
/* either came with a slash or we just put one there, so the following always works */
basename = strrchr(device, '/' )+1;
/* construct a sys path using the device numbers to avoid having to disambiguate the various text forms */
snprintf( syspath, STRMAX - 1, "/sys/dev/block/%d:%d/stat", major(stbuf.st_rdev), minor(stbuf.st_rdev) );
DEBUGMSGTL(("ucd-snmp/diskio", " monitoring sys path (%s)\n", syspath));
index = disk_exists(syspath);
if(index == -1 && addNewDisks){
/* The following buffers are cleared above, no need to add '\0' */
strlcpy(disks[numdisks].syspath, syspath, sizeof(disks[numdisks].syspath) - 1);
strlcpy(disks[numdisks].name, path, sizeof(disks[numdisks].name) - 1);
strlcpy(disks[numdisks].shortname, basename, sizeof(disks[numdisks].shortname) - 1);
disks[numdisks].major = major(stbuf.st_rdev);
disks[numdisks].minor = minor(stbuf.st_rdev);
numdisks++;
}
}
static void
diskio_parse_config_disks(const char *token, char *cptr)
{
#if HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS
char path[STRMAX];
/*
* read disk path (eg, /1 or /usr)
*/
copy_nword(cptr, path, sizeof(path));
/* TODO: we may include regular expressions in future */
/*
* check if the disk already exists, if so then modify its
* parameters. if it does not exist then add it
*/
add_device(path, 1);
#endif /* HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS */
}
#endif /* linux */
#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 */
#if defined(openbsd4)
static int ndisk;
static struct diskstats *dk;
static char **dkname;
static int
getstats(void)
{
time_t now;
int mib[2];
char *t, *tp,*te;
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;
}
te = 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++) {
while (te-t < dkn_size && *te != ',') te++;
*te++ = '\0';
dkname[i] = strdup(tp);
tp = te;
}
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;
static long long longlong_ret;
static struct counter64 c64_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 = (unsigned long) (dk[indx].ds_rbytes) & 0xffffffff;
return (u_char *) & long_ret;
case DISKIO_NWRITTEN:
long_ret = (unsigned long) (dk[indx].ds_wbytes) & 0xffffffff;
return (u_char *) & long_ret;
case DISKIO_READS:
long_ret = (unsigned long) dk[indx].ds_rxfer & 0xffffffff;
return (u_char *) & long_ret;
case DISKIO_WRITES:
long_ret = (unsigned long) dk[indx].ds_wxfer & 0xffffffff;
return (u_char *) & long_ret;
case DISKIO_NREADX:
*var_len = sizeof(struct counter64);
c64_ret.low = dk[indx].ds_rbytes & 0xffffffff;
c64_ret.high = dk[indx].ds_rbytes >> 32;
return (u_char *) & c64_ret;
case DISKIO_NWRITTENX:
*var_len = sizeof(struct counter64);
c64_ret.low = dk[indx].ds_rbytes & 0xffffffff;
c64_ret.high = dk[indx].ds_rbytes >> 32;
return (u_char *) & c64_ret;
case DISKIO_BUSYTIME:
*var_len = sizeof(struct counter64);
longlong_ret = dk[indx].ds_time.tv_sec*1000000 + dk[indx].ds_time.tv_usec;
c64_ret.low = longlong_ret & 0xffffffff;
c64_ret.high = longlong_ret >> 32;
return (u_char *) &c64_ret;
default:
ERROR_MSG("diskio.c: don't know how to handle this request.");
}
return NULL;
}
#endif /* openbsd */
#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;
static long long longlong_ret;
static struct counter64 c64_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_NREADX:
*var_len = sizeof(struct counter64);
longlong_ret = dk[indx].rbytes;
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);
longlong_ret = dk[indx].wbytes;
c64_ret.low = longlong_ret & 0xffffffff;
c64_ret.high = longlong_ret >> 32;
return (u_char *) & c64_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;
case DISKIO_BUSYTIME:
#ifdef HW_IOSTATS
*var_len = sizeof(struct counter64);
if (dk[indx].type == IOSTAT_DISK) {
longlong_ret = dk[indx].time_sec*1000 + dk[indx].time_usec/1000;
c64_ret.low = longlong_ret & 0xffffffff;
c64_ret.high = longlong_ret >> 32;
return (u_char *) & c64_ret;
}
else
return NULL;
#else
return NULL;
#endif
default:
ERROR_MSG("diskio.c: don't know how to handle this request.");
}
return NULL;
}
#endif /* __NetBSD__ */
#if defined(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
/* disk load average patch by Rojer */
struct dev_la {
#if HAVE_DEVSTAT_GETDEVS
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 ! HAVE_DEVSTAT_GETDEVS
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 HAVE_DEVSTAT_GETDEVS
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 HAVE_DEVSTAT_GETDEVS
long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_READ] & 0xFFFFFFFF;
#else
long_ret = (signed long) stat->dinfo->devices[indx].bytes_read;
#endif
return (u_char *) & long_ret;
case DISKIO_NWRITTEN:
#if HAVE_DEVSTAT_GETDEVS
long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_WRITE] & 0xFFFFFFFF;
#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 HAVE_DEVSTAT_GETDEVS
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 HAVE_DEVSTAT_GETDEVS
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 HAVE_DEVSTAT_GETDEVS
long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_READ] & 0xFFFFFFFF;
#else
long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
#endif
return (u_char *) & long_ret;
case DISKIO_WRITES:
#if HAVE_DEVSTAT_GETDEVS
long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_WRITE] & 0xFFFFFFFF;
#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
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 get_sysfs_stats(void)
{
int i;
char buffer[1024];
head.length = 0;
for(i = 0; i < numdisks; i++) {
FILE *f = fopen(disks[i].syspath, "r");
if ( f == NULL ) {
DEBUGMSGTL(("ucd-snmp/diskio", "Can't open %s, skipping", disks[i].syspath));
continue;
}
if (fgets(buffer, sizeof(buffer), f) == NULL) {
DEBUGMSGTL(("ucd-snmp/diskio", "Can't read %s, skipping", disks[i].syspath));
fclose(f);
continue;
}
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];
pTemp->major = disks[i].major;
pTemp->minor = disks[i].minor;
strlcpy( pTemp->name, disks[i].shortname, sizeof(pTemp->name) - 1 );
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",
&pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
&pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
&pTemp->running, &pTemp->use, &pTemp->aveq) != 11)
sscanf (buffer, "%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu\n",
&pTemp->rio, &pTemp->rsect,
&pTemp->wio, &pTemp->wsect);
head.length++;
fclose(f);
}
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));
if (numdisks>0) {
/* 'diskio' configuration is used - go through the whitelist only and
* read /sys/dev/block/xxx */
cache_time = now;
return get_sysfs_stats();
}
/* 'diskio' configuration is not used - report all devices */
/* 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 %255s %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:
if (la_head.length > indx)
long_ret = la_head.indices[indx].la1;
else
long_ret = 0; /* we don't have the load yet */
return (u_char *) & long_ret;
case DISKIO_LA5:
if (la_head.length > indx)
long_ret = la_head.indices[indx].la5;
else
long_ret = 0; /* we don't have the load yet */
return (u_char *) & long_ret;
case DISKIO_LA15:
if (la_head.length > indx)
long_ret = la_head.indices[indx].la15;
else
long_ret = 0;
return (u_char *) & long_ret;
case DISKIO_BUSYTIME:
*var_len = sizeof(struct counter64);
c64_ret.low = head.indices[indx].use*1000 & 0xffffffff;
c64_ret.high = head.indices[indx].use*1000 >> 32;
return (u_char *) & c64_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 kIDXBytesReadXhi 4
#define kIDXBytesReadXlo 5
#define kIDXBytesWrittenXhi 6
#define kIDXBytesWrittenXlo 7
#define kIDXLast 7
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;
SInt64 value64;
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;
}
/* grab the 64 bit versions of the bytes read */
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value64);
stats[kIDXBytesReadXhi] = (long)(value64 >> 32);
stats[kIDXBytesReadXlo] = (long)(value64 & 0xffffffff);
}
/* grab the 64 bit versions of the bytes written */
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value64);
stats[kIDXBytesWrittenXhi] = (long)(value64 >> 32);
stats[kIDXBytesWrittenXlo] = (long)(value64 & 0xffffffff);
}
}
/* 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;
static struct counter64 c64_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;
case DISKIO_NREADX:
*var_len = 8;
c64_ret.low = (signed long) drivestat[indx].stats[kIDXBytesReadXlo];
c64_ret.high = (signed long) drivestat[indx].stats[kIDXBytesReadXhi];
return (u_char *) & c64_ret;
case DISKIO_NWRITTENX:
*var_len = 8;
c64_ret.low = (signed long) drivestat[indx].stats[kIDXBytesWrittenXlo];
c64_ret.high = (signed long) drivestat[indx].stats[kIDXBytesWrittenXhi];
return (u_char *) & c64_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 */