ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.70
Committed: Sun Jun 20 20:32:24 2004 UTC (19 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.69: +5 -1 lines
Log Message:
On NetBSD 2.0 (in fact, all BSD's, but only know of NetBSD 2.0) use statvfs
instead of statfs when available.

No guarantees I've got this right - I need to upgrade our NetBSD 2 test
machine to a more recent build.

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.70 * $Id: disk_stats.c,v 1.69 2004/06/20 17:00:25 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 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 tdb 1.66 sg_set_error(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 tdb 1.66 sg_set_error(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 tdb 1.68 sg_set_error(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 tdb 1.68 sg_set_error(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 tdb 1.68 sg_set_error(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 tdb 1.68 sg_set_error(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 tdb 1.68 sg_set_error(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     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
472     return NULL;
473     }
474 ats 1.29 /* Not aware of a get all devices, so i said 999. If we ever
475     * find a machine with more than 999 disks, then i'll change
476     * this number :)
477     */
478 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) {
479     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
480     return NULL;
481     }
482 pajs 1.28 #else
483 tdb 1.66 if ((getdevs(&stats)) < 0) {
484     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
485     return NULL;
486     }
487 pajs 1.17 /* Not aware of a get all devices, so i said 999. If we ever
488     * find a machine with more than 999 disks, then i'll change
489     * this number :)
490     */
491 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) {
492     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
493     return NULL;
494     }
495 pajs 1.27 #endif
496 pajs 1.17
497     for(counter=0;counter<stats.dinfo->numdevs;counter++){
498     dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
499    
500     /* Throw away devices that have done nothing, ever.. Eg "odd"
501     * devices.. like mem, proc.. and also doesn't report floppy
502     * drives etc unless they are doing stuff :)
503     */
504 pajs 1.27 #ifdef FREEBSD5
505     if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
506     #else
507 pajs 1.17 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
508 pajs 1.27 #endif
509 ats 1.56
510     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
511 pajs 1.17 return NULL;
512     }
513     diskio_stats_ptr=diskio_stats+num_diskio;
514 pajs 1.27
515     #ifdef FREEBSD5
516     diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
517     diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
518     #else
519 pajs 1.17 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
520     diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
521 pajs 1.27 #endif
522 pajs 1.17 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
523 ats 1.60 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
524 tdb 1.66 sg_set_error(SG_ERROR_ASPRINTF, NULL);
525 ats 1.60 return NULL;
526     }
527 pajs 1.17 diskio_stats_ptr->systime=time(NULL);
528    
529     num_diskio++;
530     }
531     free(dev_sel);
532    
533     #endif
534 pajs 1.10 #ifdef SOLARIS
535 pajs 1.4 if ((kc = kstat_open()) == NULL) {
536 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
537 tdb 1.65 return NULL;
538     }
539 pajs 1.4
540     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
541 tdb 1.65 if (!strcmp(ksp->ks_class, "disk")) {
542 pajs 1.4
543     if(ksp->ks_type != KSTAT_TYPE_IO) continue;
544 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
545 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
546 tdb 1.65 if((kstat_read(kc, ksp, &kios))==-1){
547 pajs 1.4 }
548    
549 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
550 pajs 1.4 kstat_close(kc);
551     return NULL;
552     }
553 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
554 pajs 1.4
555     diskio_stats_ptr->read_bytes=kios.nread;
556     diskio_stats_ptr->write_bytes=kios.nwritten;
557 ats 1.61 if (sg_update_string(&diskio_stats_ptr->disk_name,
558 tdb 1.65 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
559 ats 1.60 return NULL;
560     }
561     diskio_stats_ptr->systime=time(NULL);
562 pajs 1.4
563 pajs 1.5 num_diskio++;
564 pajs 1.4 }
565     }
566    
567     kstat_close(kc);
568 pajs 1.10 #endif
569 pajs 1.4
570 pajs 1.10 #ifdef LINUX
571 ats 1.20 num_diskio = 0;
572     n = 0;
573 pajs 1.10
574 ats 1.20 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
575 ats 1.38 have statistics in here too, so we can use those directly.
576     2.6 kernels have /proc/diskstats instead with almost (but not quite)
577     the same format. */
578    
579     f = fopen("/proc/diskstats", "r");
580 ats 1.54 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
581 ats 1.38 if (f == NULL) {
582     f = fopen("/proc/partitions", "r");
583 ats 1.54 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
584 ats 1.38 }
585 ats 1.20 if (f == NULL) goto out;
586     now = time(NULL);
587    
588 ats 1.61 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
589 ats 1.54 char name[100];
590 ats 1.20 char *s;
591     long long rsect, wsect;
592    
593 ats 1.38 int nr = sscanf(line_ptr, format,
594 ats 1.20 &major, &minor, name, &rsect, &wsect);
595     if (nr < 3) continue;
596    
597     /* Skip device names ending in numbers, since they're
598     partitions. */
599     s = name;
600     while (*s != '\0') s++;
601     --s;
602     if (*s >= '0' && *s <= '9') continue;
603 ats 1.38
604     if (nr < 5) {
605     has_pp_stats = 0;
606     rsect = 0;
607     wsect = 0;
608     }
609 ats 1.20
610 ats 1.56 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
611     goto out;
612     }
613 ats 1.58 if (VECTOR_RESIZE(parts, n + 1) < 0) {
614     goto out;
615 pajs 1.11 }
616 pajs 1.12
617 ats 1.62 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
618 ats 1.60 goto out;
619     }
620 ats 1.20 diskio_stats[n].read_bytes = rsect * 512;
621     diskio_stats[n].write_bytes = wsect * 512;
622     diskio_stats[n].systime = now;
623     parts[n].major = major;
624     parts[n].minor = minor;
625 pajs 1.10
626 ats 1.20 n++;
627     }
628 pajs 1.10
629 ats 1.45 fclose(f);
630 ats 1.46 f = NULL;
631 pajs 1.44
632 ats 1.20 if (!has_pp_stats) {
633 ats 1.45 /* This is an older kernel where /proc/partitions doesn't
634     contain stats. Read what we can from /proc/stat instead, and
635     fill in the appropriate bits of the list allocated above. */
636 ats 1.20
637     f = fopen("/proc/stat", "r");
638     if (f == NULL) goto out;
639     now = time(NULL);
640    
641 ats 1.61 line_ptr = sg_f_read_line(f, "disk_io:");
642 ats 1.20 if (line_ptr == NULL) goto out;
643    
644     while((line_ptr=strchr(line_ptr, ' '))!=NULL){
645     long long rsect, wsect;
646    
647     if (*++line_ptr == '\0') break;
648    
649     if((sscanf(line_ptr,
650     "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
651     &major, &minor, &rsect, &wsect)) != 4) {
652     continue;
653     }
654 pajs 1.10
655 ats 1.20 /* Find the corresponding device from earlier.
656     Just to add to the fun, "minor" is actually the disk
657     number, not the device minor, so we need to figure
658     out the real minor number based on the major!
659     This list is not exhaustive; if you're running
660     an older kernel you probably don't have fancy
661     I2O hardware anyway... */
662     switch (major) {
663     case 3:
664     case 21:
665 pajs 1.10 case 22:
666 ats 1.20 case 33:
667     case 34:
668     case 36:
669     case 56:
670     case 57:
671     case 88:
672     case 89:
673     case 90:
674     case 91:
675     minor *= 64;
676 pajs 1.11 break;
677 ats 1.20 case 9:
678     case 43:
679 pajs 1.11 break;
680 pajs 1.10 default:
681 ats 1.20 minor *= 16;
682 pajs 1.11 break;
683 ats 1.20 }
684     for (i = 0; i < n; i++) {
685     if (major == parts[i].major
686     && minor == parts[i].minor)
687     break;
688     }
689     if (i == n) continue;
690    
691     /* We read the number of blocks. Blocks are stored in
692     512 bytes */
693     diskio_stats[i].read_bytes = rsect * 512;
694     diskio_stats[i].write_bytes = wsect * 512;
695     diskio_stats[i].systime = now;
696 pajs 1.10 }
697     }
698 pajs 1.16
699 ats 1.20 num_diskio = n;
700     out:
701     if (f != NULL) fclose(f);
702 ats 1.42 #endif
703 pajs 1.10
704 ats 1.42 #ifdef CYGWIN
705 tdb 1.66 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
706 ats 1.42 return NULL;
707 pajs 1.10 #endif
708 ats 1.42
709 pajs 1.5 *entries=num_diskio;
710 pajs 1.4
711     return diskio_stats;
712 pajs 1.5 }
713    
714 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
715     VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
716 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
717 ats 1.61 sg_disk_io_stats *src = NULL, *dest;
718 ats 1.56 int i, j, diff_count, new_count;
719 ats 1.50
720     if (diskio_stats == NULL) {
721     /* No previous stats, so we can't calculate a difference. */
722 ats 1.61 return sg_get_disk_io_stats(entries);
723 pajs 1.5 }
724    
725 ats 1.50 /* Resize the results array to match the previous stats. */
726 ats 1.56 diff_count = VECTOR_SIZE(diskio_stats);
727     if (VECTOR_RESIZE(diff, diff_count) < 0) {
728 pajs 1.5 return NULL;
729     }
730    
731 ats 1.50 /* Copy the previous stats into the result. */
732     for (i = 0; i < diff_count; i++) {
733     src = &diskio_stats[i];
734     dest = &diff[i];
735    
736 ats 1.62 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
737 ats 1.60 return NULL;
738 ats 1.50 }
739     dest->read_bytes = src->read_bytes;
740     dest->write_bytes = src->write_bytes;
741     dest->systime = src->systime;
742 pajs 1.5 }
743    
744 ats 1.50 /* Get a new set of stats. */
745 ats 1.61 if (sg_get_disk_io_stats(&new_count) == NULL) {
746 ats 1.47 return NULL;
747     }
748 pajs 1.5
749 ats 1.50 /* For each previous stat... */
750     for (i = 0; i < diff_count; i++) {
751     dest = &diff[i];
752    
753     /* ... find the corresponding new stat ... */
754     for (j = 0; j < new_count; j++) {
755     /* Try the new stat in the same position first,
756     since that's most likely to be it. */
757     src = &diskio_stats[(i + j) % new_count];
758     if (strcmp(src->disk_name, dest->disk_name) == 0) {
759     break;
760 pajs 1.5 }
761     }
762 ats 1.50 if (j == new_count) {
763     /* No match found. */
764     continue;
765     }
766 pajs 1.5
767 ats 1.50 /* ... and subtract the previous stat from it to get the
768     difference. */
769     dest->read_bytes = src->read_bytes - dest->read_bytes;
770     dest->write_bytes = src->write_bytes - dest->write_bytes;
771     dest->systime = src->systime - dest->systime;
772     }
773 pajs 1.5
774 ats 1.50 *entries = diff_count;
775     return diff;
776 ats 1.67 }
777    
778     int sg_disk_io_compare_name(const void *va, const void *vb) {
779     const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
780     const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
781    
782     return strcmp(a->disk_name, b->disk_name);
783 pajs 1.2 }
784 ats 1.50