# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / ucd-snmp / diskio.c
1 #include <net-snmp/net-snmp-config.h>
2
3 /*
4  * needed by util_funcs.h 
5  */
6 #if TIME_WITH_SYS_TIME
7 # include <sys/time.h>
8 # include <time.h>
9 #else
10 # if HAVE_SYS_TIME_H
11 #  include <sys/time.h>
12 # else
13 #  include <time.h>
14 # endif
15 #endif
16
17 #include <net-snmp/net-snmp-includes.h>
18 #include <net-snmp/agent/net-snmp-agent-includes.h>
19
20 /*
21  * header_generic() comes from here 
22  */
23 #include "util_funcs.h"
24
25 /*
26  * include our .h file 
27  */
28 #include "diskio.h"
29
30 #define CACHE_TIMEOUT 10
31 static time_t   cache_time = 0;
32
33 #ifdef solaris2
34 #include <kstat.h>
35
36 #define MAX_DISKS 20
37
38 static kstat_ctl_t *kc;
39 static kstat_t *ksp;
40 static kstat_io_t kio;
41 static int      cache_disknr = -1;
42 #endif                          /* solaris2 */
43
44 #if defined(bsdi3) || defined(bsdi4)
45 #include <string.h>
46 #include <sys/param.h>
47 #include <sys/sysctl.h>
48 #include <sys/diskstats.h>
49 #endif                          /* bsdi */
50
51 #if defined (freebsd4) || defined(freebsd5)
52 #include <sys/dkstat.h>
53 #include <devstat.h>
54 #endif                          /* freebsd */
55
56 #if defined (darwin)
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>
62
63 static mach_port_t masterPort;          /* to communicate with I/O Kit  */
64 #endif                          /* darwin */
65
66 static char     type[20];
67 void            diskio_parse_config(const char *, char *);
68 FILE           *file;
69
70          /*********************
71          *
72          *  Initialisation & common implementation functions
73          *
74          *********************/
75
76
77 /*
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. 
81  */
82
83 /*
84  * IMPORTANT: If you add or remove this function, you *must* re-run
85  * the configure script as it checks for its existance. 
86  */
87
88 void
89 init_diskio(void)
90 {
91     /*
92      * Define a 'variable' structure that is a representation of our mib. 
93      */
94
95     /*
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
100      * last entry. 
101      */
102
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}},
110     };
111
112     /*
113      * Define the OID pointer to the top of the mib tree that we're
114      * registering underneath. 
115      */
116     oid             diskio_variables_oid[] =
117         { 1, 3, 6, 1, 4, 1, 2021, 13, 15, 1, 1 };
118
119     /*
120      * register ourselves with the agent to handle our mib tree
121      * 
122      * This is a macro defined in ../../snmp_vars.h.  The arguments are:
123      * 
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!)  
129      */
130     REGISTER_MIB("diskio", diskio_variables, variable2,
131                  diskio_variables_oid);
132
133     /*
134      * Added to parse snmpd.conf - abby
135      */
136     snmpd_register_config_handler("diskio", diskio_parse_config,
137                                   NULL, "diskio [device-type]");
138
139 #ifdef solaris2
140     kc = kstat_open();
141
142     if (kc == NULL)
143         snmp_log(LOG_ERR, "diskio: Couln't open kstat\n");
144 #endif
145
146 #ifdef darwin
147     /*
148      * Get the I/O Kit communication handle.
149      */
150     IOMasterPort(bootstrap_port, &masterPort);
151 #endif
152 }
153
154 void
155 diskio_parse_config(const char *token, char *cptr)
156 {
157     copy_nword(cptr, type, sizeof(type));
158 }
159
160 #ifdef solaris2
161 int
162 get_disk(int disknr)
163 {
164     time_t          now;
165     int             i = 0;
166     kstat_t *tksp;
167
168     now = time(NULL);
169     if (disknr == cache_disknr && cache_time + CACHE_TIMEOUT > now) {
170         return 1;
171     }
172
173     /*
174      * could be optimiced by checking if cache_disknr<=disknr
175      * if so, just reread the data - not going through the whole chain
176      * from kc->kc_chain 
177      */
178
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")) {
182             if (i == disknr) {
183                 if (kstat_read(kc, tksp, &kio) == -1)
184                     snmp_log(LOG_ERR, "diskio: kstat_read failed\n");
185                 ksp = tksp;
186                 cache_time = now;
187                 cache_disknr = disknr;
188                 return 1;
189             } else {
190                 i++;
191             }
192         }
193     }
194     return 0;
195 }
196
197
198 u_char         *
199 var_diskio(struct variable * vp,
200            oid * name,
201            size_t * length,
202            int exact, size_t * var_len, WriteMethod ** write_method)
203 {
204     /*
205      * define any variables we might return as static! 
206      */
207     static long     long_ret;
208
209     if (header_simple_table
210         (vp, name, length, exact, var_len, write_method, MAX_DISKS))
211         return NULL;
212
213
214     if (get_disk(name[*length - 1] - 1) == 0)
215         return NULL;
216
217
218     /*
219      * We can now simply test on vp's magic number, defined in diskio.h 
220      */
221     switch (vp->magic) {
222     case DISKIO_INDEX:
223         long_ret = (long) name[*length - 1];
224         return (u_char *) & long_ret;
225     case DISKIO_DEVICE:
226         *var_len = strlen(ksp->ks_name);
227         return (u_char *) ksp->ks_name;
228     case DISKIO_NREAD:
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;
234     case DISKIO_READS:
235         long_ret = (signed long) kio.reads;
236         return (u_char *) & long_ret;
237     case DISKIO_WRITES:
238         long_ret = (signed long) kio.writes;
239         return (u_char *) & long_ret;
240
241     default:
242         ERROR_MSG("diskio.c: don't know how to handle this request.");
243     }
244     /*
245      * if we fall to here, fail by returning NULL 
246      */
247     return NULL;
248 }
249 #endif                          /* solaris2 */
250
251 #if defined(bsdi3) || defined(bsdi4)
252 static int      ndisk;
253 static struct diskstats *dk;
254 static char   **dkname;
255
256 static int
257 getstats(void)
258 {
259     time_t          now;
260     int             mib[2];
261     char           *t, *tp;
262     int             size, dkn_size, i;
263
264     now = time(NULL);
265     if (cache_time + CACHE_TIMEOUT > now) {
266         return 1;
267     }
268     mib[0] = CTL_HW;
269     mib[1] = HW_DISKSTATS;
270     size = 0;
271     if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
272         perror("Can't get size of HW_DISKSTATS mib");
273         return 0;
274     }
275     if (ndisk != size / sizeof(*dk)) {
276         if (dk)
277             free(dk);
278         if (dkname) {
279             for (i = 0; i < ndisk; i++)
280                 if (dkname[i])
281                     free(dkname[i]);
282             free(dkname);
283         }
284         ndisk = size / sizeof(*dk);
285         if (ndisk == 0)
286             return 0;
287         dkname = malloc(ndisk * sizeof(char *));
288         mib[0] = CTL_HW;
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");
292             return 0;
293         }
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");
297             return 0;
298         }
299         for (i = 0; i < ndisk; i++) {
300             dkname[i] = strdup(tp);
301             tp += strlen(tp) + 1;
302         }
303         free(t);
304         dk = malloc(ndisk * sizeof(*dk));
305     }
306     mib[0] = CTL_HW;
307     mib[1] = HW_DISKSTATS;
308     if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
309         perror("Can't get HW_DISKSTATS mib");
310         return 0;
311     }
312     cache_time = now;
313     return 1;
314 }
315
316 u_char         *
317 var_diskio(struct variable * vp,
318            oid * name,
319            size_t * length,
320            int exact, size_t * var_len, WriteMethod ** write_method)
321 {
322     static long     long_ret;
323     unsigned int    indx;
324
325     if (getstats() == 0)
326         return 0;
327
328     if (header_simple_table
329         (vp, name, length, exact, var_len, write_method, ndisk))
330         return NULL;
331
332     indx = (unsigned int) (name[*length - 1] - 1);
333     if (indx >= ndisk)
334         return NULL;
335
336     switch (vp->magic) {
337     case DISKIO_INDEX:
338         long_ret = (long) indx + 1;
339         return (u_char *) & long_ret;
340     case DISKIO_DEVICE:
341         *var_len = strlen(dkname[indx]);
342         return (u_char *) dkname[indx];
343     case DISKIO_NREAD:
344         long_ret =
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 */
349     case DISKIO_READS:
350         long_ret = (signed long) dk[indx].dk_xfers;
351         return (u_char *) & long_ret;
352     case DISKIO_WRITES:
353         return NULL;            /* Sigh... BSD doesn't keep seperate track */
354
355     default:
356         ERROR_MSG("diskio.c: don't know how to handle this request.");
357     }
358     return NULL;
359 }
360 #endif                          /* bsdi */
361
362 #if defined(freebsd4) || defined(freebsd5)
363 static int      ndisk;
364 static struct statinfo *stat;
365 FILE           *file;
366
367 static int
368 getstats(void)
369 {
370     time_t          now;
371     int             i;
372
373     now = time(NULL);
374     if (cache_time + CACHE_TIMEOUT > now) {
375         return 0;
376     }
377     if (stat == NULL) {
378         stat = (struct statinfo *) malloc(sizeof(struct statinfo));
379         stat->dinfo = (struct devinfo *) malloc(sizeof(struct devinfo));
380     }
381     memset(stat->dinfo, 0, sizeof(struct devinfo));
382
383     if ((getdevs(stat)) == -1) {
384         fprintf(stderr, "Can't get devices:%s\n", devstat_errbuf);
385         return 1;
386     }
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)
393         len -= 3;
394       cp += len;
395       sprintf(cp, "%d", stat->dinfo->devices[i].unit_number);
396     }
397     cache_time = now;
398     return 0;
399 }
400
401 u_char         *
402 var_diskio(struct variable * vp,
403            oid * name,
404            size_t * length,
405            int exact, size_t * var_len, WriteMethod ** write_method)
406 {
407     static long     long_ret;
408     unsigned int    indx;
409
410     if (getstats() == 1) {
411         return NULL;
412     }
413
414
415     if (header_simple_table
416         (vp, name, length, exact, var_len, write_method, ndisk)) {
417         return NULL;
418     }
419
420     indx = (unsigned int) (name[*length - 1] - 1);
421
422     if (indx >= ndisk)
423         return NULL;
424
425     switch (vp->magic) {
426     case DISKIO_INDEX:
427         long_ret = (long) indx + 1;;
428         return (u_char *) & long_ret;
429     case DISKIO_DEVICE:
430         *var_len = strlen(stat->dinfo->devices[indx].device_name);
431         return (u_char *) stat->dinfo->devices[indx].device_name;
432     case DISKIO_NREAD:
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;
438     case DISKIO_READS:
439         long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
440         return (u_char *) & long_ret;
441     case DISKIO_WRITES:
442         long_ret = (signed long) stat->dinfo->devices[indx].num_writes;
443         return (u_char *) & long_ret;
444
445     default:
446         ERROR_MSG("diskio.c: don't know how to handle this request.");
447     }
448     return NULL;
449 }
450 #endif                          /* freebsd4 */
451
452 #if defined(darwin)
453
454 #define MAXDRIVES       16      /* most drives we will record */
455 #define MAXDRIVENAME    31      /* largest drive name we allow */
456
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
461 #define kIDXLast                3
462
463 struct drivestats {
464     char name[MAXDRIVENAME + 1];
465     long bsd_unit_number;
466     long stats[kIDXLast+1];
467 };
468
469 static struct drivestats drivestat[MAXDRIVES];
470
471 static mach_port_t masterPort;          /* to communicate with I/O Kit  */
472
473 static int num_drives;                  /* number of drives detected    */
474
475 static int
476 collect_drive_stats(io_registry_entry_t driver, long *stats)
477 {
478     CFNumberRef     number;
479     CFDictionaryRef properties;
480     CFDictionaryRef statistics;
481     long            value;
482     kern_return_t   status;
483     int             i;
484
485
486     /*
487      * If the drive goes away, we may not get any properties
488      * for it.  So take some defaults. Nb: use memset ??
489      */
490     for (i = 0; i < kIDXLast; i++) {
491         stats[i] = 0;
492     }
493
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"); */
500         return (1);
501     }
502
503     /* retrieve statistics from properties */
504     statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
505                                                        CFSTR(kIOBlockStorageDriverStatisticsKey));
506     if (statistics) {
507
508         /* Now hand me the crystals. */
509         if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
510                                                  CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
511             CFNumberGetValue(number, kCFNumberSInt32Type, &value);
512             stats[kIDXBytesRead] = value;
513         }
514
515         if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
516                                                  CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
517             CFNumberGetValue(number, kCFNumberSInt32Type, &value);
518             stats[kIDXBytesWritten] = value;
519         }
520
521         if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
522                                                  CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
523             CFNumberGetValue(number, kCFNumberSInt32Type, &value);
524             stats[kIDXNumReads] = value;
525         }
526         if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
527                                                  CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
528             CFNumberGetValue(number, kCFNumberSInt32Type, &value);
529             stats[kIDXNumWrites] = value;
530         }
531     }
532     /* we're done with the properties, release them */
533     CFRelease(properties);
534     return (0);
535 }
536
537 /*
538  * Check whether an IORegistryEntry refers to a valid
539  * I/O device, and if so, collect the information.
540  */
541 static int
542 handle_drive(io_registry_entry_t drive, struct drivestats * dstat)
543 {
544     io_registry_entry_t parent;
545     CFDictionaryRef     properties;
546     CFStringRef         name;
547     CFNumberRef         number;
548     kern_return_t       status;
549
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"); */
555         return(1);
556     }
557
558     if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
559
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"); */
566             return(1);
567         }
568
569         /* get BSD name and unitnumber from properties */
570         name = (CFStringRef)CFDictionaryGetValue(properties,
571                                           CFSTR(kIOBSDNameKey));
572         number = (CFNumberRef)CFDictionaryGetValue(properties,
573                                             CFSTR(kIOBSDUnitKey));
574
575         /* Collect stats and if succesful store them with the name and unitnumber */
576         if (!collect_drive_stats(parent, dstat->stats)) {
577
578             CFStringGetCString(name, dstat->name, MAXDRIVENAME, CFStringGetSystemEncoding());
579             CFNumberGetValue(number, kCFNumberSInt32Type, &dstat->bsd_unit_number);
580             num_drives++;
581         }
582
583         /* clean up, return success */
584         CFRelease(properties);
585         return(0);
586     }
587
588     /* failed, don't keep parent */
589     IOObjectRelease(parent);
590     return(1);
591 }
592
593 static int
594 getstats(void)
595 {
596     time_t                 now;
597     io_iterator_t          drivelist;
598     io_registry_entry_t    drive;
599     CFMutableDictionaryRef match;
600     kern_return_t          status;
601
602     now = time(NULL);   /* register current time and check wether cache can be used */
603     if (cache_time + CACHE_TIMEOUT > now) {
604         return 0;
605     }
606
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"); */
614         return(1);
615     }
616
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);
621     }
622     IOObjectRelease(drivelist);
623
624     cache_time = now;
625     return (0);
626 }
627
628 u_char         *
629 var_diskio(struct variable * vp,
630            oid * name,
631            size_t * length,
632            int exact, size_t * var_len, WriteMethod ** write_method)
633 {
634     static long     long_ret;
635     unsigned int    indx;
636
637     if (getstats() == 1) {
638         return NULL;
639     }
640
641
642     if (header_simple_table
643         (vp, name, length, exact, var_len, write_method, num_drives)) {
644         return NULL;
645     }
646
647     indx = (unsigned int) (name[*length - 1] - 1);
648
649     if (indx >= num_drives)
650         return NULL;
651
652     switch (vp->magic) {
653         case DISKIO_INDEX:
654             long_ret = (long) drivestat[indx].bsd_unit_number;
655             return (u_char *) & long_ret;
656         case DISKIO_DEVICE:
657             *var_len = strlen(drivestat[indx].name);
658             return (u_char *) drivestat[indx].name;
659         case DISKIO_NREAD:
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;
665         case DISKIO_READS:
666             long_ret = (signed long) drivestat[indx].stats[kIDXNumReads];
667             return (u_char *) & long_ret;
668         case DISKIO_WRITES:
669             long_ret = (signed long) drivestat[indx].stats[kIDXNumWrites];
670             return (u_char *) & long_ret;
671
672         default:
673             ERROR_MSG("diskio.c: don't know how to handle this request.");
674     }
675     return NULL;
676 }
677 #endif                          /* darwin */
678