ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.72
Committed: Sun Jul 18 21:30:11 2004 UTC (19 years, 10 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_10_3, LIBSTATGRAB_0_10_2, LIBSTATGRAB_0_10_1
Changes since 1.71: +11 -9 lines
Log Message:
Use sg_set_error_with_errno whenever errno's valid.
Change the one user of SG_ERROR_ENOENT to SG_ERROR_SYSCTL instead (since
that's what it should have been).

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