ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.80
Committed: Thu Feb 24 12:34:45 2005 UTC (19 years, 2 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_11_1
Changes since 1.79: +5 -5 lines
Log Message:
Add NFS to list of file systems that we return information for.

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.79 2004/11/07 15:06:49 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", "nfs"}
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", "nfs"}
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", "nfs"}
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 < (int) (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_WAIT);
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. (This value is in
396 * 64-byte chunks according to the pstat header file,
397 * and can wrap to be negative.)
398 */
399 rbytes = wbytes = ((unsigned long) di->psd_dkwds) * 64LL;
400
401 /* Skip unused disks. */
402 if (rbytes == 0 && wbytes == 0) {
403 continue;
404 }
405
406 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
407 return NULL;
408 }
409
410 diskio_stats_ptr = diskio_stats + num_diskio;
411
412 diskio_stats_ptr->read_bytes = rbytes;
413 diskio_stats_ptr->write_bytes = wbytes;
414
415 diskio_stats_ptr->systime = time(NULL);
416
417 num_diskio++;
418
419 /* FIXME This should use a static cache, like the Linux
420 * code below. */
421 if (diskio_stats_ptr->disk_name == NULL) {
422 dh = opendir("/dev/dsk");
423 if (dh == NULL) {
424 continue;
425 }
426
427 diskid = (di->psd_dev.psd_major << 24) | di->psd_dev.psd_minor;
428 while (1) {
429 dinfo = readdir(dh);
430 if (dinfo == NULL) {
431 break;
432 }
433 snprintf(fullpathbuf, sizeof(fullpathbuf), "/dev/dsk/%s", dinfo->d_name);
434 if (lstat(fullpathbuf, &lstatinfo) < 0) {
435 continue;
436 }
437
438 if (lstatinfo.st_rdev == diskid) {
439 if (sg_update_string(&diskio_stats_ptr->disk_name, dinfo->d_name) < 0) {
440 return NULL;
441 }
442 break;
443 }
444 }
445 closedir(dh);
446
447 if (diskio_stats_ptr->disk_name == NULL) {
448 if (sg_update_string(&diskio_stats_ptr->disk_name, di->psd_hw_path.psh_name) < 0) {
449 return NULL;
450 }
451 }
452 }
453 }
454 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
455 }
456 #endif
457 #ifdef OPENBSD
458 mib[0] = CTL_HW;
459 mib[1] = HW_DISKCOUNT;
460
461 size = sizeof(diskcount);
462 if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
463 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
464 return NULL;
465 }
466
467 mib[0] = CTL_HW;
468 mib[1] = HW_DISKNAMES;
469
470 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
471 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
472 return NULL;
473 }
474
475 disknames = sg_malloc(size);
476 if (disknames == NULL) {
477 return NULL;
478 }
479
480 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
481 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
482 return NULL;
483 }
484
485 dk_name = sg_malloc(diskcount * sizeof(char *));
486 bufpp = disknames;
487 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
488 dk_name[i] = name;
489 }
490 #endif
491
492 #if defined(NETBSD) || defined(OPENBSD)
493 mib[0] = CTL_HW;
494 mib[1] = HW_DISKSTATS;
495 #ifdef NETBSD
496 mib[2] = sizeof(struct disk_sysctl);
497 #endif
498
499 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
500 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
501 return NULL;
502 }
503
504 #ifdef NETBSD
505 num_disks = size / sizeof(struct disk_sysctl);
506 #else
507 num_disks = size / sizeof(struct diskstats);
508 #endif
509
510 stats = sg_malloc(size);
511 if (stats == NULL) {
512 return NULL;
513 }
514
515 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
516 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
517 return NULL;
518 }
519
520 for (i = 0; i < num_disks; i++) {
521 const char *name;
522 u_int64_t rbytes, wbytes;
523
524 #ifdef NETBSD
525 #ifdef HAVE_DK_RBYTES
526 rbytes = stats[i].dk_rbytes;
527 wbytes = stats[i].dk_wbytes;
528 #else
529 /* Before 2.0, NetBSD merged reads and writes. */
530 rbytes = wbytes = stats[i].dk_bytes;
531 #endif
532 #else
533 #ifdef HAVE_DS_RBYTES
534 rbytes = stats[i].ds_rbytes;
535 wbytes = stats[i].ds_wbytes;
536 #else
537 /* Before 3.5, OpenBSD merged reads and writes */
538 rbytes = wbytes = stats[i].ds_bytes;
539 #endif
540 #endif
541
542 /* Don't keep stats for disks that have never been used. */
543 if (rbytes == 0 && wbytes == 0) {
544 continue;
545 }
546
547 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
548 return NULL;
549 }
550 diskio_stats_ptr = diskio_stats + num_diskio;
551
552 diskio_stats_ptr->read_bytes = rbytes;
553 diskio_stats_ptr->write_bytes = wbytes;
554 #ifdef NETBSD
555 name = stats[i].dk_name;
556 #else
557 name = dk_name[i];
558 #endif
559 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
560 return NULL;
561 }
562 diskio_stats_ptr->systime = time(NULL);
563
564 num_diskio++;
565 }
566
567 free(stats);
568 #ifdef OPENBSD
569 free(dk_name);
570 free(disknames);
571 #endif
572 #endif
573
574 #if defined(FREEBSD) || defined(DFBSD)
575 if (!stats_init) {
576 stats.dinfo=sg_malloc(sizeof(struct devinfo));
577 if(stats.dinfo==NULL) return NULL;
578 bzero(stats.dinfo, sizeof(struct devinfo));
579 stats_init = 1;
580 }
581 #ifdef FREEBSD5
582 if ((devstat_getdevs(NULL, &stats)) < 0) {
583 /* FIXME devstat functions return a string error in
584 devstat_errbuf */
585 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
586 return NULL;
587 }
588 /* Not aware of a get all devices, so i said 999. If we ever
589 * find a machine with more than 999 disks, then i'll change
590 * this number :)
591 */
592 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) {
593 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
594 return NULL;
595 }
596 #else
597 if ((getdevs(&stats)) < 0) {
598 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
599 return NULL;
600 }
601 /* Not aware of a get all devices, so i said 999. If we ever
602 * find a machine with more than 999 disks, then i'll change
603 * this number :)
604 */
605 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) {
606 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
607 return NULL;
608 }
609 #endif
610
611 for(counter=0;counter<stats.dinfo->numdevs;counter++){
612 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
613
614 /* Throw away devices that have done nothing, ever.. Eg "odd"
615 * devices.. like mem, proc.. and also doesn't report floppy
616 * drives etc unless they are doing stuff :)
617 */
618 #ifdef FREEBSD5
619 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
620 #else
621 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
622 #endif
623
624 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
625 return NULL;
626 }
627 diskio_stats_ptr=diskio_stats+num_diskio;
628
629 #ifdef FREEBSD5
630 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
631 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
632 #else
633 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
634 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
635 #endif
636 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
637 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
638 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
639 return NULL;
640 }
641 diskio_stats_ptr->systime=time(NULL);
642
643 num_diskio++;
644 }
645 free(dev_sel);
646
647 #endif
648 #ifdef SOLARIS
649 if ((kc = kstat_open()) == NULL) {
650 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
651 return NULL;
652 }
653
654 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
655 if (!strcmp(ksp->ks_class, "disk")) {
656
657 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
658 /* We dont want metadevices appearins as num_diskio */
659 if(strcmp(ksp->ks_module, "md")==0) continue;
660 if((kstat_read(kc, ksp, &kios))==-1){
661 }
662
663 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
664 kstat_close(kc);
665 return NULL;
666 }
667 diskio_stats_ptr=diskio_stats+num_diskio;
668
669 diskio_stats_ptr->read_bytes=kios.nread;
670 diskio_stats_ptr->write_bytes=kios.nwritten;
671 if (sg_update_string(&diskio_stats_ptr->disk_name,
672 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
673 return NULL;
674 }
675 diskio_stats_ptr->systime=time(NULL);
676
677 num_diskio++;
678 }
679 }
680
681 kstat_close(kc);
682 #endif
683
684 #ifdef LINUX
685 num_diskio = 0;
686 n = 0;
687
688 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
689 have statistics in here too, so we can use those directly.
690 2.6 kernels have /proc/diskstats instead with almost (but not quite)
691 the same format. */
692
693 f = fopen("/proc/diskstats", "r");
694 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
695 if (f == NULL) {
696 f = fopen("/proc/partitions", "r");
697 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
698 }
699 if (f == NULL) goto out;
700 now = time(NULL);
701
702 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
703 char name[100];
704 char *s;
705 long long rsect, wsect;
706
707 int nr = sscanf(line_ptr, format,
708 &major, &minor, name, &rsect, &wsect);
709 if (nr < 3) continue;
710
711 /* Skip device names ending in numbers, since they're
712 partitions. */
713 s = name;
714 while (*s != '\0') s++;
715 --s;
716 if (*s >= '0' && *s <= '9') continue;
717
718 if (nr < 5) {
719 has_pp_stats = 0;
720 rsect = 0;
721 wsect = 0;
722 }
723
724 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
725 goto out;
726 }
727 if (VECTOR_RESIZE(parts, n + 1) < 0) {
728 goto out;
729 }
730
731 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
732 goto out;
733 }
734 diskio_stats[n].read_bytes = rsect * 512;
735 diskio_stats[n].write_bytes = wsect * 512;
736 diskio_stats[n].systime = now;
737 parts[n].major = major;
738 parts[n].minor = minor;
739
740 n++;
741 }
742
743 fclose(f);
744 f = NULL;
745
746 if (!has_pp_stats) {
747 /* This is an older kernel where /proc/partitions doesn't
748 contain stats. Read what we can from /proc/stat instead, and
749 fill in the appropriate bits of the list allocated above. */
750
751 f = fopen("/proc/stat", "r");
752 if (f == NULL) goto out;
753 now = time(NULL);
754
755 line_ptr = sg_f_read_line(f, "disk_io:");
756 if (line_ptr == NULL) goto out;
757
758 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
759 long long rsect, wsect;
760
761 if (*++line_ptr == '\0') break;
762
763 if((sscanf(line_ptr,
764 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
765 &major, &minor, &rsect, &wsect)) != 4) {
766 continue;
767 }
768
769 /* Find the corresponding device from earlier.
770 Just to add to the fun, "minor" is actually the disk
771 number, not the device minor, so we need to figure
772 out the real minor number based on the major!
773 This list is not exhaustive; if you're running
774 an older kernel you probably don't have fancy
775 I2O hardware anyway... */
776 switch (major) {
777 case 3:
778 case 21:
779 case 22:
780 case 33:
781 case 34:
782 case 36:
783 case 56:
784 case 57:
785 case 88:
786 case 89:
787 case 90:
788 case 91:
789 minor *= 64;
790 break;
791 case 9:
792 case 43:
793 break;
794 default:
795 minor *= 16;
796 break;
797 }
798 for (i = 0; i < n; i++) {
799 if (major == parts[i].major
800 && minor == parts[i].minor)
801 break;
802 }
803 if (i == n) continue;
804
805 /* We read the number of blocks. Blocks are stored in
806 512 bytes */
807 diskio_stats[i].read_bytes = rsect * 512;
808 diskio_stats[i].write_bytes = wsect * 512;
809 diskio_stats[i].systime = now;
810 }
811 }
812
813 num_diskio = n;
814 out:
815 if (f != NULL) fclose(f);
816 #endif
817
818 #ifdef CYGWIN
819 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
820 return NULL;
821 #endif
822
823 *entries=num_diskio;
824
825 return diskio_stats;
826 }
827
828 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
829 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
830 diskio_stat_init, diskio_stat_destroy);
831 sg_disk_io_stats *src = NULL, *dest;
832 int i, j, diff_count, new_count;
833
834 if (diskio_stats == NULL) {
835 /* No previous stats, so we can't calculate a difference. */
836 return sg_get_disk_io_stats(entries);
837 }
838
839 /* Resize the results array to match the previous stats. */
840 diff_count = VECTOR_SIZE(diskio_stats);
841 if (VECTOR_RESIZE(diff, diff_count) < 0) {
842 return NULL;
843 }
844
845 /* Copy the previous stats into the result. */
846 for (i = 0; i < diff_count; i++) {
847 src = &diskio_stats[i];
848 dest = &diff[i];
849
850 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
851 return NULL;
852 }
853 dest->read_bytes = src->read_bytes;
854 dest->write_bytes = src->write_bytes;
855 dest->systime = src->systime;
856 }
857
858 /* Get a new set of stats. */
859 if (sg_get_disk_io_stats(&new_count) == NULL) {
860 return NULL;
861 }
862
863 /* For each previous stat... */
864 for (i = 0; i < diff_count; i++) {
865 dest = &diff[i];
866
867 /* ... find the corresponding new stat ... */
868 for (j = 0; j < new_count; j++) {
869 /* Try the new stat in the same position first,
870 since that's most likely to be it. */
871 src = &diskio_stats[(i + j) % new_count];
872 if (strcmp(src->disk_name, dest->disk_name) == 0) {
873 break;
874 }
875 }
876 if (j == new_count) {
877 /* No match found. */
878 continue;
879 }
880
881 /* ... and subtract the previous stat from it to get the
882 difference. */
883 dest->read_bytes = src->read_bytes - dest->read_bytes;
884 dest->write_bytes = src->write_bytes - dest->write_bytes;
885 dest->systime = src->systime - dest->systime;
886 }
887
888 *entries = diff_count;
889 return diff;
890 }
891
892 int sg_disk_io_compare_name(const void *va, const void *vb) {
893 const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
894 const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
895
896 return strcmp(a->disk_name, b->disk_name);
897 }
898