ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.51
Committed: Thu Feb 12 23:04:52 2004 UTC (20 years, 3 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.50: +32 -6 lines
Log Message:
Add preliminary support for OpenBSD (tested on 3.3).

All works apart from Disk IO stats - currently the disks are not named
correctly. The fix for this is probably to use KVM.

Mostly similar to the NetBSD code, the notable exception being the uvm
stuff. In NetBSD there's a function to get it, in OpenBSD sysctl is needed
to get hold of it.

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