ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.82
Committed: Wed Jul 13 13:01:24 2005 UTC (18 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_12
Changes since 1.81: +69 -68 lines
Log Message:
Add more fs stats.

Patches provided by: Roman Neuhauser

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