ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.63
Committed: Tue Apr 6 14:52:58 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.62: +2 -2 lines
Log Message:
Update name of project at the top of all soure files. These files now exist
in their own right, rather than as part of the "CMS".

File Contents

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