ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.73
Committed: Mon Nov 1 18:30:17 2004 UTC (19 years, 6 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.72: +107 -6 lines
Log Message:
Merge in patch to provide support for HP-UX 11.11.

Contributed by Roy Keene - thanks Roy!

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