ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.57
Committed: Sun Apr 4 22:29:54 2004 UTC (20 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.56: +19 -21 lines
Log Message:
Make copy_string not leak memory on realloc() failure.
Make copy_string static.

We probably ought to move copy_string to tools.c/h at some point in the
future, since it's useful.

File Contents

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