ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.81
Committed: Wed Apr 13 11:50:31 2005 UTC (19 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.80: +23 -7 lines
Log Message:
Rework Linux partition detection to detect devices called "c0d0" (etc.)
and not assume they're partitions -- the DAC* and Compaq RAID devices do
this.

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