ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.72
Committed: Sun Jul 18 21:30:11 2004 UTC (19 years, 10 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_10_3, LIBSTATGRAB_0_10_2, LIBSTATGRAB_0_10_1
Changes since 1.71: +11 -9 lines
Log Message:
Use sg_set_error_with_errno whenever errno's valid.
Change the one user of SG_ERROR_ENOENT to SG_ERROR_SYSCTL instead (since
that's what it should have been).

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