ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.66
Committed: Wed Apr 7 19:26:16 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.65: +28 -5 lines
Log Message:
Error reporting for disk_stats.

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