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