ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.56
Committed: Sun Apr 4 22:18:38 2004 UTC (20 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.55: +35 -81 lines
Log Message:
Make disk_stats use vectors.

(Note that this introduces some harmless strict aliasing warnings on GCC
3.3; I'll do something about that later.)

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