ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.76
Committed: Sat Nov 6 15:33:40 2004 UTC (19 years, 6 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.75: +2 -2 lines
Log Message:
Fix IO bytes calculation -- the value returned is in 16-bit words, so
divide by two to get bytes.

File Contents

# Content
1 /*
2 * i-scream libstatgrab
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: disk_stats.c,v 1.75 2004/11/06 15:32:33 ats Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include "statgrab.h"
33 #include "vector.h"
34 #include "tools.h"
35
36 #ifdef SOLARIS
37 #include <sys/mnttab.h>
38 #include <sys/statvfs.h>
39 #include <kstat.h>
40 #define VALID_FS_TYPES {"ufs", "tmpfs", "vxfs"}
41 #endif
42
43 #if defined(LINUX) || defined(CYGWIN)
44 #include <mntent.h>
45 #include <sys/vfs.h>
46 #endif
47
48 #ifdef LINUX
49 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
50 "ext3", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
51 "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
52 "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
53 "umsdos", "vfat", "xfs", "jfs"}
54 #endif
55
56 #ifdef CYGWIN
57 #define VALID_FS_TYPES {"user"}
58 #endif
59
60 #ifdef ALLBSD
61 #include <sys/param.h>
62 #include <sys/ucred.h>
63 #include <sys/mount.h>
64 #endif
65 #if defined(FREEBSD) || defined(DFBSD)
66 #include <sys/dkstat.h>
67 #include <devstat.h>
68 #define VALID_FS_TYPES {"hpfs", "msdosfs", "ntfs", "udf", "ext2fs", \
69 "ufs", "mfs"}
70 #endif
71 #if defined(NETBSD) || defined(OPENBSD)
72 #include <sys/param.h>
73 #include <sys/sysctl.h>
74 #include <sys/disk.h>
75 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
76 "ntfs"}
77 #endif
78
79 #ifdef HPUX
80 #include <sys/param.h>
81 #include <sys/pstat.h>
82 #include <sys/types.h>
83 #include <sys/stat.h>
84 #include <sys/vfs.h>
85 #include <mntent.h>
86 #include <dirent.h>
87 #include <stdio.h>
88 #include <time.h>
89 #define VALID_FS_TYPES {"vxfs", "hfs"}
90 #define DISK_BATCH 30
91 #endif
92
93 static void disk_stat_init(sg_fs_stats *d) {
94 d->device_name = NULL;
95 d->fs_type = NULL;
96 d->mnt_point = NULL;
97 }
98
99 static void disk_stat_destroy(sg_fs_stats *d) {
100 free(d->device_name);
101 free(d->fs_type);
102 free(d->mnt_point);
103 }
104
105 static int is_valid_fs_type(const char *type) {
106 const char *types[] = VALID_FS_TYPES;
107 int i;
108
109 for (i = 0; i < (sizeof types / sizeof *types); i++) {
110 if (strcmp(types[i], type) == 0) {
111 return 1;
112 }
113 }
114 return 0;
115 }
116
117 sg_fs_stats *sg_get_fs_stats(int *entries){
118 VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10,
119 disk_stat_init, disk_stat_destroy);
120
121 int valid_type;
122 int num_disks=0;
123 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN) || defined(HPUX)
124 FILE *f;
125 #endif
126
127 sg_fs_stats *disk_ptr;
128
129 #ifdef SOLARIS
130 struct mnttab mp;
131 struct statvfs fs;
132 #endif
133 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
134 struct mntent *mp;
135 struct statfs fs;
136 #endif
137 #ifdef ALLBSD
138 int nummnt;
139 #ifdef HAVE_STATVFS
140 struct statvfs *mp;
141 #else
142 struct statfs *mp;
143 #endif
144 #endif
145
146 #ifdef ALLBSD
147 nummnt=getmntinfo(&mp , MNT_LOCAL);
148 if (nummnt<=0){
149 sg_set_error_with_errno(SG_ERROR_GETMNTINFO, NULL);
150 return NULL;
151 }
152 for(;nummnt--; mp++){
153 valid_type = is_valid_fs_type(mp->f_fstypename);
154 #endif
155
156 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
157 #ifdef MNT_MNTTAB
158 if ((f=setmntent(MNT_MNTTAB, "r" ))==NULL){
159 #else
160 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
161 #endif
162 sg_set_error(SG_ERROR_SETMNTENT, NULL);
163 return NULL;
164 }
165
166 while((mp=getmntent(f))){
167 if((statfs(mp->mnt_dir, &fs)) !=0){
168 continue;
169 }
170
171 valid_type = is_valid_fs_type(mp->mnt_type);
172 #endif
173
174 #ifdef SOLARIS
175 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
176 sg_set_error_with_errno(SG_ERROR_OPEN, "/etc/mnttab");
177 return NULL;
178 }
179 while((getmntent(f, &mp)) == 0){
180 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
181 continue;
182 }
183 valid_type = is_valid_fs_type(mp.mnt_fstype);
184 #endif
185
186 if(valid_type){
187 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
188 return NULL;
189 }
190 disk_ptr=disk_stats+num_disks;
191
192 #ifdef ALLBSD
193 if (sg_update_string(&disk_ptr->device_name, mp->f_mntfromname) < 0) {
194 return NULL;
195 }
196 if (sg_update_string(&disk_ptr->fs_type, mp->f_fstypename) < 0) {
197 return NULL;
198 }
199 if (sg_update_string(&disk_ptr->mnt_point, mp->f_mntonname) < 0) {
200 return NULL;
201 }
202
203 disk_ptr->size = (long long)mp->f_bsize * (long long) mp->f_blocks;
204 disk_ptr->avail = (long long)mp->f_bsize * (long long) mp->f_bavail;
205 disk_ptr->used = (disk_ptr->size) - ((long long)mp->f_bsize * (long long)mp->f_bfree);
206
207 disk_ptr->total_inodes=(long long)mp->f_files;
208 disk_ptr->free_inodes=(long long)mp->f_ffree;
209 /* Freebsd doesn't have a "available" inodes */
210 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
211 #endif
212 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
213 if (sg_update_string(&disk_ptr->device_name, mp->mnt_fsname) < 0) {
214 return NULL;
215 }
216
217 if (sg_update_string(&disk_ptr->fs_type, mp->mnt_type) < 0) {
218 return NULL;
219 }
220
221 if (sg_update_string(&disk_ptr->mnt_point, mp->mnt_dir) < 0) {
222 return NULL;
223 }
224 disk_ptr->size = (long long)fs.f_bsize * (long long)fs.f_blocks;
225 disk_ptr->avail = (long long)fs.f_bsize * (long long)fs.f_bavail;
226 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_bsize * (long long)fs.f_bfree);
227
228 disk_ptr->total_inodes=(long long)fs.f_files;
229 disk_ptr->free_inodes=(long long)fs.f_ffree;
230 /* Linux doesn't have a "available" inodes */
231 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
232 #endif
233
234 #ifdef SOLARIS
235 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end?
236 * Downside is its a bit hungry for a lot of mounts, as MNT_MAX_SIZE would prob
237 * be upwards of a k each
238 */
239 if (sg_update_string(&disk_ptr->device_name, mp.mnt_special) < 0) {
240 return NULL;
241 }
242
243 if (sg_update_string(&disk_ptr->fs_type, mp.mnt_fstype) < 0) {
244 return NULL;
245 }
246
247 if (sg_update_string(&disk_ptr->mnt_point, mp.mnt_mountp) < 0) {
248 return NULL;
249 }
250
251 disk_ptr->size = (long long)fs.f_frsize * (long long)fs.f_blocks;
252 disk_ptr->avail = (long long)fs.f_frsize * (long long)fs.f_bavail;
253 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_frsize * (long long)fs.f_bfree);
254
255 disk_ptr->total_inodes=(long long)fs.f_files;
256 disk_ptr->used_inodes=disk_ptr->total_inodes - (long long)fs.f_ffree;
257 disk_ptr->free_inodes=(long long)fs.f_favail;
258 #endif
259 num_disks++;
260 }
261 }
262
263 *entries=num_disks;
264
265 /* If this fails, there is very little i can do about it, so
266 I'll ignore it :) */
267 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
268 endmntent(f);
269 #endif
270 #if defined(SOLARIS)
271 fclose(f);
272 #endif
273
274 return disk_stats;
275
276 }
277
278 int sg_fs_compare_device_name(const void *va, const void *vb) {
279 const sg_fs_stats *a = (const sg_fs_stats *)va;
280 const sg_fs_stats *b = (const sg_fs_stats *)vb;
281
282 return strcmp(a->device_name, b->device_name);
283 }
284
285 int sg_fs_compare_mnt_point(const void *va, const void *vb) {
286 const sg_fs_stats *a = (const sg_fs_stats *)va;
287 const sg_fs_stats *b = (const sg_fs_stats *)vb;
288
289 return strcmp(a->mnt_point, b->mnt_point);
290 }
291
292 static void diskio_stat_init(sg_disk_io_stats *d) {
293 d->disk_name = NULL;
294 }
295
296 static void diskio_stat_destroy(sg_disk_io_stats *d) {
297 free(d->disk_name);
298 }
299
300 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
301 diskio_stat_init, diskio_stat_destroy);
302
303 #ifdef LINUX
304 typedef struct {
305 int major;
306 int minor;
307 } partition;
308 #endif
309
310 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
311 int num_diskio;
312 #ifndef LINUX
313 sg_disk_io_stats *diskio_stats_ptr;
314 #endif
315
316 #ifdef HPUX
317 long long rbytes = 0, wbytes = 0;
318 struct dirent *dinfo = NULL;
319 struct stat lstatinfo;
320 struct pst_diskinfo pstat_diskinfo[DISK_BATCH];
321 char fullpathbuf[1024] = {0};
322 dev_t diskid;
323 DIR *dh = NULL;
324 int diskidx = 0;
325 int num, i;
326 #endif
327 #ifdef SOLARIS
328 kstat_ctl_t *kc;
329 kstat_t *ksp;
330 kstat_io_t kios;
331 #endif
332 #ifdef LINUX
333 FILE *f;
334 char *line_ptr;
335 int major, minor;
336 int has_pp_stats = 1;
337 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
338 int i, n;
339 time_t now;
340 const char *format;
341 #endif
342 #if defined(FREEBSD) || defined(DFBSD)
343 static struct statinfo stats;
344 static int stats_init = 0;
345 int counter;
346 struct device_selection *dev_sel = NULL;
347 int n_selected, n_selections;
348 long sel_gen;
349 struct devstat *dev_ptr;
350 #endif
351 #ifdef NETBSD
352 struct disk_sysctl *stats;
353 #endif
354 #ifdef OPENBSD
355 int diskcount;
356 char *disknames, *name, *bufpp;
357 char **dk_name;
358 struct diskstats *stats;
359 #endif
360 #ifdef NETBSD
361 #define MIBSIZE 3
362 #endif
363 #ifdef OPENBSD
364 #define MIBSIZE 2
365 #endif
366 #if defined(NETBSD) || defined(OPENBSD)
367 int num_disks, i;
368 int mib[MIBSIZE];
369 size_t size;
370 #endif
371
372 num_diskio=0;
373
374 #ifdef HPUX
375 while (1) {
376 num = pstat_getdisk(pstat_diskinfo, sizeof pstat_diskinfo[0],
377 DISK_BATCH, diskidx);
378 if (num == -1) {
379 sg_set_error_with_errno(SG_ERROR_PSTAT,
380 "pstat_getdisk");
381 return NULL;
382 } else if (num == 0) {
383 break;
384 }
385
386 for (i = 0; i < num; i++) {
387 struct pst_diskinfo *di = &pstat_diskinfo[i];
388
389 /* Skip "disabled" disks. */
390 if (di->psd_status == 0) {
391 continue;
392 }
393
394 /* We can't seperate the reads from the writes, we'll
395 just give the same to each. */
396 rbytes = wbytes = di->psd_dkwds / 2;
397
398 /* Skip unused disks. */
399 if (rbytes == 0 && wbytes == 0) {
400 continue;
401 }
402
403 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
404 return NULL;
405 }
406
407 diskio_stats_ptr = diskio_stats + num_diskio;
408
409 diskio_stats_ptr->read_bytes = rbytes;
410 diskio_stats_ptr->write_bytes = wbytes;
411
412 diskio_stats_ptr->systime = time(NULL);
413
414 num_diskio++;
415
416 if (diskio_stats_ptr->disk_name == NULL) {
417 dh = opendir("/dev/dsk");
418 if (dh == NULL) {
419 continue;
420 }
421
422 diskid = (di->psd_dev.psd_major << 24) | di->psd_dev.psd_minor;
423 while (1) {
424 dinfo = readdir(dh);
425 if (dinfo == NULL) {
426 break;
427 }
428 snprintf(fullpathbuf, sizeof(fullpathbuf), "/dev/dsk/%s", dinfo->d_name);
429 if (lstat(fullpathbuf, &lstatinfo) < 0) {
430 continue;
431 }
432
433 if (lstatinfo.st_rdev == diskid) {
434 if (sg_update_string(&diskio_stats_ptr->disk_name, dinfo->d_name) < 0) {
435 return NULL;
436 }
437 break;
438 }
439 }
440 closedir(dh);
441
442 if (diskio_stats_ptr->disk_name == NULL) {
443 if (sg_update_string(&diskio_stats_ptr->disk_name, di->psd_hw_path.psh_name) < 0) {
444 return NULL;
445 }
446 }
447 }
448 }
449 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
450 }
451 #endif
452 #ifdef OPENBSD
453 mib[0] = CTL_HW;
454 mib[1] = HW_DISKCOUNT;
455
456 size = sizeof(diskcount);
457 if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
458 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
459 return NULL;
460 }
461
462 mib[0] = CTL_HW;
463 mib[1] = HW_DISKNAMES;
464
465 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
466 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
467 return NULL;
468 }
469
470 disknames = sg_malloc(size);
471 if (disknames == NULL) {
472 return NULL;
473 }
474
475 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
476 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
477 return NULL;
478 }
479
480 dk_name = sg_malloc(diskcount * sizeof(char *));
481 bufpp = disknames;
482 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
483 dk_name[i] = name;
484 }
485 #endif
486
487 #if defined(NETBSD) || defined(OPENBSD)
488 mib[0] = CTL_HW;
489 mib[1] = HW_DISKSTATS;
490 #ifdef NETBSD
491 mib[2] = sizeof(struct disk_sysctl);
492 #endif
493
494 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
495 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
496 return NULL;
497 }
498
499 #ifdef NETBSD
500 num_disks = size / sizeof(struct disk_sysctl);
501 #else
502 num_disks = size / sizeof(struct diskstats);
503 #endif
504
505 stats = sg_malloc(size);
506 if (stats == NULL) {
507 return NULL;
508 }
509
510 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
511 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
512 return NULL;
513 }
514
515 for (i = 0; i < num_disks; i++) {
516 const char *name;
517 u_int64_t rbytes, wbytes;
518
519 #ifdef NETBSD
520 #ifdef HAVE_DK_RBYTES
521 rbytes = stats[i].dk_rbytes;
522 wbytes = stats[i].dk_wbytes;
523 #else
524 /* Before 2.0, NetBSD merged reads and writes. */
525 rbytes = wbytes = stats[i].dk_bytes;
526 #endif
527 #else
528 #ifdef HAVE_DS_RBYTES
529 rbytes = stats[i].ds_rbytes;
530 wbytes = stats[i].ds_wbytes;
531 #else
532 /* Before 3.5, OpenBSD merged reads and writes */
533 rbytes = wbytes = stats[i].ds_bytes;
534 #endif
535 #endif
536
537 /* Don't keep stats for disks that have never been used. */
538 if (rbytes == 0 && wbytes == 0) {
539 continue;
540 }
541
542 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
543 return NULL;
544 }
545 diskio_stats_ptr = diskio_stats + num_diskio;
546
547 diskio_stats_ptr->read_bytes = rbytes;
548 diskio_stats_ptr->write_bytes = wbytes;
549 #ifdef NETBSD
550 name = stats[i].dk_name;
551 #else
552 name = dk_name[i];
553 #endif
554 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
555 return NULL;
556 }
557 diskio_stats_ptr->systime = time(NULL);
558
559 num_diskio++;
560 }
561
562 free(stats);
563 #ifdef OPENBSD
564 free(dk_name);
565 free(disknames);
566 #endif
567 #endif
568
569 #if defined(FREEBSD) || defined(DFBSD)
570 if (!stats_init) {
571 stats.dinfo=sg_malloc(sizeof(struct devinfo));
572 if(stats.dinfo==NULL) return NULL;
573 bzero(stats.dinfo, sizeof(struct devinfo));
574 stats_init = 1;
575 }
576 #ifdef FREEBSD5
577 if ((devstat_getdevs(NULL, &stats)) < 0) {
578 /* FIXME devstat functions return a string error in
579 devstat_errbuf */
580 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
581 return NULL;
582 }
583 /* Not aware of a get all devices, so i said 999. If we ever
584 * find a machine with more than 999 disks, then i'll change
585 * this number :)
586 */
587 if (devstat_selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
588 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
589 return NULL;
590 }
591 #else
592 if ((getdevs(&stats)) < 0) {
593 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
594 return NULL;
595 }
596 /* Not aware of a get all devices, so i said 999. If we ever
597 * find a machine with more than 999 disks, then i'll change
598 * this number :)
599 */
600 if (selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
601 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
602 return NULL;
603 }
604 #endif
605
606 for(counter=0;counter<stats.dinfo->numdevs;counter++){
607 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
608
609 /* Throw away devices that have done nothing, ever.. Eg "odd"
610 * devices.. like mem, proc.. and also doesn't report floppy
611 * drives etc unless they are doing stuff :)
612 */
613 #ifdef FREEBSD5
614 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
615 #else
616 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
617 #endif
618
619 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
620 return NULL;
621 }
622 diskio_stats_ptr=diskio_stats+num_diskio;
623
624 #ifdef FREEBSD5
625 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
626 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
627 #else
628 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
629 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
630 #endif
631 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
632 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
633 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
634 return NULL;
635 }
636 diskio_stats_ptr->systime=time(NULL);
637
638 num_diskio++;
639 }
640 free(dev_sel);
641
642 #endif
643 #ifdef SOLARIS
644 if ((kc = kstat_open()) == NULL) {
645 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
646 return NULL;
647 }
648
649 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
650 if (!strcmp(ksp->ks_class, "disk")) {
651
652 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
653 /* We dont want metadevices appearins as num_diskio */
654 if(strcmp(ksp->ks_module, "md")==0) continue;
655 if((kstat_read(kc, ksp, &kios))==-1){
656 }
657
658 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
659 kstat_close(kc);
660 return NULL;
661 }
662 diskio_stats_ptr=diskio_stats+num_diskio;
663
664 diskio_stats_ptr->read_bytes=kios.nread;
665 diskio_stats_ptr->write_bytes=kios.nwritten;
666 if (sg_update_string(&diskio_stats_ptr->disk_name,
667 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
668 return NULL;
669 }
670 diskio_stats_ptr->systime=time(NULL);
671
672 num_diskio++;
673 }
674 }
675
676 kstat_close(kc);
677 #endif
678
679 #ifdef LINUX
680 num_diskio = 0;
681 n = 0;
682
683 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
684 have statistics in here too, so we can use those directly.
685 2.6 kernels have /proc/diskstats instead with almost (but not quite)
686 the same format. */
687
688 f = fopen("/proc/diskstats", "r");
689 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
690 if (f == NULL) {
691 f = fopen("/proc/partitions", "r");
692 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
693 }
694 if (f == NULL) goto out;
695 now = time(NULL);
696
697 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
698 char name[100];
699 char *s;
700 long long rsect, wsect;
701
702 int nr = sscanf(line_ptr, format,
703 &major, &minor, name, &rsect, &wsect);
704 if (nr < 3) continue;
705
706 /* Skip device names ending in numbers, since they're
707 partitions. */
708 s = name;
709 while (*s != '\0') s++;
710 --s;
711 if (*s >= '0' && *s <= '9') continue;
712
713 if (nr < 5) {
714 has_pp_stats = 0;
715 rsect = 0;
716 wsect = 0;
717 }
718
719 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
720 goto out;
721 }
722 if (VECTOR_RESIZE(parts, n + 1) < 0) {
723 goto out;
724 }
725
726 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
727 goto out;
728 }
729 diskio_stats[n].read_bytes = rsect * 512;
730 diskio_stats[n].write_bytes = wsect * 512;
731 diskio_stats[n].systime = now;
732 parts[n].major = major;
733 parts[n].minor = minor;
734
735 n++;
736 }
737
738 fclose(f);
739 f = NULL;
740
741 if (!has_pp_stats) {
742 /* This is an older kernel where /proc/partitions doesn't
743 contain stats. Read what we can from /proc/stat instead, and
744 fill in the appropriate bits of the list allocated above. */
745
746 f = fopen("/proc/stat", "r");
747 if (f == NULL) goto out;
748 now = time(NULL);
749
750 line_ptr = sg_f_read_line(f, "disk_io:");
751 if (line_ptr == NULL) goto out;
752
753 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
754 long long rsect, wsect;
755
756 if (*++line_ptr == '\0') break;
757
758 if((sscanf(line_ptr,
759 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
760 &major, &minor, &rsect, &wsect)) != 4) {
761 continue;
762 }
763
764 /* Find the corresponding device from earlier.
765 Just to add to the fun, "minor" is actually the disk
766 number, not the device minor, so we need to figure
767 out the real minor number based on the major!
768 This list is not exhaustive; if you're running
769 an older kernel you probably don't have fancy
770 I2O hardware anyway... */
771 switch (major) {
772 case 3:
773 case 21:
774 case 22:
775 case 33:
776 case 34:
777 case 36:
778 case 56:
779 case 57:
780 case 88:
781 case 89:
782 case 90:
783 case 91:
784 minor *= 64;
785 break;
786 case 9:
787 case 43:
788 break;
789 default:
790 minor *= 16;
791 break;
792 }
793 for (i = 0; i < n; i++) {
794 if (major == parts[i].major
795 && minor == parts[i].minor)
796 break;
797 }
798 if (i == n) continue;
799
800 /* We read the number of blocks. Blocks are stored in
801 512 bytes */
802 diskio_stats[i].read_bytes = rsect * 512;
803 diskio_stats[i].write_bytes = wsect * 512;
804 diskio_stats[i].systime = now;
805 }
806 }
807
808 num_diskio = n;
809 out:
810 if (f != NULL) fclose(f);
811 #endif
812
813 #ifdef CYGWIN
814 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
815 return NULL;
816 #endif
817
818 *entries=num_diskio;
819
820 return diskio_stats;
821 }
822
823 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
824 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
825 diskio_stat_init, diskio_stat_destroy);
826 sg_disk_io_stats *src = NULL, *dest;
827 int i, j, diff_count, new_count;
828
829 if (diskio_stats == NULL) {
830 /* No previous stats, so we can't calculate a difference. */
831 return sg_get_disk_io_stats(entries);
832 }
833
834 /* Resize the results array to match the previous stats. */
835 diff_count = VECTOR_SIZE(diskio_stats);
836 if (VECTOR_RESIZE(diff, diff_count) < 0) {
837 return NULL;
838 }
839
840 /* Copy the previous stats into the result. */
841 for (i = 0; i < diff_count; i++) {
842 src = &diskio_stats[i];
843 dest = &diff[i];
844
845 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
846 return NULL;
847 }
848 dest->read_bytes = src->read_bytes;
849 dest->write_bytes = src->write_bytes;
850 dest->systime = src->systime;
851 }
852
853 /* Get a new set of stats. */
854 if (sg_get_disk_io_stats(&new_count) == NULL) {
855 return NULL;
856 }
857
858 /* For each previous stat... */
859 for (i = 0; i < diff_count; i++) {
860 dest = &diff[i];
861
862 /* ... find the corresponding new stat ... */
863 for (j = 0; j < new_count; j++) {
864 /* Try the new stat in the same position first,
865 since that's most likely to be it. */
866 src = &diskio_stats[(i + j) % new_count];
867 if (strcmp(src->disk_name, dest->disk_name) == 0) {
868 break;
869 }
870 }
871 if (j == new_count) {
872 /* No match found. */
873 continue;
874 }
875
876 /* ... and subtract the previous stat from it to get the
877 difference. */
878 dest->read_bytes = src->read_bytes - dest->read_bytes;
879 dest->write_bytes = src->write_bytes - dest->write_bytes;
880 dest->systime = src->systime - dest->systime;
881 }
882
883 *entries = diff_count;
884 return diff;
885 }
886
887 int sg_disk_io_compare_name(const void *va, const void *vb) {
888 const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
889 const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
890
891 return strcmp(a->disk_name, b->disk_name);
892 }
893