1 #include <net-snmp/net-snmp-config.h>
4 * needed by util_funcs.h
11 # include <sys/time.h>
17 #include <net-snmp/net-snmp-includes.h>
18 #include <net-snmp/agent/net-snmp-agent-includes.h>
21 * header_generic() comes from here
23 #include "util_funcs.h"
30 #define CACHE_TIMEOUT 10
31 static time_t cache_time = 0;
38 static kstat_ctl_t *kc;
40 static kstat_io_t kio;
41 static int cache_disknr = -1;
44 #if defined(bsdi3) || defined(bsdi4)
46 #include <sys/param.h>
47 #include <sys/sysctl.h>
48 #include <sys/diskstats.h>
51 #if defined (freebsd4) || defined(freebsd5)
52 #include <sys/dkstat.h>
57 #include <CoreFoundation/CoreFoundation.h>
58 #include <IOKit/IOKitLib.h>
59 #include <IOKit/storage/IOBlockStorageDriver.h>
60 #include <IOKit/storage/IOMedia.h>
61 #include <IOKit/IOBSD.h>
63 static mach_port_t masterPort; /* to communicate with I/O Kit */
67 void diskio_parse_config(const char *, char *);
70 /*********************
72 * Initialisation & common implementation functions
74 *********************/
78 * this is an optional function called at the time the agent starts up
79 * to do any initilizations you might require. You don't have to
80 * create it, as it is optional.
84 * IMPORTANT: If you add or remove this function, you *must* re-run
85 * the configure script as it checks for its existance.
92 * Define a 'variable' structure that is a representation of our mib.
96 * first, we have to pick the variable type. They are all defined in
97 * the var_struct.h file in the agent subdirectory. I'm picking the
98 * variable2 structure since the longest sub-component of the oid I
99 * want to load is .2.1 and .2.2 so I need at most 2 spaces in the
103 struct variable2 diskio_variables[] = {
104 {DISKIO_INDEX, ASN_INTEGER, RONLY, var_diskio, 1, {1}},
105 {DISKIO_DEVICE, ASN_OCTET_STR, RONLY, var_diskio, 1, {2}},
106 {DISKIO_NREAD, ASN_COUNTER, RONLY, var_diskio, 1, {3}},
107 {DISKIO_NWRITTEN, ASN_COUNTER, RONLY, var_diskio, 1, {4}},
108 {DISKIO_READS, ASN_COUNTER, RONLY, var_diskio, 1, {5}},
109 {DISKIO_WRITES, ASN_COUNTER, RONLY, var_diskio, 1, {6}},
113 * Define the OID pointer to the top of the mib tree that we're
114 * registering underneath.
116 oid diskio_variables_oid[] =
117 { 1, 3, 6, 1, 4, 1, 2021, 13, 15, 1, 1 };
120 * register ourselves with the agent to handle our mib tree
122 * This is a macro defined in ../../snmp_vars.h. The arguments are:
124 * descr: A short description of the mib group being loaded.
125 * var: The variable structure to load.
126 * vartype: The variable structure used to define it (variable2, variable4, ...)
127 * theoid: A *initialized* *exact length* oid pointer.
128 * (sizeof(theoid) *must* return the number of elements!)
130 REGISTER_MIB("diskio", diskio_variables, variable2,
131 diskio_variables_oid);
134 * Added to parse snmpd.conf - abby
136 snmpd_register_config_handler("diskio", diskio_parse_config,
137 NULL, "diskio [device-type]");
143 snmp_log(LOG_ERR, "diskio: Couln't open kstat\n");
148 * Get the I/O Kit communication handle.
150 IOMasterPort(bootstrap_port, &masterPort);
155 diskio_parse_config(const char *token, char *cptr)
157 copy_nword(cptr, type, sizeof(type));
169 if (disknr == cache_disknr && cache_time + CACHE_TIMEOUT > now) {
174 * could be optimiced by checking if cache_disknr<=disknr
175 * if so, just reread the data - not going through the whole chain
179 for (tksp = kc->kc_chain; tksp != NULL; tksp = tksp->ks_next) {
180 if (tksp->ks_type == KSTAT_TYPE_IO
181 && !strcmp(tksp->ks_class, "disk")) {
183 if (kstat_read(kc, tksp, &kio) == -1)
184 snmp_log(LOG_ERR, "diskio: kstat_read failed\n");
187 cache_disknr = disknr;
199 var_diskio(struct variable * vp,
202 int exact, size_t * var_len, WriteMethod ** write_method)
205 * define any variables we might return as static!
207 static long long_ret;
209 if (header_simple_table
210 (vp, name, length, exact, var_len, write_method, MAX_DISKS))
214 if (get_disk(name[*length - 1] - 1) == 0)
219 * We can now simply test on vp's magic number, defined in diskio.h
223 long_ret = (long) name[*length - 1];
224 return (u_char *) & long_ret;
226 *var_len = strlen(ksp->ks_name);
227 return (u_char *) ksp->ks_name;
229 long_ret = (signed long) kio.nread;
230 return (u_char *) & long_ret;
231 case DISKIO_NWRITTEN:
232 long_ret = (signed long) kio.nwritten;
233 return (u_char *) & long_ret;
235 long_ret = (signed long) kio.reads;
236 return (u_char *) & long_ret;
238 long_ret = (signed long) kio.writes;
239 return (u_char *) & long_ret;
242 ERROR_MSG("diskio.c: don't know how to handle this request.");
245 * if we fall to here, fail by returning NULL
249 #endif /* solaris2 */
251 #if defined(bsdi3) || defined(bsdi4)
253 static struct diskstats *dk;
254 static char **dkname;
262 int size, dkn_size, i;
265 if (cache_time + CACHE_TIMEOUT > now) {
269 mib[1] = HW_DISKSTATS;
271 if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
272 perror("Can't get size of HW_DISKSTATS mib");
275 if (ndisk != size / sizeof(*dk)) {
279 for (i = 0; i < ndisk; i++)
284 ndisk = size / sizeof(*dk);
287 dkname = malloc(ndisk * sizeof(char *));
289 mib[1] = HW_DISKNAMES;
290 if (sysctl(mib, 2, NULL, &dkn_size, NULL, 0) < 0) {
291 perror("Can't get size of HW_DISKNAMES mib");
294 tp = t = malloc(dkn_size);
295 if (sysctl(mib, 2, t, &dkn_size, NULL, 0) < 0) {
296 perror("Can't get size of HW_DISKNAMES mib");
299 for (i = 0; i < ndisk; i++) {
300 dkname[i] = strdup(tp);
301 tp += strlen(tp) + 1;
304 dk = malloc(ndisk * sizeof(*dk));
307 mib[1] = HW_DISKSTATS;
308 if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
309 perror("Can't get HW_DISKSTATS mib");
317 var_diskio(struct variable * vp,
320 int exact, size_t * var_len, WriteMethod ** write_method)
322 static long long_ret;
328 if (header_simple_table
329 (vp, name, length, exact, var_len, write_method, ndisk))
332 indx = (unsigned int) (name[*length - 1] - 1);
338 long_ret = (long) indx + 1;
339 return (u_char *) & long_ret;
341 *var_len = strlen(dkname[indx]);
342 return (u_char *) dkname[indx];
345 (signed long) (dk[indx].dk_sectors * dk[indx].dk_secsize);
346 return (u_char *) & long_ret;
347 case DISKIO_NWRITTEN:
348 return NULL; /* Sigh... BSD doesn't keep seperate track */
350 long_ret = (signed long) dk[indx].dk_xfers;
351 return (u_char *) & long_ret;
353 return NULL; /* Sigh... BSD doesn't keep seperate track */
356 ERROR_MSG("diskio.c: don't know how to handle this request.");
362 #if defined(freebsd4) || defined(freebsd5)
364 static struct statinfo *stat;
374 if (cache_time + CACHE_TIMEOUT > now) {
378 stat = (struct statinfo *) malloc(sizeof(struct statinfo));
379 stat->dinfo = (struct devinfo *) malloc(sizeof(struct devinfo));
381 memset(stat->dinfo, 0, sizeof(struct devinfo));
383 if ((getdevs(stat)) == -1) {
384 fprintf(stderr, "Can't get devices:%s\n", devstat_errbuf);
387 ndisk = stat->dinfo->numdevs;
388 /* Gross hack to include device numbers in the device name array */
389 for (i = 0; i < ndisk; i++) {
390 char *cp = stat->dinfo->devices[i].device_name;
391 int len = strlen(cp);
392 if (len > DEVSTAT_NAME_LEN - 3)
395 sprintf(cp, "%d", stat->dinfo->devices[i].unit_number);
402 var_diskio(struct variable * vp,
405 int exact, size_t * var_len, WriteMethod ** write_method)
407 static long long_ret;
410 if (getstats() == 1) {
415 if (header_simple_table
416 (vp, name, length, exact, var_len, write_method, ndisk)) {
420 indx = (unsigned int) (name[*length - 1] - 1);
427 long_ret = (long) indx + 1;;
428 return (u_char *) & long_ret;
430 *var_len = strlen(stat->dinfo->devices[indx].device_name);
431 return (u_char *) stat->dinfo->devices[indx].device_name;
433 long_ret = (signed long) stat->dinfo->devices[indx].bytes_read;
434 return (u_char *) & long_ret;
435 case DISKIO_NWRITTEN:
436 long_ret = (signed long) stat->dinfo->devices[indx].bytes_written;
437 return (u_char *) & long_ret;
439 long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
440 return (u_char *) & long_ret;
442 long_ret = (signed long) stat->dinfo->devices[indx].num_writes;
443 return (u_char *) & long_ret;
446 ERROR_MSG("diskio.c: don't know how to handle this request.");
450 #endif /* freebsd4 */
454 #define MAXDRIVES 16 /* most drives we will record */
455 #define MAXDRIVENAME 31 /* largest drive name we allow */
457 #define kIDXBytesRead 0 /* used as index into the stats array in a drivestats struct */
458 #define kIDXBytesWritten 1
459 #define kIDXNumReads 2
460 #define kIDXNumWrites 3
464 char name[MAXDRIVENAME + 1];
465 long bsd_unit_number;
466 long stats[kIDXLast+1];
469 static struct drivestats drivestat[MAXDRIVES];
471 static mach_port_t masterPort; /* to communicate with I/O Kit */
473 static int num_drives; /* number of drives detected */
476 collect_drive_stats(io_registry_entry_t driver, long *stats)
479 CFDictionaryRef properties;
480 CFDictionaryRef statistics;
482 kern_return_t status;
487 * If the drive goes away, we may not get any properties
488 * for it. So take some defaults. Nb: use memset ??
490 for (i = 0; i < kIDXLast; i++) {
494 /* retrieve the properties */
495 status = IORegistryEntryCreateCFProperties(driver, (CFMutableDictionaryRef *)&properties,
496 kCFAllocatorDefault, kNilOptions);
497 if (status != KERN_SUCCESS) {
498 snmp_log(LOG_ERR, "diskio: device has no properties\n");
499 /* fprintf(stderr, "device has no properties\n"); */
503 /* retrieve statistics from properties */
504 statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
505 CFSTR(kIOBlockStorageDriverStatisticsKey));
508 /* Now hand me the crystals. */
509 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
510 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
511 CFNumberGetValue(number, kCFNumberSInt32Type, &value);
512 stats[kIDXBytesRead] = value;
515 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
516 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
517 CFNumberGetValue(number, kCFNumberSInt32Type, &value);
518 stats[kIDXBytesWritten] = value;
521 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
522 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
523 CFNumberGetValue(number, kCFNumberSInt32Type, &value);
524 stats[kIDXNumReads] = value;
526 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
527 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
528 CFNumberGetValue(number, kCFNumberSInt32Type, &value);
529 stats[kIDXNumWrites] = value;
532 /* we're done with the properties, release them */
533 CFRelease(properties);
538 * Check whether an IORegistryEntry refers to a valid
539 * I/O device, and if so, collect the information.
542 handle_drive(io_registry_entry_t drive, struct drivestats * dstat)
544 io_registry_entry_t parent;
545 CFDictionaryRef properties;
548 kern_return_t status;
550 /* get drive's parent */
551 status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
552 if (status != KERN_SUCCESS) {
553 snmp_log(LOG_ERR, "diskio: device has no parent\n");
554 /* fprintf(stderr, "device has no parent\n"); */
558 if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
560 /* get drive properties */
561 status = IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties,
562 kCFAllocatorDefault, kNilOptions);
563 if (status != KERN_SUCCESS) {
564 snmp_log(LOG_ERR, "diskio: device has no properties\n");
565 /* fprintf(stderr, "device has no properties\n"); */
569 /* get BSD name and unitnumber from properties */
570 name = (CFStringRef)CFDictionaryGetValue(properties,
571 CFSTR(kIOBSDNameKey));
572 number = (CFNumberRef)CFDictionaryGetValue(properties,
573 CFSTR(kIOBSDUnitKey));
575 /* Collect stats and if succesful store them with the name and unitnumber */
576 if (!collect_drive_stats(parent, dstat->stats)) {
578 CFStringGetCString(name, dstat->name, MAXDRIVENAME, CFStringGetSystemEncoding());
579 CFNumberGetValue(number, kCFNumberSInt32Type, &dstat->bsd_unit_number);
583 /* clean up, return success */
584 CFRelease(properties);
588 /* failed, don't keep parent */
589 IOObjectRelease(parent);
597 io_iterator_t drivelist;
598 io_registry_entry_t drive;
599 CFMutableDictionaryRef match;
600 kern_return_t status;
602 now = time(NULL); /* register current time and check wether cache can be used */
603 if (cache_time + CACHE_TIMEOUT > now) {
607 /* Retrieve a list of drives. */
608 match = IOServiceMatching("IOMedia");
609 CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
610 status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
611 if (status != KERN_SUCCESS) {
612 snmp_log(LOG_ERR, "diskio: couldn't match whole IOMedia devices\n");
613 /* fprintf(stderr,"Couldn't match whole IOMedia devices\n"); */
617 num_drives = 0; /* NB: Incremented by handle_drive */
618 while ((drive = IOIteratorNext(drivelist)) && (num_drives < MAXDRIVES)) {
619 handle_drive(drive, &drivestat[num_drives]);
620 IOObjectRelease(drive);
622 IOObjectRelease(drivelist);
629 var_diskio(struct variable * vp,
632 int exact, size_t * var_len, WriteMethod ** write_method)
634 static long long_ret;
637 if (getstats() == 1) {
642 if (header_simple_table
643 (vp, name, length, exact, var_len, write_method, num_drives)) {
647 indx = (unsigned int) (name[*length - 1] - 1);
649 if (indx >= num_drives)
654 long_ret = (long) drivestat[indx].bsd_unit_number;
655 return (u_char *) & long_ret;
657 *var_len = strlen(drivestat[indx].name);
658 return (u_char *) drivestat[indx].name;
660 long_ret = (signed long) drivestat[indx].stats[kIDXBytesRead];
661 return (u_char *) & long_ret;
662 case DISKIO_NWRITTEN:
663 long_ret = (signed long) drivestat[indx].stats[kIDXBytesWritten];
664 return (u_char *) & long_ret;
666 long_ret = (signed long) drivestat[indx].stats[kIDXNumReads];
667 return (u_char *) & long_ret;
669 long_ret = (signed long) drivestat[indx].stats[kIDXNumWrites];
670 return (u_char *) & long_ret;
673 ERROR_MSG("diskio.c: don't know how to handle this request.");