ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.68
Committed: Thu Apr 8 14:30:43 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_10
Changes since 1.67: +6 -6 lines
Log Message:
Fix bugs in error code.

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