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
3  * that may apply:
4  */
5 /*
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.
10  */
11 
12 #include <net-snmp/net-snmp-config.h>
13 
14 /*
15  * needed by util_funcs.h
16  */
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27 
28 #include <math.h>
29 
30 #if defined (linux)
31 /* for stat() */
32 #include <ctype.h>
33 #include <sys/stat.h>
34 #endif
35 
36 #ifdef HAVE_SYS_SYSMACROS_H
37 #include <sys/sysmacros.h> /* major() */
38 #endif
39 
40 #include <net-snmp/net-snmp-includes.h>
41 #include <net-snmp/agent/net-snmp-agent-includes.h>
42 #include <net-snmp/agent/agent_callbacks.h>
43 
44 #include "util_funcs/header_simple_table.h"
45 
46 #include "struct.h"
47 /*
48  * include our .h file
49  */
50 #include "diskio.h"
51 
52 #define CACHE_TIMEOUT 1
53 static time_t   cache_time = 0;
54 
55 #ifdef solaris2
56 #include <kstat.h>
57 
58 #define MAX_DISKS 128
59 
60 static kstat_ctl_t *kc;
61 static kstat_t *ksp;
62 static kstat_io_t kio;
63 static int      cache_disknr = -1;
64 #endif                          /* solaris2 */
65 
66 #if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
67 /*
68  * handle disk statistics via libperfstat
69  */
70 #ifdef HAVE_SYS_PROTOSW_H
71 #include <sys/protosw.h>
72 #endif
73 #include <libperfstat.h>
74 static perfstat_disk_t *ps_disk;	/* storage for all disk values */
75 static int ps_numdisks;			/* number of disks in system, may change while running */
76 #endif
77 
78 #if defined(bsdi3) || defined(bsdi4) || defined(openbsd4)
79 #include <string.h>
80 #include <sys/param.h>
81 #include <sys/sysctl.h>
82 #ifdef openbsd4
83 #include <sys/disk.h>
84 #else
85 #include <sys/diskstats.h>
86 #endif
87 #endif                          /* bsdi */
88 
89 #if defined(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
90 #include <sys/param.h>
91 #if HAVE_DEVSTAT_GETDEVS
92 #include <sys/resource.h>       /* for CPUSTATES in devstat.h */
93 #elif HAVE_SYS_DKSTAT_H
94 #include <sys/dkstat.h>
95 #endif
96 #include <devstat.h>
97 #include <net-snmp/utilities.h>
98 
99 #include <math.h>
100 /* sampling interval, in seconds */
101 #define DISKIO_SAMPLE_INTERVAL 5
102 
103 #endif                          /* freebsd */
104 
105 #if HAVE_DEVSTAT_GETDEVS
106   #define GETDEVS(x) devstat_getdevs(NULL, (x))
107 #else
108   #define GETDEVS(x) getdevs((x))
109 #endif
110 
111 #if defined (linux)
112 #define DISKIO_SAMPLE_INTERVAL 5
113 void devla_getstats(unsigned int regno, void * dummy);
114 static void diskio_parse_config_disks(const char *token, char *cptr);
115 static int diskio_pre_update_config(int, int, void *, void *);
116 static void diskio_free_config(void);
117 
118 #define DISK_INCR 2
119 
120 typedef struct linux_diskio
121 {
122     int major;
123     int  minor;
124     unsigned long  blocks;
125     char name[256];
126     unsigned long  rio;
127     unsigned long  rmerge;
128     unsigned long  rsect;
129     unsigned long  ruse;
130     unsigned long  wio;
131     unsigned long  wmerge;
132     unsigned long  wsect;
133     unsigned long  wuse;
134     unsigned long  running;
135     unsigned long  use;
136     unsigned long  aveq;
137 } linux_diskio;
138 
139 /* disk load averages */
140 typedef struct linux_diskio_la
141 {
142     unsigned long use_prev;
143     double la1, la5, la15;
144 } linux_diskio_la;
145 
146 typedef struct linux_diskio_header
147 {
148     linux_diskio* indices;
149     int length;
150     int alloc;
151 } linux_diskio_header;
152 
153 typedef struct linux_diskio_la_header
154 {
155     linux_diskio_la * indices;
156     int length;
157 } linux_diskio_la_header;
158 
159 static linux_diskio_header head;
160 static linux_diskio_la_header la_head;
161 
162 #endif /* linux */
163 
164 #if defined (darwin)
165 #include <CoreFoundation/CoreFoundation.h>
166 #include <IOKit/IOKitLib.h>
167 #include <IOKit/storage/IOBlockStorageDriver.h>
168 #include <IOKit/storage/IOMedia.h>
169 #include <IOKit/IOBSD.h>
170 
171 static mach_port_t masterPort;		/* to communicate with I/O Kit	*/
172 #endif                          /* darwin */
173 
174 #if !defined(solaris2) && !(defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7))
175 static int      getstats(void);
176 #endif
177 
178 #if defined (HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
179 void		devla_getstats(unsigned int regno, void *dummy);
180 #endif
181 
182 #ifdef linux
183 struct diskiopart {
184     char            syspath[STRMAX];	/* full stat path */
185     char            name[STRMAX];	/* name as provided */
186     char            shortname[STRMAX];	/* short name for output */
187     int             major;
188     int             minor;
189 };
190 
191 static int             numdisks;
192 static int             maxdisks = 0;
193 static struct diskiopart *disks;
194 #endif
195 
196          /*********************
197 	 *
198 	 *  Initialisation & common implementation functions
199 	 *
200 	 *********************/
201 
202 
203 /*
204  * this is an optional function called at the time the agent starts up
205  * to do any initilizations you might require.  You don't have to
206  * create it, as it is optional.
207  */
208 
209 /*
210  * IMPORTANT: If you add or remove this function, you *must* re-run
211  * the configure script as it checks for its existance.
212  */
213 
214 void
init_diskio(void)215 init_diskio(void)
216 {
217     /*
218      * Define a 'variable' structure that is a representation of our mib.
219      */
220 
221     /*
222      * first, we have to pick the variable type.  They are all defined in
223      * the var_struct.h file in the agent subdirectory.  I'm picking the
224      * variable2 structure since the longest sub-component of the oid I
225      * want to load is .2.1 and .2.2 so I need at most 2 spaces in the
226      * last entry.
227      */
228 
229     struct variable2 diskio_variables[] = {
230         {DISKIO_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
231          var_diskio, 1, {1}},
232         {DISKIO_DEVICE, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
233          var_diskio, 1, {2}},
234         {DISKIO_NREAD, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
235          var_diskio, 1, {3}},
236         {DISKIO_NWRITTEN, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
237          var_diskio, 1, {4}},
238         {DISKIO_READS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
239          var_diskio, 1, {5}},
240         {DISKIO_WRITES, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
241          var_diskio, 1, {6}},
242 #if defined(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS) || defined(linux)
243         {DISKIO_LA1, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
244          var_diskio, 1, {9}},
245         {DISKIO_LA5, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
246          var_diskio, 1, {10}},
247         {DISKIO_LA15, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
248          var_diskio, 1, {11}},
249 #endif
250         {DISKIO_NREADX, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
251          var_diskio, 1, {12}},
252         {DISKIO_NWRITTENX, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
253          var_diskio, 1, {13}},
254         {DISKIO_BUSYTIME, ASN_COUNTER64, NETSNMP_OLDAPI_RONLY,
255          var_diskio, 1, {14}},
256     };
257 
258     /*
259      * Define the OID pointer to the top of the mib tree that we're
260      * registering underneath.
261      */
262     oid             diskio_variables_oid[] =
263         { 1, 3, 6, 1, 4, 1, 2021, 13, 15, 1, 1 };
264 
265     /*
266      * register ourselves with the agent to handle our mib tree
267      *
268      * This is a macro defined in ../../snmp_vars.h.  The arguments are:
269      *
270      * descr:   A short description of the mib group being loaded.
271      * var:     The variable structure to load.
272      * vartype: The variable structure used to define it (variable2, variable4, ...)
273      * theoid:  A *initialized* *exact length* oid pointer.
274      * (sizeof(theoid) *must* return the number of elements!)
275      */
276     REGISTER_MIB("diskio", diskio_variables, variable2,
277                  diskio_variables_oid);
278 
279 #ifdef solaris2
280     kc = kstat_open();
281 
282     if (kc == NULL)
283         snmp_log(LOG_ERR, "diskio: Couldn't open kstat\n");
284 #endif
285 
286 #ifdef darwin
287     /*
288      * Get the I/O Kit communication handle.
289      */
290     IOMasterPort(bootstrap_port, &masterPort);
291 #endif
292 
293 #if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
294     /*
295      * initialize values to gather information on first request
296      */
297     ps_numdisks = 0;
298     ps_disk = NULL;
299 #endif
300 
301 #if defined (HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS) || defined(linux)
302     devla_getstats(0, NULL);
303     /* collect LA data regularly */
304     snmp_alarm_register(DISKIO_SAMPLE_INTERVAL, SA_REPEAT, devla_getstats, NULL);
305 #endif
306 
307 
308 #ifdef linux
309     char *app = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
310                                       NETSNMP_DS_LIB_APPTYPE);
311     netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_fd",
312                                NETSNMP_DS_APPLICATION_ID,
313                                NETSNMP_DS_AGENT_DISKIO_NO_FD);
314     netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_loop",
315                                NETSNMP_DS_APPLICATION_ID,
316                                NETSNMP_DS_AGENT_DISKIO_NO_LOOP);
317     netsnmp_ds_register_config(ASN_BOOLEAN, app, "diskio_exclude_ram",
318                                NETSNMP_DS_APPLICATION_ID,
319                                NETSNMP_DS_AGENT_DISKIO_NO_RAM);
320 
321     snmpd_register_config_handler("diskio", diskio_parse_config_disks,
322         diskio_free_config, "path | device");
323 
324 
325     snmp_register_callback(SNMP_CALLBACK_APPLICATION,
326 	                   SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
327 	                   diskio_pre_update_config, NULL);
328 
329 #endif
330 }
331 
332 #ifdef linux
333 /* to do: make sure diskio_free_config() gets invoked upon SIGHUP. */
334 static int
diskio_pre_update_config(int major,int minor,void * serverarg,void * clientarg)335 diskio_pre_update_config(int major, int minor, void *serverarg, void *clientarg)
336 {
337     diskio_free_config();
338     return 0;
339 }
340 
341 static void
diskio_free_config(void)342 diskio_free_config(void)
343 {
344     int i;
345 
346     DEBUGMSGTL(("diskio", "free config %d\n",
347 		netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
348 				       NETSNMP_DS_AGENT_DISKIO_NO_RAM)));
349     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
350 			   NETSNMP_DS_AGENT_DISKIO_NO_FD,   0);
351     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
352 			   NETSNMP_DS_AGENT_DISKIO_NO_LOOP, 0);
353     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
354 			   NETSNMP_DS_AGENT_DISKIO_NO_RAM,  0);
355 
356     if (la_head.length) {
357         /* reset any usage stats, we may get different list of devices from config */
358         free(la_head.indices);
359         la_head.length = 0;
360         la_head.indices = NULL;
361     }
362     if (numdisks > 0) {
363         head.length = 0;
364         numdisks = 0;
365         for (i = 0; i < maxdisks; i++) {    /* init/erase disk db */
366             disks[i].syspath[0] = 0;
367             disks[i].name[0] = 0;
368             disks[i].shortname[0] = 0;
369             disks[i].major = -1;
370             disks[i].minor = -1;
371         }
372     }
373 }
374 
375 static int
disk_exists(char * path)376 disk_exists(char *path)
377 {
378     int index;
379     for(index = 0; index < numdisks; index++) {
380         DEBUGMSGTL(("ucd-snmp/disk", "Checking for %s. Found %s at %d\n", path, disks[index].syspath, index));
381         if(strcmp(path, disks[index].syspath) == 0) {
382             return index;
383         }
384     }
385     return -1;
386 }
387 
388 static void
add_device(char * path,int addNewDisks)389 add_device(char *path, int addNewDisks )
390 {
391     int index;
392     char device[STRMAX];
393     char syspath[STRMAX];
394     char *basename;
395     struct stat stbuf;
396 
397     if (!path || !strcmp(path, "none")) {
398         DEBUGMSGTL(("ucd-snmp/diskio", "Skipping null path device (%s)\n", path));
399         return;
400     }
401     if (numdisks == maxdisks) {
402         if (maxdisks == 0) {
403             maxdisks = 50;
404             disks = malloc(maxdisks * sizeof(struct diskiopart));
405             if (!disks) {
406                 config_perror("malloc failed for new disko allocation.");
407 	            netsnmp_config_error("\tignoring:  %s", path);
408                 return;
409             }
410             memset(disks, 0, maxdisks * sizeof(struct diskiopart));
411         } else {
412             struct diskiopart *newdisks;
413             maxdisks *= 2;
414             newdisks = realloc(disks, maxdisks * sizeof(struct diskiopart));
415             if (!newdisks) {
416                 free(disks);
417                 disks = NULL;
418                 config_perror("malloc failed for new disko allocation.");
419 	            netsnmp_config_error("\tignoring:  %s", path);
420                 return;
421             }
422             disks = newdisks;
423             memset(disks + maxdisks/2, 0, maxdisks/2 * sizeof(struct diskiopart));
424         }
425     }
426 
427     /* first find the path for this device */
428     device[0]='\0';
429     if ( *path != '/' ) {
430         strlcpy(device, "/dev/", STRMAX - 1 );
431     }
432     strncat(device, path, STRMAX - 1 );
433 
434     /* check for /dev existence */
435     if ( stat(device,&stbuf)!=0 ) { /* ENOENT */
436         config_perror("diskio path does not exist.");
437         netsnmp_config_error("\tignoring:  %s", path);
438         return;
439     }
440     else if ( ! S_ISBLK(stbuf.st_mode) ) { /* ENODEV */
441         config_perror("diskio path is not a device.");
442         netsnmp_config_error("\tignoring:  %s", path);
443         return;
444     }
445 
446     /* either came with a slash or we just put one there, so the following always works */
447     basename = strrchr(device, '/' )+1;
448     /* construct a sys path using the device numbers to avoid having to disambiguate the various text forms */
449     snprintf( syspath, STRMAX - 1, "/sys/dev/block/%d:%d/stat", major(stbuf.st_rdev), minor(stbuf.st_rdev) );
450     DEBUGMSGTL(("ucd-snmp/diskio", " monitoring sys path (%s)\n", syspath));
451 
452     index = disk_exists(syspath);
453 
454     if(index == -1 && addNewDisks){
455         /* The following buffers are cleared above, no need to add '\0' */
456         strlcpy(disks[numdisks].syspath, syspath, sizeof(disks[numdisks].syspath) - 1);
457         strlcpy(disks[numdisks].name, path, sizeof(disks[numdisks].name) - 1);
458         strlcpy(disks[numdisks].shortname, basename, sizeof(disks[numdisks].shortname) - 1);
459         disks[numdisks].major = major(stbuf.st_rdev);
460         disks[numdisks].minor = minor(stbuf.st_rdev);
461         numdisks++;
462     }
463 }
464 
465 static void
diskio_parse_config_disks(const char * token,char * cptr)466 diskio_parse_config_disks(const char *token, char *cptr)
467 {
468 #if HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS
469     char path[STRMAX];
470 
471 
472     /*
473      * read disk path (eg, /1 or /usr)
474      */
475     copy_nword(cptr, path, sizeof(path));
476 
477     /* TODO: we may include regular expressions in future */
478     /*
479      * check if the disk already exists, if so then modify its
480      * parameters. if it does not exist then add it
481      */
482     add_device(path, 1);
483 #endif /* HAVE_FSTAB_H || HAVE_GETMNTENT || HAVE_STATFS */
484 }
485 
486 #endif /* linux */
487 
488 
489 #ifdef solaris2
490 int
get_disk(int disknr)491 get_disk(int disknr)
492 {
493     time_t          now;
494     int             i = 0;
495     kstat_t *tksp;
496 
497     now = time(NULL);
498     if (disknr == cache_disknr && cache_time + CACHE_TIMEOUT > now) {
499         return 1;
500     }
501 
502     /*
503      * could be optimiced by checking if cache_disknr<=disknr
504      * if so, just reread the data - not going through the whole chain
505      * from kc->kc_chain
506      */
507 
508     for (tksp = kc->kc_chain; tksp != NULL; tksp = tksp->ks_next) {
509         if (tksp->ks_type == KSTAT_TYPE_IO
510             && !strcmp(tksp->ks_class, "disk")) {
511             if (i == disknr) {
512                 if (kstat_read(kc, tksp, &kio) == -1)
513                     snmp_log(LOG_ERR, "diskio: kstat_read failed\n");
514 		ksp = tksp;
515                 cache_time = now;
516                 cache_disknr = disknr;
517                 return 1;
518             } else {
519                 i++;
520             }
521         }
522     }
523     return 0;
524 }
525 
526 
527 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)528 var_diskio(struct variable * vp,
529            oid * name,
530            size_t * length,
531            int exact, size_t * var_len, WriteMethod ** write_method)
532 {
533     /*
534      * define any variables we might return as static!
535      */
536     static long     long_ret;
537     static struct counter64 c64_ret;
538 
539     if (header_simple_table
540         (vp, name, length, exact, var_len, write_method, MAX_DISKS))
541         return NULL;
542 
543 
544     if (get_disk(name[*length - 1] - 1) == 0)
545         return NULL;
546 
547 
548     /*
549      * We can now simply test on vp's magic number, defined in diskio.h
550      */
551     switch (vp->magic) {
552     case DISKIO_INDEX:
553         long_ret = (long) name[*length - 1];
554         return (u_char *) & long_ret;
555     case DISKIO_DEVICE:
556         *var_len = strlen(ksp->ks_name);
557         return (u_char *) ksp->ks_name;
558     case DISKIO_NREAD:
559         long_ret = (uint32_t) kio.nread;
560         return (u_char *) & long_ret;
561     case DISKIO_NWRITTEN:
562         long_ret = (uint32_t) kio.nwritten;
563         return (u_char *) & long_ret;
564     case DISKIO_NREADX:
565         *var_len = sizeof(struct counter64);
566         c64_ret.low = kio.nread & 0xffffffff;
567         c64_ret.high = kio.nread >> 32;
568         return (u_char *) & c64_ret;
569     case DISKIO_NWRITTENX:
570         *var_len = sizeof(struct counter64);
571         c64_ret.low = kio.nwritten & 0xffffffff;
572         c64_ret.high = kio.nwritten >> 32;
573         return (u_char *) & c64_ret;
574     case DISKIO_READS:
575         long_ret = (uint32_t) kio.reads;
576         return (u_char *) & long_ret;
577     case DISKIO_WRITES:
578         long_ret = (uint32_t) kio.writes;
579         return (u_char *) & long_ret;
580 
581     default:
582         ERROR_MSG("diskio.c: don't know how to handle this request.");
583     }
584     /*
585      * if we fall to here, fail by returning NULL
586      */
587     return NULL;
588 }
589 #endif                          /* solaris2 */
590 
591 #if defined(bsdi3) || defined(bsdi4)
592 static int      ndisk;
593 static struct diskstats *dk;
594 static char   **dkname;
595 
596 static int
getstats(void)597 getstats(void)
598 {
599     time_t          now;
600     int             mib[2];
601     char           *t, *tp;
602     size_t          size, dkn_size;
603     int             i;
604 
605     now = time(NULL);
606     if (cache_time + CACHE_TIMEOUT > now) {
607         return 1;
608     }
609     mib[0] = CTL_HW;
610     mib[1] = HW_DISKSTATS;
611     size = 0;
612     if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
613         perror("Can't get size of HW_DISKSTATS mib");
614         return 0;
615     }
616     if (ndisk != size / sizeof(*dk)) {
617         if (dk)
618             free(dk);
619         if (dkname) {
620             for (i = 0; i < ndisk; i++)
621                 if (dkname[i])
622                     free(dkname[i]);
623             free(dkname);
624         }
625         ndisk = size / sizeof(*dk);
626         if (ndisk == 0)
627             return 0;
628         dkname = malloc(ndisk * sizeof(char *));
629         mib[0] = CTL_HW;
630         mib[1] = HW_DISKNAMES;
631         if (sysctl(mib, 2, NULL, &dkn_size, NULL, 0) < 0) {
632             perror("Can't get size of HW_DISKNAMES mib");
633             return 0;
634         }
635         tp = t = malloc(dkn_size);
636         if (sysctl(mib, 2, t, &dkn_size, NULL, 0) < 0) {
637             perror("Can't get size of HW_DISKNAMES mib");
638             return 0;
639         }
640         for (i = 0; i < ndisk; i++) {
641             dkname[i] = strdup(tp);
642             tp += strlen(tp) + 1;
643         }
644         free(t);
645         dk = malloc(ndisk * sizeof(*dk));
646     }
647     mib[0] = CTL_HW;
648     mib[1] = HW_DISKSTATS;
649     if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
650         perror("Can't get HW_DISKSTATS mib");
651         return 0;
652     }
653     cache_time = now;
654     return 1;
655 }
656 
657 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)658 var_diskio(struct variable * vp,
659            oid * name,
660            size_t * length,
661            int exact, size_t * var_len, WriteMethod ** write_method)
662 {
663     static long     long_ret;
664     unsigned int    indx;
665 
666     if (getstats() == 0)
667         return 0;
668 
669     if (header_simple_table
670         (vp, name, length, exact, var_len, write_method, ndisk))
671         return NULL;
672 
673     indx = (unsigned int) (name[*length - 1] - 1);
674     if (indx >= ndisk)
675         return NULL;
676 
677     switch (vp->magic) {
678     case DISKIO_INDEX:
679         long_ret = (long) indx + 1;
680         return (u_char *) & long_ret;
681     case DISKIO_DEVICE:
682         *var_len = strlen(dkname[indx]);
683         return (u_char *) dkname[indx];
684     case DISKIO_NREAD:
685         long_ret =
686             (signed long) (dk[indx].dk_sectors * dk[indx].dk_secsize);
687         return (u_char *) & long_ret;
688     case DISKIO_NWRITTEN:
689         return NULL;            /* Sigh... BSD doesn't keep seperate track */
690     case DISKIO_READS:
691         long_ret = (signed long) dk[indx].dk_xfers;
692         return (u_char *) & long_ret;
693     case DISKIO_WRITES:
694         return NULL;            /* Sigh... BSD doesn't keep seperate track */
695 
696     default:
697         ERROR_MSG("diskio.c: don't know how to handle this request.");
698     }
699     return NULL;
700 }
701 #endif                          /* bsdi */
702 
703 #if defined(openbsd4)
704 static int      ndisk;
705 static struct diskstats *dk;
706 static char   **dkname;
707 
708 static int
getstats(void)709 getstats(void)
710 {
711     time_t          now;
712     int             mib[2];
713     char           *t, *tp,*te;
714     size_t          size, dkn_size;
715     int             i;
716 
717     now = time(NULL);
718     if (cache_time + CACHE_TIMEOUT > now) {
719         return 1;
720     }
721     mib[0] = CTL_HW;
722     mib[1] = HW_DISKSTATS;
723     size = 0;
724     if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) {
725         perror("Can't get size of HW_DISKSTATS mib");
726         return 0;
727     }
728     if (ndisk != size / sizeof(*dk)) {
729         if (dk)
730             free(dk);
731         if (dkname) {
732             for (i = 0; i < ndisk; i++)
733                 if (dkname[i])
734                     free(dkname[i]);
735             free(dkname);
736         }
737         ndisk = size / sizeof(*dk);
738         if (ndisk == 0)
739             return 0;
740         dkname = malloc(ndisk * sizeof(char *));
741         mib[0] = CTL_HW;
742         mib[1] = HW_DISKNAMES;
743         if (sysctl(mib, 2, NULL, &dkn_size, NULL, 0) < 0) {
744             perror("Can't get size of HW_DISKNAMES mib");
745             return 0;
746         }
747         te = tp = t = malloc(dkn_size);
748         if (sysctl(mib, 2, t, &dkn_size, NULL, 0) < 0) {
749             perror("Can't get size of HW_DISKNAMES mib");
750             return 0;
751         }
752         for (i = 0; i < ndisk; i++) {
753 	    while (te-t < dkn_size && *te != ',') te++;
754 	    *te++ = '\0';
755             dkname[i] = strdup(tp);
756             tp = te;
757         }
758         free(t);
759         dk = malloc(ndisk * sizeof(*dk));
760     }
761     mib[0] = CTL_HW;
762     mib[1] = HW_DISKSTATS;
763     if (sysctl(mib, 2, dk, &size, NULL, 0) < 0) {
764         perror("Can't get HW_DISKSTATS mib");
765         return 0;
766     }
767     cache_time = now;
768     return 1;
769 }
770 
771 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)772 var_diskio(struct variable * vp,
773            oid * name,
774            size_t * length,
775            int exact, size_t * var_len, WriteMethod ** write_method)
776 {
777     static long     long_ret;
778     static long long        longlong_ret;
779     static struct counter64 c64_ret;
780     unsigned int    indx;
781 
782     if (getstats() == 0)
783         return 0;
784 
785     if (header_simple_table
786         (vp, name, length, exact, var_len, write_method, ndisk))
787         return NULL;
788 
789     indx = (unsigned int) (name[*length - 1] - 1);
790     if (indx >= ndisk)
791         return NULL;
792 
793     switch (vp->magic) {
794     case DISKIO_INDEX:
795         long_ret = (long) indx + 1;
796         return (u_char *) & long_ret;
797     case DISKIO_DEVICE:
798         *var_len = strlen(dkname[indx]);
799         return (u_char *) dkname[indx];
800     case DISKIO_NREAD:
801         long_ret = (unsigned long) (dk[indx].ds_rbytes) & 0xffffffff;
802         return (u_char *) & long_ret;
803     case DISKIO_NWRITTEN:
804         long_ret = (unsigned long) (dk[indx].ds_wbytes) & 0xffffffff;
805         return (u_char *) & long_ret;
806     case DISKIO_READS:
807         long_ret = (unsigned long) dk[indx].ds_rxfer & 0xffffffff;
808         return (u_char *) & long_ret;
809     case DISKIO_WRITES:
810         long_ret = (unsigned long) dk[indx].ds_wxfer & 0xffffffff;
811         return (u_char *) & long_ret;
812     case DISKIO_NREADX:
813         *var_len = sizeof(struct counter64);
814         c64_ret.low = dk[indx].ds_rbytes & 0xffffffff;
815         c64_ret.high = dk[indx].ds_rbytes >> 32;
816         return (u_char *) & c64_ret;
817     case DISKIO_NWRITTENX:
818         *var_len = sizeof(struct counter64);
819         c64_ret.low = dk[indx].ds_rbytes & 0xffffffff;
820         c64_ret.high = dk[indx].ds_rbytes >> 32;
821         return (u_char *) & c64_ret;
822     case DISKIO_BUSYTIME:
823         *var_len = sizeof(struct counter64);
824 	longlong_ret = dk[indx].ds_time.tv_sec*1000000 + dk[indx].ds_time.tv_usec;
825         c64_ret.low = longlong_ret & 0xffffffff;
826         c64_ret.high = longlong_ret >> 32;
827 	return (u_char *) &c64_ret;
828     default:
829         ERROR_MSG("diskio.c: don't know how to handle this request.");
830     }
831     return NULL;
832 }
833 #endif                          /* openbsd */
834 
835 #ifdef __NetBSD__
836 #include <sys/sysctl.h>
837 static int      ndisk;
838 #ifdef HW_IOSTATNAMES
839 static int nmib[2] = {CTL_HW, HW_IOSTATNAMES};
840 #else
841 static int nmib[2] = {CTL_HW, HW_DISKNAMES};
842 #endif
843 #ifdef HW_DISKSTATS
844 #include <sys/disk.h>
845 static int dmib[3] = {CTL_HW, HW_DISKSTATS, sizeof(struct disk_sysctl)};
846 static struct disk_sysctl *dk;
847 #endif
848 #ifdef HW_IOSTATS
849 #include <sys/iostat.h>
850 static int dmib[3] = {CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl)};
851 static struct io_sysctl *dk;
852 #endif
853 static char   **dkname;
854 
855 static int
getstats(void)856 getstats(void)
857 {
858     time_t          now;
859     char           *t, *tp;
860     size_t          size, dkn_size;
861     int             i;
862 
863     now = time(NULL);
864     if (cache_time + CACHE_TIMEOUT > now) {
865         return 1;
866     }
867     size = 0;
868     if (sysctl(dmib, 3, NULL, &size, NULL, 0) < 0) {
869         perror("Can't get size of HW_DISKSTATS/HW_IOSTATS mib");
870         return 0;
871     }
872     if (ndisk != size / dmib[2]) {
873         if (dk)
874             free(dk);
875         if (dkname) {
876             for (i = 0; i < ndisk; i++)
877                 if (dkname[i])
878                     free(dkname[i]);
879             free(dkname);
880         }
881         ndisk = size / dmib[2];
882         if (ndisk == 0)
883             return 0;
884         dkname = malloc(ndisk * sizeof(char *));
885         dkn_size = 0;
886         if (sysctl(nmib, 2, NULL, &dkn_size, NULL, 0) < 0) {
887             perror("Can't get size of HW_DISKNAMES mib");
888             return 0;
889         }
890         t = malloc(dkn_size);
891         if (sysctl(nmib, 2, t, &dkn_size, NULL, 0) < 0) {
892             perror("Can't get size of HW_DISKNAMES mib");
893             return 0;
894         }
895         for (i = 0, tp = strtok(t, " "); tp && i < ndisk; i++,
896 	    tp = strtok(NULL, " ")) {
897             dkname[i] = strdup(tp);
898         }
899         free(t);
900         dk = malloc(ndisk * sizeof(*dk));
901     }
902     if (sysctl(dmib, 3, dk, &size, NULL, 0) < 0) {
903         perror("Can't get HW_DISKSTATS/HW_IOSTATS mib");
904         return 0;
905     }
906     cache_time = now;
907     return 1;
908 }
909 
910 u_char *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)911 var_diskio(struct variable * vp,
912            oid * name,
913            size_t * length,
914            int exact, size_t * var_len, WriteMethod ** write_method)
915 {
916     static long     long_ret;
917     static long long        longlong_ret;
918     static struct counter64 c64_ret;
919     unsigned int    indx;
920 
921     if (getstats() == 0)
922         return 0;
923 
924     if (header_simple_table
925         (vp, name, length, exact, var_len, write_method, ndisk))
926         return NULL;
927 
928     indx = (unsigned int) (name[*length - 1] - 1);
929     if (indx >= ndisk)
930         return NULL;
931 
932     switch (vp->magic) {
933     case DISKIO_INDEX:
934         long_ret = (long) indx + 1;
935         return (u_char *) & long_ret;
936 
937     case DISKIO_DEVICE:
938         *var_len = strlen(dkname[indx]);
939         return (u_char *) dkname[indx];
940 
941     case DISKIO_NREAD:
942 #ifdef HW_DISKSTATS
943      	long_ret = dk[indx].dk_rbytes;
944 #endif
945 #ifdef HW_IOSTATS
946 	if (dk[indx].type == IOSTAT_DISK)
947 	    long_ret = dk[indx].rbytes;
948 #endif
949         return (u_char *) & long_ret;
950 
951     case DISKIO_NWRITTEN:
952 #ifdef HW_DISKSTATS
953      	long_ret = dk[indx].dk_wbytes;
954 #endif
955 #ifdef HW_IOSTATS
956 	if (dk[indx].type == IOSTAT_DISK)
957 	    long_ret = dk[indx].wbytes;
958 #endif
959         return (u_char *) & long_ret;
960 
961     case DISKIO_NREADX:
962         *var_len = sizeof(struct counter64);
963         longlong_ret = dk[indx].rbytes;
964         c64_ret.low = longlong_ret & 0xffffffff;
965         c64_ret.high = longlong_ret >> 32;
966         return (u_char *) & c64_ret;
967 
968     case DISKIO_NWRITTENX:
969         *var_len = sizeof(struct counter64);
970         longlong_ret = dk[indx].wbytes;
971         c64_ret.low = longlong_ret & 0xffffffff;
972         c64_ret.high = longlong_ret >> 32;
973         return (u_char *) & c64_ret;
974 
975     case DISKIO_READS:
976 #ifdef HW_DISKSTATS
977      	long_ret = dk[indx].dk_rxfer;
978 #endif
979 #ifdef HW_IOSTATS
980 	if (dk[indx].type == IOSTAT_DISK)
981 	    long_ret = dk[indx].rxfer;
982 #endif
983         return (u_char *) & long_ret;
984 
985     case DISKIO_WRITES:
986 #ifdef HW_DISKSTATS
987      	long_ret = dk[indx].dk_wxfer;
988 #endif
989 #ifdef HW_IOSTATS
990 	if (dk[indx].type == IOSTAT_DISK)
991 	    long_ret = dk[indx].wxfer;
992 #endif
993         return (u_char *) & long_ret;
994 
995     case DISKIO_BUSYTIME:
996 #ifdef HW_IOSTATS
997         *var_len = sizeof(struct counter64);
998 	if (dk[indx].type == IOSTAT_DISK) {
999 	    longlong_ret = dk[indx].time_sec*1000 + dk[indx].time_usec/1000;
1000 	    c64_ret.low = longlong_ret & 0xffffffff;
1001 	    c64_ret.high = longlong_ret >> 32;
1002 	    return (u_char *) & c64_ret;
1003 	}
1004 	else
1005 	    return NULL;
1006 #else
1007 	return NULL;
1008 #endif
1009 
1010     default:
1011         ERROR_MSG("diskio.c: don't know how to handle this request.");
1012     }
1013     return NULL;
1014 }
1015 #endif /* __NetBSD__ */
1016 
1017 #if defined(HAVE_GETDEVS) || defined(HAVE_DEVSTAT_GETDEVS)
1018 
1019 /* disk load average patch by Rojer */
1020 
1021 struct dev_la {
1022 #if HAVE_DEVSTAT_GETDEVS
1023         struct bintime prev;
1024 #else
1025         struct timeval prev;
1026 #endif
1027         double la1,la5,la15;
1028         char name[DEVSTAT_NAME_LEN+5];
1029         };
1030 
1031 static struct dev_la *devloads = NULL;
1032 static int ndevs = 0;
1033 
1034 #if ! HAVE_DEVSTAT_GETDEVS
devla_timeval_diff(struct timeval * t1,struct timeval * t2)1035 double devla_timeval_diff(struct timeval *t1, struct timeval *t2) {
1036 
1037         double dt1 = (double) t1->tv_sec + (double) t1->tv_usec * 0.000001;
1038         double dt2 = (double) t2->tv_sec + (double) t2->tv_usec * 0.000001;
1039 
1040         return dt2-dt1;
1041 
1042         }
1043 #endif
1044 
devla_getstats(unsigned int regno,void * dummy)1045 void devla_getstats(unsigned int regno, void *dummy) {
1046 
1047         static struct statinfo *lastat = NULL;
1048         int i;
1049         double busy_time, busy_percent;
1050         static double expon1, expon5, expon15;
1051         char current_name[DEVSTAT_NAME_LEN+5];
1052 
1053 	if (lastat == NULL) {
1054 	    lastat = (struct statinfo *) malloc(sizeof(struct statinfo));
1055 	    if (lastat != NULL)
1056 		lastat->dinfo = (struct devinfo *) calloc(sizeof(struct devinfo), 1);
1057 	    if (lastat == NULL || lastat->dinfo == NULL) {
1058 		    SNMP_FREE(lastat);
1059 		    ERROR_MSG("Memory alloc failure - devla_getstats()\n");
1060 		    return;
1061 	    }
1062 	}
1063 
1064         if ((GETDEVS(lastat)) == -1) {
1065                 ERROR_MSG("can't do getdevs()\n");
1066                 return;
1067                 }
1068 
1069         if (ndevs != 0) {
1070                 for (i=0; i < ndevs; i++) {
1071                         snprintf(current_name, sizeof(current_name), "%s%d",
1072                                 lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number);
1073                         if (strcmp(current_name, devloads[i].name)) {
1074                                 ndevs = 0;
1075                                 free(devloads);
1076                                 }
1077                         }
1078                 }
1079 
1080         if (ndevs == 0) {
1081                 ndevs = lastat->dinfo->numdevs;
1082                 devloads = (struct dev_la *) malloc(ndevs * sizeof(struct dev_la));
1083                 memset(devloads, '\0', ndevs * sizeof(struct dev_la));
1084                 for (i=0; i < ndevs; i++) {
1085                         devloads[i].la1 = devloads[i].la5 = devloads[i].la15 = 0;
1086                         memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev));
1087                         snprintf(devloads[i].name, sizeof(devloads[i].name), "%s%d",
1088                                 lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number);
1089                         }
1090                 expon1  = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)60)));
1091                 expon5  = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)300)));
1092                 expon15 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)900)));
1093                 }
1094 
1095         for (i=0; i<ndevs; i++) {
1096 #if HAVE_DEVSTAT_GETDEVS
1097                 busy_time = devstat_compute_etime(&lastat->dinfo->devices[i].busy_time, &devloads[i].prev);
1098 #else
1099                 busy_time = devla_timeval_diff(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time);
1100 #endif
1101                 if ( busy_time < 0 )
1102                     busy_time = 0;   /* Account for possible FP loss of precision near zero */
1103                 busy_percent = busy_time * 100 / DISKIO_SAMPLE_INTERVAL;
1104                 devloads[i].la1 = devloads[i].la1 * expon1 + busy_percent * (1 - expon1);
1105 /*		fprintf(stderr, "(%d) %s: update la1=%.2lf%%\n", i, devloads[i].name, expon1); */
1106                 devloads[i].la5 = devloads[i].la5 * expon5 + busy_percent * (1 - expon5);
1107                 devloads[i].la15 = devloads[i].la15 * expon15 + busy_percent * (1 - expon15);
1108                 memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev));
1109                 }
1110 
1111         }
1112 
1113 /* end of disk LA patch */
1114 
1115 static int      ndisk;
1116 static struct statinfo *stat;
1117 FILE           *file;
1118 
1119 static int
getstats(void)1120 getstats(void)
1121 {
1122     time_t          now;
1123     int             i;
1124 
1125     now = time(NULL);
1126     if (cache_time + CACHE_TIMEOUT > now) {
1127         return 0;
1128     }
1129     if (stat == NULL) {
1130         stat = (struct statinfo *) malloc(sizeof(struct statinfo));
1131         if (stat != NULL)
1132             stat->dinfo = (struct devinfo *) calloc(sizeof(struct devinfo), 1);
1133         if (stat == NULL || stat->dinfo == NULL) {
1134 		SNMP_FREE(stat);
1135         	ERROR_MSG("Memory alloc failure - getstats\n");
1136 		return 1;
1137 	}
1138     }
1139 
1140     if (GETDEVS(stat) == -1) {
1141         fprintf(stderr, "Can't get devices:%s\n", devstat_errbuf);
1142         return 1;
1143     }
1144     ndisk = stat->dinfo->numdevs;
1145     /* Gross hack to include device numbers in the device name array */
1146     for (i = 0; i < ndisk; i++) {
1147       char *cp = stat->dinfo->devices[i].device_name;
1148       int len = strlen(cp);
1149       if (len > DEVSTAT_NAME_LEN - 3)
1150         len -= 3;
1151       cp += len;
1152       sprintf(cp, "%d", stat->dinfo->devices[i].unit_number);
1153     }
1154     cache_time = now;
1155     return 0;
1156 }
1157 
1158 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1159 var_diskio(struct variable * vp,
1160            oid * name,
1161            size_t * length,
1162            int exact, size_t * var_len, WriteMethod ** write_method)
1163 {
1164     static long     long_ret;
1165     static struct   counter64 c64_ret;
1166     long long       longlong_ret;
1167     unsigned int    indx;
1168 
1169     if (getstats() == 1) {
1170         return NULL;
1171     }
1172 
1173 
1174     if (header_simple_table
1175         (vp, name, length, exact, var_len, write_method, ndisk)) {
1176         return NULL;
1177     }
1178 
1179     indx = (unsigned int) (name[*length - 1] - 1);
1180 
1181     if (indx >= ndisk)
1182         return NULL;
1183 
1184     switch (vp->magic) {
1185     case DISKIO_INDEX:
1186         long_ret = (long) indx + 1;
1187         return (u_char *) & long_ret;
1188     case DISKIO_DEVICE:
1189         *var_len = strlen(stat->dinfo->devices[indx].device_name);
1190         return (u_char *) stat->dinfo->devices[indx].device_name;
1191     case DISKIO_NREAD:
1192 #if HAVE_DEVSTAT_GETDEVS
1193         long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_READ] & 0xFFFFFFFF;
1194 #else
1195         long_ret = (signed long) stat->dinfo->devices[indx].bytes_read;
1196 #endif
1197         return (u_char *) & long_ret;
1198     case DISKIO_NWRITTEN:
1199 #if HAVE_DEVSTAT_GETDEVS
1200         long_ret = (signed long) stat->dinfo->devices[indx].bytes[DEVSTAT_WRITE] & 0xFFFFFFFF;
1201 #else
1202         long_ret = (signed long) stat->dinfo->devices[indx].bytes_written;
1203 #endif
1204         return (u_char *) & long_ret;
1205     case DISKIO_NREADX:
1206         *var_len = sizeof(struct counter64);
1207 #if HAVE_DEVSTAT_GETDEVS
1208         longlong_ret = stat->dinfo->devices[indx].bytes[DEVSTAT_READ];
1209 #else
1210         longlong_ret = stat->dinfo->devices[indx].bytes_read;
1211 #endif
1212         c64_ret.low = longlong_ret & 0xffffffff;
1213         c64_ret.high = longlong_ret >> 32;
1214         return (u_char *) & c64_ret;
1215     case DISKIO_NWRITTENX:
1216         *var_len = sizeof(struct counter64);
1217 #if HAVE_DEVSTAT_GETDEVS
1218         longlong_ret = stat->dinfo->devices[indx].bytes[DEVSTAT_WRITE];
1219 #else
1220         longlong_ret = stat->dinfo->devices[indx].bytes_written;
1221 #endif
1222         c64_ret.low = longlong_ret & 0xffffffff;
1223         c64_ret.high = longlong_ret >> 32;
1224         return (u_char *) & c64_ret;
1225     case DISKIO_READS:
1226 #if HAVE_DEVSTAT_GETDEVS
1227         long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_READ] & 0xFFFFFFFF;
1228 #else
1229         long_ret = (signed long) stat->dinfo->devices[indx].num_reads;
1230 #endif
1231         return (u_char *) & long_ret;
1232     case DISKIO_WRITES:
1233 #if HAVE_DEVSTAT_GETDEVS
1234         long_ret = (signed long) stat->dinfo->devices[indx].operations[DEVSTAT_WRITE] & 0xFFFFFFFF;
1235 #else
1236         long_ret = (signed long) stat->dinfo->devices[indx].num_writes;
1237 #endif
1238         return (u_char *) & long_ret;
1239     case DISKIO_LA1:
1240 	long_ret = devloads[indx].la1;
1241 	return (u_char *) & long_ret;
1242     case DISKIO_LA5:
1243         long_ret = devloads[indx].la5;
1244         return (u_char *) & long_ret;
1245     case DISKIO_LA15:
1246         long_ret = devloads[indx].la15;
1247         return (u_char *) & long_ret;
1248 
1249     default:
1250         ERROR_MSG("diskio.c: don't know how to handle this request.");
1251     }
1252     return NULL;
1253 }
1254 #endif                          /* freebsd4 */
1255 
1256 
1257 #ifdef linux
1258 
1259 
devla_getstats(unsigned int regno,void * dummy)1260 void devla_getstats(unsigned int regno, void * dummy) {
1261 
1262     static double expon1, expon5, expon15;
1263     double busy_time, busy_percent;
1264     int idx;
1265 
1266     if (getstats() == 1) {
1267         ERROR_MSG("can't do diskio getstats()\n");
1268         return;
1269     }
1270 
1271     if (!la_head.length) {
1272         la_head.indices = (linux_diskio_la *) malloc(head.length * sizeof(linux_diskio_la));
1273         for (idx=0; idx<head.length; idx++) {
1274             la_head.indices[idx].la1 = la_head.indices[idx].la5 = la_head.indices[idx].la15 = 0.;
1275             la_head.indices[idx].use_prev = head.indices[idx].use;
1276         }
1277         la_head.length = head.length;
1278         expon1 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)60)));
1279         expon5 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)300)));
1280         expon15 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)900)));
1281     }
1282     else if (head.length - la_head.length) {
1283         la_head.indices = (linux_diskio_la *) realloc(la_head.indices, head.length * sizeof(linux_diskio_la));
1284         for (idx=la_head.length; idx<head.length; idx++) {
1285             la_head.indices[idx].la1 = la_head.indices[idx].la5 = la_head.indices[idx].la15 = 0.;
1286             la_head.indices[idx].use_prev = head.indices[idx].use;
1287         }
1288         la_head.length = head.length;
1289     }
1290 
1291     for (idx=0; idx<head.length; idx++) {
1292         busy_time = head.indices[idx].use - la_head.indices[idx].use_prev;
1293         busy_percent = busy_time * 100. / ((double) DISKIO_SAMPLE_INTERVAL) / 1000.;
1294         la_head.indices[idx].la1 = la_head.indices[idx].la1 * expon1 + busy_percent * (1. - expon1);
1295         la_head.indices[idx].la5 = la_head.indices[idx].la5 * expon5 + busy_percent * (1. - expon5);
1296         la_head.indices[idx].la15 = la_head.indices[idx].la15 * expon15 + busy_percent * (1. - expon15);
1297         /*
1298           fprintf(stderr, "(%d) update la1=%f la5=%f la15=%f\n",
1299           idx, la_head.indices[idx].la1, la_head.indices[idx].la5, la_head.indices[idx].la15);
1300         */
1301         la_head.indices[idx].use_prev = head.indices[idx].use;
1302     }
1303 }
1304 
is_excluded(const char * name)1305 int is_excluded(const char *name)
1306 {
1307     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1308                                NETSNMP_DS_AGENT_DISKIO_NO_FD)
1309                            && !(strncmp(name, "fd", 2)))
1310         return 1;
1311     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1312                                NETSNMP_DS_AGENT_DISKIO_NO_LOOP)
1313                            && !(strncmp(name, "loop", 4)))
1314         return 1;
1315     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1316                                NETSNMP_DS_AGENT_DISKIO_NO_RAM)
1317                            && !(strncmp(name, "ram", 3)))
1318         return 1;
1319     return 0;
1320 }
1321 
get_sysfs_stats(void)1322 static int get_sysfs_stats(void)
1323 {
1324     int i;
1325     char buffer[1024];
1326 
1327     head.length  = 0;
1328 
1329     for(i = 0; i < numdisks; i++) {
1330         FILE *f = fopen(disks[i].syspath, "r");
1331         if ( f == NULL ) {
1332             DEBUGMSGTL(("ucd-snmp/diskio", "Can't open %s, skipping", disks[i].syspath));
1333             continue;
1334         }
1335 
1336         if (fgets(buffer, sizeof(buffer), f) == NULL) {
1337             DEBUGMSGTL(("ucd-snmp/diskio", "Can't read %s, skipping", disks[i].syspath));
1338             fclose(f);
1339             continue;
1340         }
1341 
1342         linux_diskio* pTemp;
1343         if (head.length == head.alloc) {
1344             head.alloc += DISK_INCR;
1345             head.indices = (linux_diskio *) realloc(head.indices, head.alloc*sizeof(linux_diskio));
1346         }
1347         pTemp = &head.indices[head.length];
1348         pTemp->major = disks[i].major;
1349         pTemp->minor = disks[i].minor;
1350         strlcpy( pTemp->name, disks[i].shortname, sizeof(pTemp->name) - 1 );
1351         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",
1352                 &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
1353                 &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
1354                 &pTemp->running, &pTemp->use, &pTemp->aveq) != 11)
1355             sscanf (buffer, "%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu%*[ \n\t]%lu\n",
1356                 &pTemp->rio, &pTemp->rsect,
1357                 &pTemp->wio, &pTemp->wsect);
1358         head.length++;
1359         fclose(f);
1360     }
1361     return 0;
1362 }
1363 
1364 static int
getstats(void)1365 getstats(void)
1366 {
1367     struct stat stbuf;
1368     FILE* parts;
1369     time_t now;
1370 
1371     now = time(NULL);
1372     if (cache_time + CACHE_TIMEOUT > now) {
1373         return 0;
1374     }
1375 
1376     if (!head.indices) {
1377 	head.alloc = DISK_INCR;
1378 	head.indices = (linux_diskio *)malloc(head.alloc*sizeof(linux_diskio));
1379     }
1380     head.length  = 0;
1381 
1382     memset(head.indices, 0, head.alloc*sizeof(linux_diskio));
1383 
1384     if (numdisks>0) {
1385         /* 'diskio' configuration is used - go through the whitelist only and
1386          * read /sys/dev/block/xxx */
1387         cache_time = now;
1388         return get_sysfs_stats();
1389     }
1390     /* 'diskio' configuration is not used - report all devices */
1391     /* Is this a 2.6 kernel? */
1392     parts = fopen("/proc/diskstats", "r");
1393     if (parts) {
1394 	char buffer[1024];
1395 	while (fgets(buffer, sizeof(buffer), parts)) {
1396 	    linux_diskio* pTemp;
1397 	    if (head.length == head.alloc) {
1398 		head.alloc += DISK_INCR;
1399 		head.indices = (linux_diskio *)realloc(head.indices, head.alloc*sizeof(linux_diskio));
1400 	    }
1401 	    pTemp = &head.indices[head.length];
1402 	    sscanf (buffer, "%d %d", &pTemp->major, &pTemp->minor);
1403  	    if (sscanf (buffer, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
1404 		    &pTemp->major, &pTemp->minor, pTemp->name,
1405 		    &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
1406 		    &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
1407  		    &pTemp->running, &pTemp->use, &pTemp->aveq) != 14)
1408 		sscanf (buffer, "%d %d %s %lu %lu %lu %lu\n",
1409 		    &pTemp->major, &pTemp->minor, pTemp->name,
1410 		    &pTemp->rio, &pTemp->rsect,
1411 		    &pTemp->wio, &pTemp->wsect);
1412             if (!is_excluded(pTemp->name))
1413 	        head.length++;
1414 	}
1415     }
1416     else if (stat("/proc/vz", &stbuf) == 0) {
1417         // OpenVZ / Virtuozzo containers do not have /proc/diskstats
1418         goto update_cache_time;
1419     }
1420     else {
1421 	/* See if a 2.4 kernel */
1422 	char buffer[1024];
1423 	int rc;
1424 	parts = fopen("/proc/partitions", "r");
1425 	if (!parts) {
1426 	    snmp_log_perror("/proc/partitions");
1427 	    return 1;
1428 	}
1429 
1430 	/*
1431 	 * first few fscanfs are garbage we don't care about. skip it.
1432 	 */
1433 	NETSNMP_IGNORE_RESULT(fgets(buffer, sizeof(buffer), parts));
1434 	NETSNMP_IGNORE_RESULT(fgets(buffer, sizeof(buffer), parts));
1435 
1436 	while (! feof(parts)) {
1437 	    linux_diskio* pTemp;
1438 
1439 	    if (head.length == head.alloc) {
1440 		head.alloc += DISK_INCR;
1441 		head.indices = (linux_diskio *)realloc(head.indices, head.alloc*sizeof(linux_diskio));
1442 	    }
1443 	    pTemp = &head.indices[head.length];
1444 
1445 	    rc = fscanf(parts, "%d %d %lu %255s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
1446 		    &pTemp->major, &pTemp->minor, &pTemp->blocks, pTemp->name,
1447 		    &pTemp->rio, &pTemp->rmerge, &pTemp->rsect, &pTemp->ruse,
1448 		    &pTemp->wio, &pTemp->wmerge, &pTemp->wsect, &pTemp->wuse,
1449 		    &pTemp->running, &pTemp->use, &pTemp->aveq);
1450             if (rc != 15) {
1451                snmp_log(LOG_ERR, "diskio.c: cannot find statistics in /proc/partitions\n");
1452                fclose(parts);
1453                return 1;
1454             }
1455             if (!is_excluded(pTemp->name))
1456 	        head.length++;
1457 	}
1458     }
1459 
1460     fclose(parts);
1461 
1462 update_cache_time:
1463     cache_time = now;
1464     return 0;
1465 }
1466 
1467 u_char *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1468 var_diskio(struct variable * vp,
1469 	   oid * name,
1470 	   size_t * length,
1471 	   int exact,
1472 	   size_t * var_len,
1473 	   WriteMethod ** write_method)
1474 {
1475     unsigned int indx;
1476     static unsigned long long_ret;
1477     static struct counter64 c64_ret;
1478 
1479     if (getstats() == 1) {
1480 	return NULL;
1481     }
1482 
1483  if (header_simple_table(vp, name, length, exact, var_len, write_method, head.length))
1484     {
1485 	return NULL;
1486     }
1487 
1488   indx = (unsigned int) (name[*length - 1] - 1);
1489 
1490   if (indx >= head.length)
1491     return NULL;
1492 
1493   switch (vp->magic) {
1494     case DISKIO_INDEX:
1495       long_ret = indx+1;
1496       return (u_char *) &long_ret;
1497     case DISKIO_DEVICE:
1498       *var_len = strlen(head.indices[indx].name);
1499       return (u_char *) head.indices[indx].name;
1500     case DISKIO_NREAD:
1501       long_ret = (head.indices[indx].rsect*512) & 0xffffffff;
1502       return (u_char *) & long_ret;
1503     case DISKIO_NWRITTEN:
1504       long_ret = (head.indices[indx].wsect*512) & 0xffffffff;
1505       return (u_char *) & long_ret;
1506     case DISKIO_READS:
1507       long_ret = head.indices[indx].rio & 0xffffffff;
1508       return (u_char *) & long_ret;
1509     case DISKIO_WRITES:
1510       long_ret = head.indices[indx].wio & 0xffffffff;
1511       return (u_char *) & long_ret;
1512     case DISKIO_LA1:
1513       if (la_head.length > indx)
1514           long_ret = la_head.indices[indx].la1;
1515       else
1516           long_ret = 0; /* we don't have the load yet */
1517       return (u_char *) & long_ret;
1518     case DISKIO_LA5:
1519       if (la_head.length > indx)
1520           long_ret = la_head.indices[indx].la5;
1521       else
1522           long_ret = 0; /* we don't have the load yet */
1523       return (u_char *) & long_ret;
1524     case DISKIO_LA15:
1525       if (la_head.length > indx)
1526           long_ret = la_head.indices[indx].la15;
1527       else
1528           long_ret = 0;
1529       return (u_char *) & long_ret;
1530     case DISKIO_BUSYTIME:
1531       *var_len = sizeof(struct counter64);
1532       c64_ret.low = head.indices[indx].use*1000 & 0xffffffff;
1533       c64_ret.high = head.indices[indx].use*1000 >> 32;
1534       return (u_char *) & c64_ret;
1535     case DISKIO_NREADX:
1536       *var_len = sizeof(struct counter64);
1537       c64_ret.low = head.indices[indx].rsect * 512 & 0xffffffff;
1538       c64_ret.high = head.indices[indx].rsect >> (32 - 9);
1539       return (u_char *) & c64_ret;
1540     case DISKIO_NWRITTENX:
1541       *var_len = sizeof(struct counter64);
1542       c64_ret.low = head.indices[indx].wsect * 512 & 0xffffffff;
1543       c64_ret.high = head.indices[indx].wsect >> (32 - 9);
1544       return (u_char *) & c64_ret;
1545     default:
1546 	snmp_log(LOG_ERR, "don't know how to handle %d request\n", vp->magic);
1547   }
1548   return NULL;
1549 }
1550 #endif  /* linux */
1551 
1552 #if defined(darwin)
1553 
1554 #define MAXDRIVES	16	/* most drives we will record */
1555 #define MAXDRIVENAME	31	/* largest drive name we allow */
1556 
1557 #define kIDXBytesRead		0	/* used as index into the stats array in a drivestats struct */
1558 #define kIDXBytesWritten	1
1559 #define kIDXNumReads		2
1560 #define kIDXNumWrites		3
1561 #define kIDXBytesReadXhi	4
1562 #define kIDXBytesReadXlo	5
1563 #define kIDXBytesWrittenXhi	6
1564 #define kIDXBytesWrittenXlo	7
1565 #define kIDXLast		7
1566 
1567 struct drivestats {
1568     char name[MAXDRIVENAME + 1];
1569     long bsd_unit_number;
1570     long stats[kIDXLast+1];
1571 };
1572 
1573 static struct drivestats drivestat[MAXDRIVES];
1574 
1575 static mach_port_t masterPort;		/* to communicate with I/O Kit	*/
1576 
1577 static int num_drives;			/* number of drives detected	*/
1578 
1579 static int
collect_drive_stats(io_registry_entry_t driver,long * stats)1580 collect_drive_stats(io_registry_entry_t driver, long *stats)
1581 {
1582     CFNumberRef     number;
1583     CFDictionaryRef properties;
1584     CFDictionaryRef statistics;
1585     long            value;
1586     SInt64          value64;
1587     kern_return_t   status;
1588     int             i;
1589 
1590 
1591     /*
1592      * If the drive goes away, we may not get any properties
1593      * for it.  So take some defaults. Nb: use memset ??
1594      */
1595     for (i = 0; i < kIDXLast; i++) {
1596 	stats[i] = 0;
1597     }
1598 
1599     /* retrieve the properties */
1600     status = IORegistryEntryCreateCFProperties(driver, (CFMutableDictionaryRef *)&properties,
1601 					       kCFAllocatorDefault, kNilOptions);
1602     if (status != KERN_SUCCESS) {
1603 	snmp_log(LOG_ERR, "diskio: device has no properties\n");
1604 /*	fprintf(stderr, "device has no properties\n"); */
1605 	return (1);
1606     }
1607 
1608     /* retrieve statistics from properties */
1609     statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
1610 						       CFSTR(kIOBlockStorageDriverStatisticsKey));
1611     if (statistics) {
1612 
1613 	/* Now hand me the crystals. */
1614 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1615 						 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
1616 	    CFNumberGetValue(number, kCFNumberSInt32Type, &value);
1617 	    stats[kIDXBytesRead] = value;
1618 	}
1619 
1620 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1621 						 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
1622 	    CFNumberGetValue(number, kCFNumberSInt32Type, &value);
1623 	    stats[kIDXBytesWritten] = value;
1624 	}
1625 
1626 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1627 						 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
1628 	    CFNumberGetValue(number, kCFNumberSInt32Type, &value);
1629 	    stats[kIDXNumReads] = value;
1630 	}
1631 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1632 						 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
1633 	    CFNumberGetValue(number, kCFNumberSInt32Type, &value);
1634 	    stats[kIDXNumWrites] = value;
1635 	}
1636 	/* grab the 64 bit versions of the bytes read */
1637 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1638 						 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
1639 	    CFNumberGetValue(number, kCFNumberSInt64Type, &value64);
1640 	    stats[kIDXBytesReadXhi] = (long)(value64 >> 32);
1641 	    stats[kIDXBytesReadXlo] = (long)(value64 & 0xffffffff);
1642 	}
1643 
1644 	/* grab the 64 bit versions of the bytes written */
1645 	if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
1646 						 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
1647 	    CFNumberGetValue(number, kCFNumberSInt64Type, &value64);
1648 	    stats[kIDXBytesWrittenXhi] = (long)(value64 >> 32);
1649 	    stats[kIDXBytesWrittenXlo] = (long)(value64 & 0xffffffff);
1650 	}
1651     }
1652     /* we're done with the properties, release them */
1653     CFRelease(properties);
1654     return (0);
1655 }
1656 
1657 /*
1658  * Check whether an IORegistryEntry refers to a valid
1659  * I/O device, and if so, collect the information.
1660  */
1661 static int
handle_drive(io_registry_entry_t drive,struct drivestats * dstat)1662 handle_drive(io_registry_entry_t drive, struct drivestats * dstat)
1663 {
1664     io_registry_entry_t parent;
1665     CFMutableDictionaryRef     properties;
1666     CFStringRef         name;
1667     CFNumberRef         number;
1668     kern_return_t       status;
1669 
1670     /* get drive's parent */
1671     status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
1672     if (status != KERN_SUCCESS) {
1673 	snmp_log(LOG_ERR, "diskio: device has no parent\n");
1674 /*	fprintf(stderr, "device has no parent\n"); */
1675 	return(1);
1676     }
1677 
1678     if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
1679 
1680 	/* get drive properties */
1681 	status = IORegistryEntryCreateCFProperties(drive, &properties,
1682 					    kCFAllocatorDefault, kNilOptions);
1683 	if (status != KERN_SUCCESS) {
1684 	    snmp_log(LOG_ERR, "diskio: device has no properties\n");
1685 /*	    fprintf(stderr, "device has no properties\n"); */
1686 	    return(1);
1687 	}
1688 
1689 	/* get BSD name and unitnumber from properties */
1690 	name = (CFStringRef)CFDictionaryGetValue(properties,
1691 					  CFSTR(kIOBSDNameKey));
1692 	number = (CFNumberRef)CFDictionaryGetValue(properties,
1693 					    CFSTR(kIOBSDUnitKey));
1694 
1695 	/* Collect stats and if succesful store them with the name and unitnumber */
1696 	if (name && number && !collect_drive_stats(parent, dstat->stats)) {
1697 
1698 	    CFStringGetCString(name, dstat->name, MAXDRIVENAME, CFStringGetSystemEncoding());
1699 	    CFNumberGetValue(number, kCFNumberSInt32Type, &dstat->bsd_unit_number);
1700 	    num_drives++;
1701 	}
1702 
1703 	/* clean up, return success */
1704 	CFRelease(properties);
1705 	return(0);
1706     }
1707 
1708     /* failed, don't keep parent */
1709     IOObjectRelease(parent);
1710     return(1);
1711 }
1712 
1713 static int
getstats(void)1714 getstats(void)
1715 {
1716     time_t                 now;
1717     io_iterator_t          drivelist;
1718     io_registry_entry_t    drive;
1719     CFMutableDictionaryRef match;
1720     kern_return_t          status;
1721 
1722     now = time(NULL);	/* register current time and check wether cache can be used */
1723     if (cache_time + CACHE_TIMEOUT > now) {
1724         return 0;
1725     }
1726 
1727     /*  Retrieve a list of drives. */
1728     match = IOServiceMatching("IOMedia");
1729     CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
1730     status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
1731     if (status != KERN_SUCCESS) {
1732 	snmp_log(LOG_ERR, "diskio: couldn't match whole IOMedia devices\n");
1733 /*	fprintf(stderr,"Couldn't match whole IOMedia devices\n"); */
1734 	return -1;
1735     }
1736 
1737     num_drives = 0;  /* NB: Incremented by handle_drive */
1738     while ((drive = IOIteratorNext(drivelist)) && (num_drives < MAXDRIVES)) {
1739 	handle_drive(drive, &drivestat[num_drives]);
1740 	IOObjectRelease(drive);
1741     }
1742     IOObjectRelease(drivelist);
1743 
1744     cache_time = now;
1745     return 0;
1746 }
1747 
1748 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1749 var_diskio(struct variable * vp,
1750            oid * name,
1751            size_t * length,
1752            int exact, size_t * var_len, WriteMethod ** write_method)
1753 {
1754     static long     long_ret;
1755     static struct   counter64 c64_ret;
1756     unsigned int    indx;
1757 
1758     if (getstats() == 1) {
1759         return NULL;
1760     }
1761 
1762 
1763     if (header_simple_table
1764         (vp, name, length, exact, var_len, write_method, num_drives)) {
1765         return NULL;
1766     }
1767 
1768     indx = (unsigned int) (name[*length - 1] - 1);
1769 
1770     if (indx >= num_drives)
1771         return NULL;
1772 
1773     switch (vp->magic) {
1774 	case DISKIO_INDEX:
1775 	    long_ret = (long) drivestat[indx].bsd_unit_number;
1776 	    return (u_char *) & long_ret;
1777 	case DISKIO_DEVICE:
1778 	    *var_len = strlen(drivestat[indx].name);
1779 	    return (u_char *) drivestat[indx].name;
1780 	case DISKIO_NREAD:
1781 	    long_ret = (signed long) drivestat[indx].stats[kIDXBytesRead];
1782 	    return (u_char *) & long_ret;
1783 	case DISKIO_NWRITTEN:
1784 	    long_ret = (signed long) drivestat[indx].stats[kIDXBytesWritten];
1785 	    return (u_char *) & long_ret;
1786 	case DISKIO_READS:
1787 	    long_ret = (signed long) drivestat[indx].stats[kIDXNumReads];
1788 	    return (u_char *) & long_ret;
1789 	case DISKIO_WRITES:
1790 	    long_ret = (signed long) drivestat[indx].stats[kIDXNumWrites];
1791 	    return (u_char *) & long_ret;
1792 	case DISKIO_NREADX:
1793 	    *var_len = 8;
1794 	    c64_ret.low = (signed long) drivestat[indx].stats[kIDXBytesReadXlo];
1795 	    c64_ret.high = (signed long) drivestat[indx].stats[kIDXBytesReadXhi];
1796 	    return (u_char *) & c64_ret;
1797 	case DISKIO_NWRITTENX:
1798 	    *var_len = 8;
1799 	    c64_ret.low = (signed long) drivestat[indx].stats[kIDXBytesWrittenXlo];
1800 	    c64_ret.high = (signed long) drivestat[indx].stats[kIDXBytesWrittenXhi];
1801 	    return (u_char *) & c64_ret;
1802 	default:
1803 	    ERROR_MSG("diskio.c: don't know how to handle this request.");
1804     }
1805     return NULL;
1806 }
1807 #endif                          /* darwin */
1808 
1809 
1810 #if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)
1811 /*
1812  * collect statistics for all disks
1813  */
1814 int
collect_disks(void)1815 collect_disks(void)
1816 {
1817     time_t          now;
1818     int             i;
1819     perfstat_id_t   first;
1820 
1821     /* cache valid? if yes, just return */
1822     now = time(NULL);
1823     if (ps_disk != NULL && cache_time + CACHE_TIMEOUT > now) {
1824         return 0;
1825     }
1826 
1827     /* get number of disks we have */
1828     i = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
1829     if(i <= 0) return 1;
1830 
1831     /* if number of disks differs or structures are uninitialized, init them */
1832     if(i != ps_numdisks || ps_disk == NULL) {
1833         if(ps_disk != NULL) free(ps_disk);
1834         ps_numdisks = i;
1835         ps_disk = malloc(sizeof(perfstat_disk_t) * ps_numdisks);
1836         if(ps_disk == NULL) return 1;
1837     }
1838 
1839     /* gather statistics about all disks we have */
1840     strcpy(first.name, "");
1841     i = perfstat_disk(&first, ps_disk, sizeof(perfstat_disk_t), ps_numdisks);
1842     if(i != ps_numdisks) return 1;
1843 
1844     cache_time = now;
1845     return 0;
1846 }
1847 
1848 
1849 u_char         *
var_diskio(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1850 var_diskio(struct variable * vp,
1851            oid * name,
1852            size_t * length,
1853            int exact, size_t * var_len, WriteMethod ** write_method)
1854 {
1855     static long     long_ret;
1856     static struct counter64 c64_ret;
1857     unsigned int    indx;
1858 
1859     /* get disk statistics */
1860     if (collect_disks())
1861         return NULL;
1862 
1863     if (header_simple_table
1864         (vp, name, length, exact, var_len, write_method, ps_numdisks))
1865         return NULL;
1866 
1867     indx = (unsigned int) (name[*length - 1] - 1);
1868     if (indx >= ps_numdisks)
1869         return NULL;
1870 
1871     /* deliver requested data on requested disk */
1872     switch (vp->magic) {
1873     case DISKIO_INDEX:
1874         long_ret = (long) indx;
1875         return (u_char *) & long_ret;
1876     case DISKIO_DEVICE:
1877         *var_len = strlen(ps_disk[indx].name);
1878         return (u_char *) ps_disk[indx].name;
1879     case DISKIO_NREAD:
1880         long_ret = (signed long) ps_disk[indx].rblks * ps_disk[indx].bsize;
1881         return (u_char *) & long_ret;
1882     case DISKIO_NWRITTEN:
1883         long_ret = (signed long) ps_disk[indx].wblks * ps_disk[indx].bsize;
1884         return (u_char *) & long_ret;
1885     case DISKIO_READS:
1886         long_ret = (signed long) ps_disk[indx].xfers;
1887         return (u_char *) & long_ret;
1888     case DISKIO_WRITES:
1889         long_ret = (signed long) 0;	/* AIX has just one value for read/write transfers */
1890         return (u_char *) & long_ret;
1891     case DISKIO_NREADX:
1892         *var_len = sizeof(struct counter64);
1893         c64_ret.low = (ps_disk[indx].rblks * ps_disk[indx].bsize) & 0xffffffff;
1894         c64_ret.high = (ps_disk[indx].rblks * ps_disk[indx].bsize) >> 32;
1895         return (u_char *) & c64_ret;
1896     case DISKIO_NWRITTENX:
1897         *var_len = sizeof(struct counter64);
1898         c64_ret.low = (ps_disk[indx].wblks * ps_disk[indx].bsize) & 0xffffffff;
1899         c64_ret.high = (ps_disk[indx].wblks * ps_disk[indx].bsize) >> 32;
1900         return (u_char *) & c64_ret;
1901 
1902     default:
1903         ERROR_MSG("diskio.c: don't know how to handle this request.");
1904     }
1905 
1906     /* return NULL in case of error */
1907     return NULL;
1908 }
1909 #endif                          /* aix 4/5 */
1910