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

# User Rev Content
1 tdb 1.48 /*
2 pajs 1.1 * i-scream central monitoring system
3 tdb 1.18 * http://www.i-scream.org
4 tdb 1.48 * Copyright (C) 2000-2004 i-scream
5 pajs 1.1 *
6 tdb 1.48 * 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 pajs 1.1 *
11 tdb 1.48 * This library is distributed in the hope that it will be useful,
12 pajs 1.1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 tdb 1.48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * Lesser General Public License for more details.
15 pajs 1.1 *
16 tdb 1.48 * 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 tdb 1.49 *
21 ats 1.57 * $Id: disk_stats.c,v 1.56 2004/04/04 22:18:38 ats Exp $
22 pajs 1.1 */
23    
24     #ifdef HAVE_CONFIG_H
25     #include "config.h"
26     #endif
27    
28 pajs 1.17 #include <stdio.h>
29 pajs 1.1 #include <stdlib.h>
30     #include <string.h>
31 ats 1.40 #include <time.h>
32 tdb 1.7 #include "statgrab.h"
33 ats 1.56 #include "vector.h"
34 pajs 1.1
35     #ifdef SOLARIS
36     #include <sys/mnttab.h>
37     #include <sys/statvfs.h>
38 pajs 1.4 #include <kstat.h>
39 pajs 1.1 #define VALID_FS_TYPES {"ufs", "tmpfs"}
40     #endif
41    
42 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
43     #include <mntent.h>
44 pajs 1.9 #include <sys/vfs.h>
45 pajs 1.10 #include "tools.h"
46 ats 1.40 #endif
47    
48     #ifdef LINUX
49 ats 1.32 #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 pajs 1.9 #endif
55 ats 1.40
56     #ifdef CYGWIN
57     #define VALID_FS_TYPES {"user"}
58 tdb 1.39 #endif
59 pajs 1.9
60 ats 1.31 #ifdef ALLBSD
61 pajs 1.16 #include <sys/param.h>
62     #include <sys/ucred.h>
63     #include <sys/mount.h>
64 ats 1.31 #endif
65 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
66 pajs 1.17 #include <sys/dkstat.h>
67     #include <devstat.h>
68 ats 1.36 #define VALID_FS_TYPES {"hpfs", "msdosfs", "ntfs", "udf", "ext2fs", \
69     "ufs", "mfs"}
70 pajs 1.16 #endif
71 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
72 ats 1.34 #include <sys/param.h>
73     #include <sys/sysctl.h>
74     #include <sys/disk.h>
75 ats 1.35 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
76     "ntfs"}
77 ats 1.34 #endif
78 ats 1.31
79 ats 1.57 static char *copy_string(char **dest, const char *src) {
80     char *new;
81 pajs 1.1
82 ats 1.57 new = realloc(*dest, strlen(src) + 1);
83     if (new == NULL) {
84     return NULL;
85     }
86 pajs 1.1
87 ats 1.57 strcpy(new, src);
88     *dest = new;
89     return new;
90 pajs 1.1 }
91    
92 ats 1.56 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 pajs 1.1
98 ats 1.56 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 pajs 1.1 }
103    
104 ats 1.41 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 pajs 1.1 disk_stat_t *get_disk_stats(int *entries){
117 ats 1.56 VECTOR_DECLARE_STATIC(disk_stats, disk_stat_t, 10,
118     disk_stat_init, disk_stat_destroy);
119 pajs 1.1
120 ats 1.41 int valid_type;
121 pajs 1.1 int num_disks=0;
122 ats 1.40 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN)
123 pajs 1.1 FILE *f;
124 pajs 1.16 #endif
125 pajs 1.1
126     disk_stat_t *disk_ptr;
127    
128 pajs 1.9 #ifdef SOLARIS
129 tdb 1.13 struct mnttab mp;
130 pajs 1.9 struct statvfs fs;
131     #endif
132 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
133 pajs 1.9 struct mntent *mp;
134     struct statfs fs;
135     #endif
136 ats 1.31 #ifdef ALLBSD
137 pajs 1.16 int nummnt;
138     struct statfs *mp;
139     #endif
140 pajs 1.9
141 ats 1.31 #ifdef ALLBSD
142 pajs 1.16 nummnt=getmntinfo(&mp , MNT_LOCAL);
143     if (nummnt<=0){
144     return NULL;
145     }
146     for(;nummnt--; mp++){
147 ats 1.41 valid_type = is_valid_fs_type(mp->f_fstypename);
148 pajs 1.16 #endif
149    
150 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
151 pajs 1.9 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
152     return NULL;
153     }
154 pajs 1.1
155 pajs 1.9 while((mp=getmntent(f))){
156     if((statfs(mp->mnt_dir, &fs)) !=0){
157     continue;
158     }
159 pajs 1.1
160 ats 1.41 valid_type = is_valid_fs_type(mp->mnt_type);
161 pajs 1.9 #endif
162    
163     #ifdef SOLARIS
164 pajs 1.1 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 ats 1.41 valid_type = is_valid_fs_type(mp.mnt_fstype);
172 pajs 1.9 #endif
173 pajs 1.1
174     if(valid_type){
175 ats 1.56 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
176     return NULL;
177 pajs 1.1 }
178 ats 1.56 disk_ptr=disk_stats+num_disks;
179 pajs 1.1
180 ats 1.31 #ifdef ALLBSD
181 ats 1.57 if (copy_string(&disk_ptr->device_name, mp->f_mntfromname) == NULL) {
182 pajs 1.16 return NULL;
183     }
184 ats 1.57 if (copy_string(&disk_ptr->fs_type, mp->f_fstypename) == NULL) {
185 pajs 1.16 return NULL;
186     }
187 ats 1.57 if (copy_string(&disk_ptr->mnt_point, mp->f_mntonname) == NULL) {
188 pajs 1.16 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 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
201 ats 1.57 if (copy_string(&disk_ptr->device_name, mp->mnt_fsname) == NULL) {
202 pajs 1.9 return NULL;
203     }
204    
205 ats 1.57 if (copy_string(&disk_ptr->fs_type, mp->mnt_type) == NULL) {
206 pajs 1.9 return NULL;
207     }
208    
209 ats 1.57 if (copy_string(&disk_ptr->mnt_point, mp->mnt_dir) == NULL) {
210 pajs 1.9 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 pajs 1.1 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end?
224 pajs 1.9 * 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 ats 1.57 if (copy_string(&disk_ptr->device_name, mp.mnt_special) == NULL) {
228 pajs 1.1 return NULL;
229     }
230    
231 ats 1.57 if (copy_string(&disk_ptr->fs_type, mp.mnt_fstype) == NULL) {
232 pajs 1.1 return NULL;
233     }
234    
235 ats 1.57 if (copy_string(&disk_ptr->mnt_point, mp.mnt_mountp) == NULL) {
236 pajs 1.1 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 pajs 1.9 #endif
247 pajs 1.1 num_disks++;
248     }
249     }
250    
251     *entries=num_disks;
252    
253 tdb 1.39 /* If this fails, there is very little i can do about it, so
254     I'll ignore it :) */
255 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
256 tdb 1.39 endmntent(f);
257     #endif
258     #if defined(SOLARIS)
259 pajs 1.1 fclose(f);
260 pajs 1.16 #endif
261 pajs 1.1
262     return disk_stats;
263    
264     }
265 pajs 1.4
266 ats 1.56 static void diskio_stat_init(diskio_stat_t *d) {
267     d->disk_name = NULL;
268 pajs 1.4 }
269    
270 ats 1.56 static void diskio_stat_destroy(diskio_stat_t *d) {
271     free(d->disk_name);
272 pajs 1.4 }
273    
274 ats 1.56 VECTOR_DECLARE_STATIC(diskio_stats, diskio_stat_t, 10,
275     diskio_stat_init, diskio_stat_destroy);
276 pajs 1.4
277 ats 1.20 #ifdef LINUX
278     typedef struct {
279     int major;
280     int minor;
281     } partition;
282     #endif
283    
284 tdb 1.3 diskio_stat_t *get_diskio_stats(int *entries){
285 ats 1.56 int num_diskio;
286 ats 1.38 #ifndef LINUX
287 pajs 1.4 diskio_stat_t *diskio_stats_ptr;
288 ats 1.38 #endif
289 pajs 1.4
290 pajs 1.10 #ifdef SOLARIS
291 pajs 1.4 kstat_ctl_t *kc;
292     kstat_t *ksp;
293     kstat_io_t kios;
294 pajs 1.10 #endif
295     #ifdef LINUX
296     FILE *f;
297     char *line_ptr;
298     int major, minor;
299 ats 1.20 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 ats 1.38 const char *format;
305 pajs 1.10 #endif
306 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
307 ats 1.21 static struct statinfo stats;
308     static int stats_init = 0;
309 pajs 1.17 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 ats 1.31 #endif
315     #ifdef NETBSD
316 ats 1.34 struct disk_sysctl *stats;
317 tdb 1.51 #endif
318     #ifdef OPENBSD
319 tdb 1.52 int diskcount;
320     char *disknames, *name, *bufpp;
321     char **dk_name;
322 tdb 1.51 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 ats 1.34 int num_disks, i;
332 tdb 1.51 int mib[MIBSIZE];
333 ats 1.34 size_t size;
334 pajs 1.17 #endif
335 ats 1.34
336 pajs 1.10 num_diskio=0;
337 ats 1.34
338 tdb 1.52 #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 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
371 ats 1.34 mib[0] = CTL_HW;
372     mib[1] = HW_DISKSTATS;
373 tdb 1.51 #ifdef NETBSD
374 ats 1.34 mib[2] = sizeof(struct disk_sysctl);
375 tdb 1.51 #endif
376 ats 1.34
377 tdb 1.51 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
378 ats 1.34 return NULL;
379     }
380 tdb 1.51
381     #ifdef NETBSD
382 ats 1.34 num_disks = size / sizeof(struct disk_sysctl);
383 tdb 1.51 #else
384     num_disks = size / sizeof(struct diskstats);
385     #endif
386 ats 1.34
387     stats = malloc(size);
388     if (stats == NULL) {
389     return NULL;
390     }
391    
392 tdb 1.51 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
393 ats 1.34 return NULL;
394     }
395    
396     for (i = 0; i < num_disks; i++) {
397     u_int64_t rbytes, wbytes;
398    
399 tdb 1.51 #ifdef NETBSD
400 ats 1.34 #ifdef HAVE_DK_RBYTES
401     rbytes = stats[i].dk_rbytes;
402     wbytes = stats[i].dk_wbytes;
403     #else
404 ats 1.37 /* Before 1.7, NetBSD merged reads and writes. */
405 ats 1.34 rbytes = wbytes = stats[i].dk_bytes;
406     #endif
407 tdb 1.51 #else
408     rbytes = wbytes = stats[i].ds_bytes;
409     #endif
410 ats 1.34
411     /* Don't keep stats for disks that have never been used. */
412     if (rbytes == 0 && wbytes == 0) {
413     continue;
414     }
415    
416 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
417 ats 1.34 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 tdb 1.51 #ifdef NETBSD
427 ats 1.34 diskio_stats_ptr->disk_name = strdup(stats[i].dk_name);
428 tdb 1.51 #else
429 tdb 1.52 diskio_stats_ptr->disk_name = strdup(dk_name[i]);
430 tdb 1.51 #endif
431 ats 1.34 diskio_stats_ptr->systime = time(NULL);
432    
433     num_diskio++;
434     }
435    
436     free(stats);
437 tdb 1.52 #ifdef OPENBSD
438     free(dk_name);
439     free(disknames);
440     #endif
441 ats 1.34 #endif
442 pajs 1.4
443 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
444 ats 1.21 if (!stats_init) {
445     stats.dinfo=malloc(sizeof(struct devinfo));
446 ats 1.33 if(stats.dinfo==NULL) return NULL;
447 tdb 1.25 bzero(stats.dinfo, sizeof(struct devinfo));
448 ats 1.21 stats_init = 1;
449     }
450 pajs 1.28 #ifdef FREEBSD5
451     if ((devstat_getdevs(NULL, &stats)) < 0) return NULL;
452 ats 1.29 /* 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 pajs 1.28 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 pajs 1.17 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 pajs 1.27 #endif
465 pajs 1.17
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 pajs 1.27 #ifdef FREEBSD5
474     if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
475     #else
476 pajs 1.17 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
477 pajs 1.27 #endif
478 ats 1.56
479     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
480 pajs 1.17 return NULL;
481     }
482     diskio_stats_ptr=diskio_stats+num_diskio;
483 pajs 1.27
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 pajs 1.17 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
489     diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
490 pajs 1.27 #endif
491 pajs 1.17 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 pajs 1.10 #ifdef SOLARIS
501 pajs 1.4 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 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
510 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
511     if((kstat_read(kc, ksp, &kios))==-1){
512     }
513    
514 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
515 pajs 1.4 kstat_close(kc);
516     return NULL;
517     }
518 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
519 pajs 1.4
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 tdb 1.43 diskio_stats_ptr->disk_name=strdup((char *) get_svr_from_bsd(ksp->ks_name));
527 pajs 1.14 diskio_stats_ptr->systime=time(NULL);
528 pajs 1.5 num_diskio++;
529 pajs 1.4 }
530     }
531    
532     kstat_close(kc);
533 pajs 1.10 #endif
534 pajs 1.4
535 pajs 1.10 #ifdef LINUX
536 ats 1.20 num_diskio = 0;
537     n = 0;
538 pajs 1.10
539 ats 1.20 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
540 ats 1.38 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 ats 1.54 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
546 ats 1.38 if (f == NULL) {
547     f = fopen("/proc/partitions", "r");
548 ats 1.54 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
549 ats 1.38 }
550 ats 1.20 if (f == NULL) goto out;
551     now = time(NULL);
552    
553     while ((line_ptr = f_read_line(f, "")) != NULL) {
554 ats 1.54 char name[100];
555 ats 1.20 char *s;
556     long long rsect, wsect;
557    
558 ats 1.38 int nr = sscanf(line_ptr, format,
559 ats 1.20 &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 ats 1.38
569     if (nr < 5) {
570     has_pp_stats = 0;
571     rsect = 0;
572     wsect = 0;
573     }
574 ats 1.20
575 ats 1.56 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
576     goto out;
577     }
578 ats 1.20 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 pajs 1.11 }
586 pajs 1.12
587 ats 1.20 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 pajs 1.10
596 ats 1.20 n++;
597     }
598 pajs 1.10
599 ats 1.45 fclose(f);
600 ats 1.46 f = NULL;
601 pajs 1.44
602 ats 1.20 if (!has_pp_stats) {
603 ats 1.45 /* 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 ats 1.20
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 pajs 1.10
625 ats 1.20 /* 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 pajs 1.10 case 22:
636 ats 1.20 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 pajs 1.11 break;
647 ats 1.20 case 9:
648     case 43:
649 pajs 1.11 break;
650 pajs 1.10 default:
651 ats 1.20 minor *= 16;
652 pajs 1.11 break;
653 ats 1.20 }
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 pajs 1.10 }
667     }
668 pajs 1.16
669 ats 1.20 num_diskio = n;
670     out:
671     if (f != NULL) fclose(f);
672 ats 1.42 #endif
673 pajs 1.10
674 ats 1.42 #ifdef CYGWIN
675     return NULL;
676 pajs 1.10 #endif
677 ats 1.42
678 pajs 1.5 *entries=num_diskio;
679 pajs 1.4
680     return diskio_stats;
681 pajs 1.5 }
682    
683 pajs 1.6 diskio_stat_t *get_diskio_stats_diff(int *entries){
684 ats 1.56 VECTOR_DECLARE_STATIC(diff, diskio_stat_t, 1,
685     diskio_stat_init, diskio_stat_destroy);
686 ats 1.55 diskio_stat_t *src = NULL, *dest;
687 ats 1.56 int i, j, diff_count, new_count;
688 ats 1.50
689     if (diskio_stats == NULL) {
690     /* No previous stats, so we can't calculate a difference. */
691     return get_diskio_stats(entries);
692 pajs 1.5 }
693    
694 ats 1.50 /* Resize the results array to match the previous stats. */
695 ats 1.56 diff_count = VECTOR_SIZE(diskio_stats);
696     if (VECTOR_RESIZE(diff, diff_count) < 0) {
697 pajs 1.5 return NULL;
698     }
699    
700 ats 1.50 /* 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 pajs 1.5 }
713    
714 ats 1.50 /* Get a new set of stats. */
715     if (get_diskio_stats(&new_count) == NULL) {
716 ats 1.47 return NULL;
717     }
718 pajs 1.5
719 ats 1.50 /* 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 pajs 1.5 }
731     }
732 ats 1.50 if (j == new_count) {
733     /* No match found. */
734     continue;
735     }
736 pajs 1.5
737 ats 1.50 /* ... 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 pajs 1.5
744 ats 1.50 *entries = diff_count;
745     return diff;
746 pajs 1.2 }
747 ats 1.50