1
/* Portions of this file are subject to the following copyright(s). See
2
* the Net-SNMP's COPYING file for more details and other copyrights
6
* Portions of this file are copyrighted by:
7
* Copyright ļæ½ 2003 Sun Microsystems, Inc. All rights reserved.
8
* Use is subject to license terms specified in the COPYING file
9
* distributed with the Net-SNMP package.
12
#include <net-snmp/net-snmp-config.h>
15
* needed by util_funcs.h
17
#if TIME_WITH_SYS_TIME
18
# include <sys/time.h>
22
# include <sys/time.h>
28
#include <net-snmp/net-snmp-includes.h>
29
#include <net-snmp/agent/net-snmp-agent-includes.h>
32
* header_generic() comes from here
34
#include "util_funcs.h"
41
#define CACHE_TIMEOUT 10
42
static time_t cache_time = 0;
49
static kstat_ctl_t *kc;
51
static kstat_io_t kio;
52
static int cache_disknr = -1;
55
#if defined(aix4) || defined(aix5)
57
* handle disk statistics via libperfstat
59
#include <libperfstat.h>
60
static perfstat_disk_t *ps_disk; /* storage for all disk values */
61
static int ps_numdisks; /* number of disks in system, may change while running */
64
#if defined(bsdi3) || defined(bsdi4)
66
#include <sys/param.h>
67
#include <sys/sysctl.h>
68
#include <sys/diskstats.h>
71
#if defined (freebsd4) || defined(freebsd5)
72
#include <sys/dkstat.h>
82
#include <CoreFoundation/CoreFoundation.h>
83
#include <IOKit/IOKitLib.h>
84
#include <IOKit/storage/IOBlockStorageDriver.h>
85
#include <IOKit/storage/IOMedia.h>
86
#include <IOKit/IOBSD.h>
88
static mach_port_t masterPort; /* to communicate with I/O Kit */
92
void diskio_parse_config(const char *, char *);
95
/*********************
97
* Initialisation & common implementation functions
99
*********************/
103
* this is an optional function called at the time the agent starts up
104
* to do any initilizations you might require. You don't have to
105
* create it, as it is optional.
109
* IMPORTANT: If you add or remove this function, you *must* re-run
110
* the configure script as it checks for its existance.
117
* Define a 'variable' structure that is a representation of our mib.
121
* first, we have to pick the variable type. They are all defined in
122
* the var_struct.h file in the agent subdirectory. I'm picking the
123
* variable2 structure since the longest sub-component of the oid I
124
* want to load is .2.1 and .2.2 so I need at most 2 spaces in the
128
struct variable2 diskio_variables[] = {
129
{DISKIO_INDEX, ASN_INTEGER, RONLY, var_diskio, 1, {1}},
130
{DISKIO_DEVICE, ASN_OCTET_STR, RONLY, var_diskio, 1, {2}},
131
{DISKIO_NREAD, ASN_COUNTER, RONLY, var_diskio, 1, {3}},
132
{DISKIO_NWRITTEN, ASN_COUNTER, RONLY, var_diskio, 1, {4}},
133
{DISKIO_READS, ASN_COUNTER, RONLY, var_diskio, 1, {5}},
134
{DISKIO_WRITES, ASN_COUNTER, RONLY, var_diskio, 1, {6}},
138
* Define the OID pointer to the top of the mib tree that we're
139
* registering underneath.
141
oid diskio_variables_oid[] =
142
{ 1, 3, 6, 1, 4, 1, 2021, 13, 15, 1, 1 };
145
* register ourselves with the agent to handle our mib tree
147
* This is a macro defined in ../../snmp_vars.h. The arguments are:
149
* descr: A short description of the mib group being loaded.
150
* var: The variable structure to load.
151
* vartype: The variable structure used to define it (variable2, variable4, ...)
152
* theoid: A *initialized* *exact length* oid pointer.
153
* (sizeof(theoid) *must* return the number of elements!)
155
REGISTER_MIB("diskio", diskio_variables, variable2,
156
diskio_variables_oid);
159
* Added to parse snmpd.conf - abby
161
snmpd_register_config_handler("diskio", diskio_parse_config,
162
NULL, "diskio [device-type]");
168
snmp_log(LOG_ERR, "diskio: Couln't open kstat\n");
173
* Get the I/O Kit communication handle.
175
IOMasterPort(bootstrap_port, &masterPort);
178
#if defined(aix4) || defined(aix5)
180
* initialize values to gather information on first request
188
diskio_parse_config(const char *token, char *cptr)
190
copy_nword(cptr, type, sizeof(type));
202
if (disknr == cache_disknr && cache_time + CACHE_TIMEOUT > now) {
207
* could be optimiced by checking if cache_disknr<=disknr
208
* if so, just reread the data - not going through the whole chain
212
for (tksp = kc->kc_chain; tksp != NULL; tksp = tksp->ks_next) {
213
if (tksp->ks_type == KSTAT_TYPE_IO
214
&& !strcmp(tksp->ks_class, "disk")) {
216
if (kstat_read(kc, tksp, &kio) == -1)
217
snmp_log(LOG_ERR, "diskio: kstat_read failed\n");
220
cache_disknr = disknr;
232
var_diskio(struct variable * vp,
235
int exact, size_t * var_len, WriteMethod ** write_method)
238
* define any variables we might return as static!
240
static long long_ret;
242
if (header_simple_table
243
(vp, name, length, exact, var_len, write_method, MAX_DISKS))
247
if (get_disk(name[*length - 1] - 1) == 0)
252
* We can now simply test on vp's magic number, defined in diskio.h
256
long_ret = (long) name[*length - 1];
257
return (u_char *) & long_ret;
259
*var_len = strlen(ksp->ks_name);
260
return (u_char *) ksp->ks_name;
262
long_ret = (uint32_t) kio.nread;
263
return (u_char *) & long_ret;
264
case DISKIO_NWRITTEN:
265
long_ret = (uint32_t) kio.nwritten;
266
return (u_char *) & long_ret;
268
long_ret = (uint32_t) kio.reads;
269
return (u_char *) & long_ret;
271
long_ret = (uint32_t) kio.writes;
272
return (u_char *) & long_ret;
275
ERROR_MSG("diskio.c: don't know how to handle this request.");
278
* if we fall to here, fail by returning NULL
282
#endif /* solaris2 */
284
#if defined(bsdi3) || defined(bsdi4)
286
static struct diskstats *dk;
287
static char **dkname;
295
int size, dkn_size, i;
298
if (cache_time + CACHE_TIMEOUT > now) {
302
mib[1] = HW_DISKSTATS;
304
if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
305
perror("Can't get size of HW_DISKSTATS mib");
308
if (ndisk != size / sizeof(*dk)) {
312
for (i = 0; i < ndisk; i++)
317
ndisk = size / sizeof(*dk);
320
dkname = malloc(ndisk * sizeof(char *));
322
mib[1] = HW_DISKNAMES;
323
if (sysctl(mib, 2, NULL, &dkn_size, NULL, 0) < 0) {
324
perror("Can't get size of HW_DISKNAMES mib");
327
tp = t = malloc(dkn_size);
328
if (sysctl(mib, 2, t, &dkn_size, NULL, 0) < 0) {
329
perror("Can't get size of HW_DISKNAMES mib");
332
for (i = 0; i < ndisk; i++) {
333
dkname[i] = strdup(tp);
334
tp += strlen(tp) + 1;
337
dk = malloc(ndisk * sizeof(*dk));
340
mib[1] = HW_DISKSTATS;
341
if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
342
perror("Can't get HW_DISKSTATS mib");
350
var_diskio(struct variable * vp,
353
int exact, size_t * var_len, WriteMethod ** write_method)
355
static long long_ret;
361
if (header_simple_table
362
(vp, name, length, exact, var_len, write_method, ndisk))
365
indx = (unsigned int) (name[*length - 1] - 1);
371
long_ret = (long) indx + 1;
372
return (u_char *) & long_ret;
374
*var_len = strlen(dkname[indx]);
375
return (u_char *) dkname[indx];
378
(signed long) (dk[indx].dk_sectors * dk[indx].dk_secsize);
379
return (u_char *) & long_ret;
380
case DISKIO_NWRITTEN:
381
return NULL; /* Sigh... BSD doesn't keep seperate track */
383
long_ret = (signed long) dk[indx].dk_xfers;
384
return (u_char *) & long_ret;
386
return NULL; /* Sigh... BSD doesn't keep seperate track */
389
ERROR_MSG("diskio.c: don't know how to handle this request.");
395
#if defined(freebsd4) || defined(freebsd5)
397
static struct statinfo *stat;
407
if (cache_time + CACHE_TIMEOUT > now) {
411
stat = (struct statinfo *) malloc(sizeof(struct statinfo));
412
stat->dinfo = (struct devinfo *) malloc(sizeof(struct devinfo));
414
memset(stat->dinfo, 0, sizeof(struct devinfo));
416
if ((getdevs(stat)) == -1) {
417
fprintf(stderr, "Can't get devices:%s\n", devstat_errbuf);
420
ndisk = stat->dinfo->numdevs;
421
/* Gross hack to include device numbers in the device name array */
422
for (i = 0; i < ndisk; i++) {
423
char *cp = stat->dinfo->devices[i].device_name;
424
int len = strlen(cp);
425
if (len > DEVSTAT_NAME_LEN - 3)
428
sprintf(cp, "%d", stat->dinfo->devices[i].unit_number);
435
var_diskio(struct variable * vp,
438
int exact, size_t * var_len, WriteMethod ** write_method)
440
static long long_ret;
443
if (getstats() == 1) {
448
if (header_simple_table
449
(vp, name, length, exact, var_len, write_method, ndisk)) {
453
indx = (unsigned int) (name[*length - 1] - 1);
460
long_ret = (long) indx + 1;;
461
return (u_char *) & long_ret;
463
*var_len = strlen(stat->dinfo->devices[indx].device_name);
464
return (u_char *) stat->dinfo->devices[indx].device_name;
466
long_ret = (signed long) stat->dinfo->devices[indx].bytes_read;
467
return (u_char *) & long_ret;
468
case DISKIO_NWRITTEN:
469
long_ret = (signed long) stat->dinfo->devices[indx].bytes_written;
470
return (u_char *) & long_ret;
472
long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
473
return (u_char *) & long_ret;
475
long_ret = (signed long) stat->dinfo->devices[indx].num_writes;
476
return (u_char *) & long_ret;
479
ERROR_MSG("diskio.c: don't know how to handle this request.");
483
#endif /* freebsd4 */
487
typedef struct linux_diskio
506
typedef struct linux_diskio_header
508
linux_diskio* indices;
510
} linux_diskio_header;
512
linux_diskio_header head;
523
if (cache_time + CACHE_TIMEOUT > now) {
527
/* remove the indicies and repopulate */
532
head.indices = (struct linux_diskio*)malloc(
533
sizeof(struct linux_diskio[MAX_DISKS]));
536
memset(head.indices, (sizeof(linux_diskio) * MAX_DISKS), 0);
539
parts = fopen("/proc/partitions", "r");
545
first few fscanfs are garbage we don't care about. skip it.
546
Probably a better way to do this, but I cheaped out on ya :).
551
fgets(buffer, sizeof(buffer), parts);
554
linux_diskio* pTemp = &head.indices[head.length];
556
if ((feof(parts) != 0) || head.length >= MAX_DISKS)
559
fscanf (parts, "%d %d %ld %s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n",
561
&pTemp->minor, &pTemp->blocks, pTemp->name, &pTemp->rio,
562
&pTemp->rmerge, &pTemp->rsect, &pTemp->ruse, &pTemp->wio,
563
&pTemp->wmerge, &pTemp->wsect, &pTemp->wuse, &pTemp->running,
564
&pTemp->use, &pTemp->aveq);
578
var_diskio(struct variable * vp,
583
WriteMethod ** write_method)
586
static long long_ret;
592
if (header_simple_table(vp, name, length, exact, var_len, write_method, head.length))
597
indx = (unsigned int) (name[*length - 1] - 1);
599
if (indx >= head.length)
604
long_ret = (long) indx+1;
605
return (u_char *) &long_ret;
607
*var_len = strlen(head.indices[indx].name);
608
return (u_char *) head.indices[indx].name;
610
long_ret = (signed long) head.indices[indx].rio;
611
return (u_char *) & long_ret;
612
case DISKIO_NWRITTEN:
613
long_ret = (signed long) head.indices[indx].wio;
614
return (u_char *) & long_ret;
616
long_ret = (signed long) head.indices[indx].ruse;
617
return (u_char *) & long_ret;
619
long_ret = (signed long) head.indices[indx].wuse;
620
return (u_char *) & long_ret;
623
ERROR_MSG("diskio.c: don't know how to handle this request.");
631
#define MAXDRIVES 16 /* most drives we will record */
632
#define MAXDRIVENAME 31 /* largest drive name we allow */
634
#define kIDXBytesRead 0 /* used as index into the stats array in a drivestats struct */
635
#define kIDXBytesWritten 1
636
#define kIDXNumReads 2
637
#define kIDXNumWrites 3
641
char name[MAXDRIVENAME + 1];
642
long bsd_unit_number;
643
long stats[kIDXLast+1];
646
static struct drivestats drivestat[MAXDRIVES];
648
static mach_port_t masterPort; /* to communicate with I/O Kit */
650
static int num_drives; /* number of drives detected */
653
collect_drive_stats(io_registry_entry_t driver, long *stats)
656
CFDictionaryRef properties;
657
CFDictionaryRef statistics;
659
kern_return_t status;
664
* If the drive goes away, we may not get any properties
665
* for it. So take some defaults. Nb: use memset ??
667
for (i = 0; i < kIDXLast; i++) {
671
/* retrieve the properties */
672
status = IORegistryEntryCreateCFProperties(driver, (CFMutableDictionaryRef *)&properties,
673
kCFAllocatorDefault, kNilOptions);
674
if (status != KERN_SUCCESS) {
675
snmp_log(LOG_ERR, "diskio: device has no properties\n");
676
/* fprintf(stderr, "device has no properties\n"); */
680
/* retrieve statistics from properties */
681
statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
682
CFSTR(kIOBlockStorageDriverStatisticsKey));
685
/* Now hand me the crystals. */
686
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
687
CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
688
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
689
stats[kIDXBytesRead] = value;
692
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
693
CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
694
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
695
stats[kIDXBytesWritten] = value;
698
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
699
CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
700
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
701
stats[kIDXNumReads] = value;
703
if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
704
CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
705
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
706
stats[kIDXNumWrites] = value;
709
/* we're done with the properties, release them */
710
CFRelease(properties);
715
* Check whether an IORegistryEntry refers to a valid
716
* I/O device, and if so, collect the information.
719
handle_drive(io_registry_entry_t drive, struct drivestats * dstat)
721
io_registry_entry_t parent;
722
CFDictionaryRef properties;
725
kern_return_t status;
727
/* get drive's parent */
728
status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
729
if (status != KERN_SUCCESS) {
730
snmp_log(LOG_ERR, "diskio: device has no parent\n");
731
/* fprintf(stderr, "device has no parent\n"); */
735
if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
737
/* get drive properties */
738
status = IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties,
739
kCFAllocatorDefault, kNilOptions);
740
if (status != KERN_SUCCESS) {
741
snmp_log(LOG_ERR, "diskio: device has no properties\n");
742
/* fprintf(stderr, "device has no properties\n"); */
746
/* get BSD name and unitnumber from properties */
747
name = (CFStringRef)CFDictionaryGetValue(properties,
748
CFSTR(kIOBSDNameKey));
749
number = (CFNumberRef)CFDictionaryGetValue(properties,
750
CFSTR(kIOBSDUnitKey));
752
/* Collect stats and if succesful store them with the name and unitnumber */
753
if (!collect_drive_stats(parent, dstat->stats)) {
755
CFStringGetCString(name, dstat->name, MAXDRIVENAME, CFStringGetSystemEncoding());
756
CFNumberGetValue(number, kCFNumberSInt32Type, &dstat->bsd_unit_number);
760
/* clean up, return success */
761
CFRelease(properties);
765
/* failed, don't keep parent */
766
IOObjectRelease(parent);
774
io_iterator_t drivelist;
775
io_registry_entry_t drive;
776
CFMutableDictionaryRef match;
777
kern_return_t status;
779
now = time(NULL); /* register current time and check wether cache can be used */
780
if (cache_time + CACHE_TIMEOUT > now) {
784
/* Retrieve a list of drives. */
785
match = IOServiceMatching("IOMedia");
786
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
787
status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
788
if (status != KERN_SUCCESS) {
789
snmp_log(LOG_ERR, "diskio: couldn't match whole IOMedia devices\n");
790
/* fprintf(stderr,"Couldn't match whole IOMedia devices\n"); */
794
num_drives = 0; /* NB: Incremented by handle_drive */
795
while ((drive = IOIteratorNext(drivelist)) && (num_drives < MAXDRIVES)) {
796
handle_drive(drive, &drivestat[num_drives]);
797
IOObjectRelease(drive);
799
IOObjectRelease(drivelist);
806
var_diskio(struct variable * vp,
809
int exact, size_t * var_len, WriteMethod ** write_method)
811
static long long_ret;
814
if (getstats() == 1) {
819
if (header_simple_table
820
(vp, name, length, exact, var_len, write_method, num_drives)) {
824
indx = (unsigned int) (name[*length - 1] - 1);
826
if (indx >= num_drives)
831
long_ret = (long) drivestat[indx].bsd_unit_number;
832
return (u_char *) & long_ret;
834
*var_len = strlen(drivestat[indx].name);
835
return (u_char *) drivestat[indx].name;
837
long_ret = (signed long) drivestat[indx].stats[kIDXBytesRead];
838
return (u_char *) & long_ret;
839
case DISKIO_NWRITTEN:
840
long_ret = (signed long) drivestat[indx].stats[kIDXBytesWritten];
841
return (u_char *) & long_ret;
843
long_ret = (signed long) drivestat[indx].stats[kIDXNumReads];
844
return (u_char *) & long_ret;
846
long_ret = (signed long) drivestat[indx].stats[kIDXNumWrites];
847
return (u_char *) & long_ret;
850
ERROR_MSG("diskio.c: don't know how to handle this request.");
857
#if defined(aix4) || defined(aix5)
859
* collect statistics for all disks
868
/* cache valid? if yes, just return */
870
if (ps_disk != NULL && cache_time + CACHE_TIMEOUT > now) {
874
/* get number of disks we have */
875
i = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
878
/* if number of disks differs or structures are uninitialized, init them */
879
if(i != ps_numdisks || ps_disk == NULL) {
880
if(ps_disk != NULL) free(ps_disk);
882
ps_disk = malloc(sizeof(perfstat_disk_t) * ps_numdisks);
883
if(ps_disk == NULL) return 1;
886
/* gather statistics about all disks we have */
887
strcpy(first.name, "");
888
i = perfstat_disk(&first, ps_disk, sizeof(perfstat_disk_t), ps_numdisks);
889
if(i != ps_numdisks) return 1;
897
var_diskio(struct variable * vp,
900
int exact, size_t * var_len, WriteMethod ** write_method)
902
static long long_ret;
905
/* get disk statistics */
909
if (header_simple_table
910
(vp, name, length, exact, var_len, write_method, ps_numdisks))
913
indx = (unsigned int) (name[*length - 1] - 1);
914
if (indx >= ps_numdisks)
917
/* deliver requested data on requested disk */
920
long_ret = (long) indx;
921
return (u_char *) & long_ret;
923
*var_len = strlen(ps_disk[indx].name);
924
return (u_char *) ps_disk[indx].name;
926
long_ret = (signed long) ps_disk[indx].rblks * ps_disk[indx].bsize;
927
return (u_char *) & long_ret;
928
case DISKIO_NWRITTEN:
929
long_ret = (signed long) ps_disk[indx].wblks * ps_disk[indx].bsize;
930
return (u_char *) & long_ret;
932
long_ret = (signed long) ps_disk[indx].xfers;
933
return (u_char *) & long_ret;
935
long_ret = (signed long) 0; /* AIX has just one value for read/write transfers */
936
return (u_char *) & long_ret;
939
ERROR_MSG("diskio.c: don't know how to handle this request.");
942
/* return NULL in case of error */