ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.77
Committed: Sat Nov 6 15:36:29 2004 UTC (19 years, 6 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.76: +3 -1 lines
Log Message:
Add a FIXME (it'd be preferable to avoid the /dev/dsk search when
possible).

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