ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.69
Committed: Sun Jun 20 17:00:25 2004 UTC (19 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.68: +8 -2 lines
Log Message:
Since OpenBSD 3.5 disk io stats have been seperated into read and write,
just like NetBSD. This fixes libstatgrab to work on 3.5+.

Bug reported by Wijnand Wiersma and Christian Weisgerber.

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.69 * $Id: disk_stats.c,v 1.68 2004/04/08 14:30:43 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.67 int sg_fs_compare_device_name(const void *va, const void *vb) {
257     const sg_fs_stats *a = (const sg_fs_stats *)va;
258     const sg_fs_stats *b = (const sg_fs_stats *)vb;
259    
260     return strcmp(a->device_name, b->device_name);
261     }
262    
263     int sg_fs_compare_mnt_point(const void *va, const void *vb) {
264     const sg_fs_stats *a = (const sg_fs_stats *)va;
265     const sg_fs_stats *b = (const sg_fs_stats *)vb;
266    
267     return strcmp(a->mnt_point, b->mnt_point);
268     }
269    
270 ats 1.61 static void diskio_stat_init(sg_disk_io_stats *d) {
271 ats 1.56 d->disk_name = NULL;
272 pajs 1.4 }
273    
274 ats 1.61 static void diskio_stat_destroy(sg_disk_io_stats *d) {
275 ats 1.56 free(d->disk_name);
276 pajs 1.4 }
277    
278 ats 1.61 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
279 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
280 pajs 1.4
281 ats 1.20 #ifdef LINUX
282     typedef struct {
283     int major;
284     int minor;
285     } partition;
286     #endif
287    
288 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
289 ats 1.56 int num_diskio;
290 ats 1.38 #ifndef LINUX
291 ats 1.61 sg_disk_io_stats *diskio_stats_ptr;
292 ats 1.38 #endif
293 pajs 1.4
294 pajs 1.10 #ifdef SOLARIS
295 tdb 1.65 kstat_ctl_t *kc;
296     kstat_t *ksp;
297 pajs 1.4 kstat_io_t kios;
298 pajs 1.10 #endif
299     #ifdef LINUX
300     FILE *f;
301     char *line_ptr;
302     int major, minor;
303 ats 1.20 int has_pp_stats = 1;
304 ats 1.58 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
305 ats 1.20 int i, n;
306     time_t now;
307 ats 1.38 const char *format;
308 pajs 1.10 #endif
309 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
310 ats 1.21 static struct statinfo stats;
311     static int stats_init = 0;
312 pajs 1.17 int counter;
313     struct device_selection *dev_sel = NULL;
314     int n_selected, n_selections;
315     long sel_gen;
316     struct devstat *dev_ptr;
317 ats 1.31 #endif
318     #ifdef NETBSD
319 ats 1.34 struct disk_sysctl *stats;
320 tdb 1.51 #endif
321     #ifdef OPENBSD
322 tdb 1.52 int diskcount;
323     char *disknames, *name, *bufpp;
324     char **dk_name;
325 tdb 1.51 struct diskstats *stats;
326     #endif
327     #ifdef NETBSD
328     #define MIBSIZE 3
329     #endif
330     #ifdef OPENBSD
331     #define MIBSIZE 2
332     #endif
333     #if defined(NETBSD) || defined(OPENBSD)
334 ats 1.34 int num_disks, i;
335 tdb 1.51 int mib[MIBSIZE];
336 ats 1.34 size_t size;
337 pajs 1.17 #endif
338 ats 1.34
339 pajs 1.10 num_diskio=0;
340 ats 1.34
341 tdb 1.52 #ifdef OPENBSD
342     mib[0] = CTL_HW;
343     mib[1] = HW_DISKCOUNT;
344    
345     size = sizeof(diskcount);
346     if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
347 tdb 1.68 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
348 tdb 1.52 return NULL;
349     }
350    
351     mib[0] = CTL_HW;
352     mib[1] = HW_DISKNAMES;
353    
354     if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
355 tdb 1.68 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
356 tdb 1.52 return NULL;
357     }
358    
359 ats 1.64 disknames = sg_malloc(size);
360 tdb 1.52 if (disknames == NULL) {
361     return NULL;
362     }
363    
364     if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
365 tdb 1.68 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
366 tdb 1.52 return NULL;
367     }
368    
369 ats 1.64 dk_name = sg_malloc(diskcount * sizeof(char *));
370 tdb 1.52 bufpp = disknames;
371     for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
372     dk_name[i] = name;
373     }
374     #endif
375    
376 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
377 ats 1.34 mib[0] = CTL_HW;
378     mib[1] = HW_DISKSTATS;
379 tdb 1.51 #ifdef NETBSD
380 ats 1.34 mib[2] = sizeof(struct disk_sysctl);
381 tdb 1.51 #endif
382 ats 1.34
383 tdb 1.51 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
384 tdb 1.68 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
385 ats 1.34 return NULL;
386     }
387 tdb 1.51
388     #ifdef NETBSD
389 ats 1.34 num_disks = size / sizeof(struct disk_sysctl);
390 tdb 1.51 #else
391     num_disks = size / sizeof(struct diskstats);
392     #endif
393 ats 1.34
394 ats 1.64 stats = sg_malloc(size);
395 ats 1.34 if (stats == NULL) {
396     return NULL;
397     }
398    
399 tdb 1.51 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
400 tdb 1.68 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
401 ats 1.34 return NULL;
402     }
403    
404     for (i = 0; i < num_disks; i++) {
405 ats 1.60 const char *name;
406 ats 1.34 u_int64_t rbytes, wbytes;
407    
408 tdb 1.51 #ifdef NETBSD
409 ats 1.34 #ifdef HAVE_DK_RBYTES
410     rbytes = stats[i].dk_rbytes;
411     wbytes = stats[i].dk_wbytes;
412     #else
413 tdb 1.69 /* Before 2.0, NetBSD merged reads and writes. */
414 ats 1.34 rbytes = wbytes = stats[i].dk_bytes;
415     #endif
416 tdb 1.51 #else
417 tdb 1.69 #ifdef HAVE_DS_RBYTES
418     rbytes = stats[i].ds_rbytes;
419     wbytes = stats[i].ds_wbytes;
420     #else
421     /* Before 3.5, OpenBSD merged reads and writes */
422 tdb 1.51 rbytes = wbytes = stats[i].ds_bytes;
423 tdb 1.69 #endif
424 tdb 1.51 #endif
425 ats 1.34
426     /* Don't keep stats for disks that have never been used. */
427     if (rbytes == 0 && wbytes == 0) {
428     continue;
429     }
430    
431 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
432 ats 1.34 return NULL;
433     }
434     diskio_stats_ptr = diskio_stats + num_diskio;
435    
436     diskio_stats_ptr->read_bytes = rbytes;
437     diskio_stats_ptr->write_bytes = wbytes;
438 tdb 1.51 #ifdef NETBSD
439 ats 1.60 name = stats[i].dk_name;
440 tdb 1.51 #else
441 ats 1.60 name = dk_name[i];
442 tdb 1.51 #endif
443 ats 1.62 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
444 ats 1.60 return NULL;
445     }
446 ats 1.34 diskio_stats_ptr->systime = time(NULL);
447    
448     num_diskio++;
449     }
450    
451     free(stats);
452 tdb 1.52 #ifdef OPENBSD
453     free(dk_name);
454     free(disknames);
455     #endif
456 ats 1.34 #endif
457 pajs 1.4
458 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
459 ats 1.21 if (!stats_init) {
460 ats 1.64 stats.dinfo=sg_malloc(sizeof(struct devinfo));
461 ats 1.33 if(stats.dinfo==NULL) return NULL;
462 tdb 1.25 bzero(stats.dinfo, sizeof(struct devinfo));
463 ats 1.21 stats_init = 1;
464     }
465 pajs 1.28 #ifdef FREEBSD5
466 tdb 1.66 if ((devstat_getdevs(NULL, &stats)) < 0) {
467     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
468     return NULL;
469     }
470 ats 1.29 /* Not aware of a get all devices, so i said 999. If we ever
471     * find a machine with more than 999 disks, then i'll change
472     * this number :)
473     */
474 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) {
475     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
476     return NULL;
477     }
478 pajs 1.28 #else
479 tdb 1.66 if ((getdevs(&stats)) < 0) {
480     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
481     return NULL;
482     }
483 pajs 1.17 /* Not aware of a get all devices, so i said 999. If we ever
484     * find a machine with more than 999 disks, then i'll change
485     * this number :)
486     */
487 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) {
488     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
489     return NULL;
490     }
491 pajs 1.27 #endif
492 pajs 1.17
493     for(counter=0;counter<stats.dinfo->numdevs;counter++){
494     dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
495    
496     /* Throw away devices that have done nothing, ever.. Eg "odd"
497     * devices.. like mem, proc.. and also doesn't report floppy
498     * drives etc unless they are doing stuff :)
499     */
500 pajs 1.27 #ifdef FREEBSD5
501     if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
502     #else
503 pajs 1.17 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
504 pajs 1.27 #endif
505 ats 1.56
506     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
507 pajs 1.17 return NULL;
508     }
509     diskio_stats_ptr=diskio_stats+num_diskio;
510 pajs 1.27
511     #ifdef FREEBSD5
512     diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
513     diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
514     #else
515 pajs 1.17 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
516     diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
517 pajs 1.27 #endif
518 pajs 1.17 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
519 ats 1.60 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
520 tdb 1.66 sg_set_error(SG_ERROR_ASPRINTF, NULL);
521 ats 1.60 return NULL;
522     }
523 pajs 1.17 diskio_stats_ptr->systime=time(NULL);
524    
525     num_diskio++;
526     }
527     free(dev_sel);
528    
529     #endif
530 pajs 1.10 #ifdef SOLARIS
531 pajs 1.4 if ((kc = kstat_open()) == NULL) {
532 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
533 tdb 1.65 return NULL;
534     }
535 pajs 1.4
536     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
537 tdb 1.65 if (!strcmp(ksp->ks_class, "disk")) {
538 pajs 1.4
539     if(ksp->ks_type != KSTAT_TYPE_IO) continue;
540 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
541 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
542 tdb 1.65 if((kstat_read(kc, ksp, &kios))==-1){
543 pajs 1.4 }
544    
545 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
546 pajs 1.4 kstat_close(kc);
547     return NULL;
548     }
549 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
550 pajs 1.4
551     diskio_stats_ptr->read_bytes=kios.nread;
552     diskio_stats_ptr->write_bytes=kios.nwritten;
553 ats 1.61 if (sg_update_string(&diskio_stats_ptr->disk_name,
554 tdb 1.65 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
555 ats 1.60 return NULL;
556     }
557     diskio_stats_ptr->systime=time(NULL);
558 pajs 1.4
559 pajs 1.5 num_diskio++;
560 pajs 1.4 }
561     }
562    
563     kstat_close(kc);
564 pajs 1.10 #endif
565 pajs 1.4
566 pajs 1.10 #ifdef LINUX
567 ats 1.20 num_diskio = 0;
568     n = 0;
569 pajs 1.10
570 ats 1.20 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
571 ats 1.38 have statistics in here too, so we can use those directly.
572     2.6 kernels have /proc/diskstats instead with almost (but not quite)
573     the same format. */
574    
575     f = fopen("/proc/diskstats", "r");
576 ats 1.54 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
577 ats 1.38 if (f == NULL) {
578     f = fopen("/proc/partitions", "r");
579 ats 1.54 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
580 ats 1.38 }
581 ats 1.20 if (f == NULL) goto out;
582     now = time(NULL);
583    
584 ats 1.61 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
585 ats 1.54 char name[100];
586 ats 1.20 char *s;
587     long long rsect, wsect;
588    
589 ats 1.38 int nr = sscanf(line_ptr, format,
590 ats 1.20 &major, &minor, name, &rsect, &wsect);
591     if (nr < 3) continue;
592    
593     /* Skip device names ending in numbers, since they're
594     partitions. */
595     s = name;
596     while (*s != '\0') s++;
597     --s;
598     if (*s >= '0' && *s <= '9') continue;
599 ats 1.38
600     if (nr < 5) {
601     has_pp_stats = 0;
602     rsect = 0;
603     wsect = 0;
604     }
605 ats 1.20
606 ats 1.56 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
607     goto out;
608     }
609 ats 1.58 if (VECTOR_RESIZE(parts, n + 1) < 0) {
610     goto out;
611 pajs 1.11 }
612 pajs 1.12
613 ats 1.62 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
614 ats 1.60 goto out;
615     }
616 ats 1.20 diskio_stats[n].read_bytes = rsect * 512;
617     diskio_stats[n].write_bytes = wsect * 512;
618     diskio_stats[n].systime = now;
619     parts[n].major = major;
620     parts[n].minor = minor;
621 pajs 1.10
622 ats 1.20 n++;
623     }
624 pajs 1.10
625 ats 1.45 fclose(f);
626 ats 1.46 f = NULL;
627 pajs 1.44
628 ats 1.20 if (!has_pp_stats) {
629 ats 1.45 /* This is an older kernel where /proc/partitions doesn't
630     contain stats. Read what we can from /proc/stat instead, and
631     fill in the appropriate bits of the list allocated above. */
632 ats 1.20
633     f = fopen("/proc/stat", "r");
634     if (f == NULL) goto out;
635     now = time(NULL);
636    
637 ats 1.61 line_ptr = sg_f_read_line(f, "disk_io:");
638 ats 1.20 if (line_ptr == NULL) goto out;
639    
640     while((line_ptr=strchr(line_ptr, ' '))!=NULL){
641     long long rsect, wsect;
642    
643     if (*++line_ptr == '\0') break;
644    
645     if((sscanf(line_ptr,
646     "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
647     &major, &minor, &rsect, &wsect)) != 4) {
648     continue;
649     }
650 pajs 1.10
651 ats 1.20 /* Find the corresponding device from earlier.
652     Just to add to the fun, "minor" is actually the disk
653     number, not the device minor, so we need to figure
654     out the real minor number based on the major!
655     This list is not exhaustive; if you're running
656     an older kernel you probably don't have fancy
657     I2O hardware anyway... */
658     switch (major) {
659     case 3:
660     case 21:
661 pajs 1.10 case 22:
662 ats 1.20 case 33:
663     case 34:
664     case 36:
665     case 56:
666     case 57:
667     case 88:
668     case 89:
669     case 90:
670     case 91:
671     minor *= 64;
672 pajs 1.11 break;
673 ats 1.20 case 9:
674     case 43:
675 pajs 1.11 break;
676 pajs 1.10 default:
677 ats 1.20 minor *= 16;
678 pajs 1.11 break;
679 ats 1.20 }
680     for (i = 0; i < n; i++) {
681     if (major == parts[i].major
682     && minor == parts[i].minor)
683     break;
684     }
685     if (i == n) continue;
686    
687     /* We read the number of blocks. Blocks are stored in
688     512 bytes */
689     diskio_stats[i].read_bytes = rsect * 512;
690     diskio_stats[i].write_bytes = wsect * 512;
691     diskio_stats[i].systime = now;
692 pajs 1.10 }
693     }
694 pajs 1.16
695 ats 1.20 num_diskio = n;
696     out:
697     if (f != NULL) fclose(f);
698 ats 1.42 #endif
699 pajs 1.10
700 ats 1.42 #ifdef CYGWIN
701 tdb 1.66 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
702 ats 1.42 return NULL;
703 pajs 1.10 #endif
704 ats 1.42
705 pajs 1.5 *entries=num_diskio;
706 pajs 1.4
707     return diskio_stats;
708 pajs 1.5 }
709    
710 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
711     VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
712 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
713 ats 1.61 sg_disk_io_stats *src = NULL, *dest;
714 ats 1.56 int i, j, diff_count, new_count;
715 ats 1.50
716     if (diskio_stats == NULL) {
717     /* No previous stats, so we can't calculate a difference. */
718 ats 1.61 return sg_get_disk_io_stats(entries);
719 pajs 1.5 }
720    
721 ats 1.50 /* Resize the results array to match the previous stats. */
722 ats 1.56 diff_count = VECTOR_SIZE(diskio_stats);
723     if (VECTOR_RESIZE(diff, diff_count) < 0) {
724 pajs 1.5 return NULL;
725     }
726    
727 ats 1.50 /* Copy the previous stats into the result. */
728     for (i = 0; i < diff_count; i++) {
729     src = &diskio_stats[i];
730     dest = &diff[i];
731    
732 ats 1.62 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
733 ats 1.60 return NULL;
734 ats 1.50 }
735     dest->read_bytes = src->read_bytes;
736     dest->write_bytes = src->write_bytes;
737     dest->systime = src->systime;
738 pajs 1.5 }
739    
740 ats 1.50 /* Get a new set of stats. */
741 ats 1.61 if (sg_get_disk_io_stats(&new_count) == NULL) {
742 ats 1.47 return NULL;
743     }
744 pajs 1.5
745 ats 1.50 /* For each previous stat... */
746     for (i = 0; i < diff_count; i++) {
747     dest = &diff[i];
748    
749     /* ... find the corresponding new stat ... */
750     for (j = 0; j < new_count; j++) {
751     /* Try the new stat in the same position first,
752     since that's most likely to be it. */
753     src = &diskio_stats[(i + j) % new_count];
754     if (strcmp(src->disk_name, dest->disk_name) == 0) {
755     break;
756 pajs 1.5 }
757     }
758 ats 1.50 if (j == new_count) {
759     /* No match found. */
760     continue;
761     }
762 pajs 1.5
763 ats 1.50 /* ... and subtract the previous stat from it to get the
764     difference. */
765     dest->read_bytes = src->read_bytes - dest->read_bytes;
766     dest->write_bytes = src->write_bytes - dest->write_bytes;
767     dest->systime = src->systime - dest->systime;
768     }
769 pajs 1.5
770 ats 1.50 *entries = diff_count;
771     return diff;
772 ats 1.67 }
773    
774     int sg_disk_io_compare_name(const void *va, const void *vb) {
775     const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
776     const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
777    
778     return strcmp(a->disk_name, b->disk_name);
779 pajs 1.2 }
780 ats 1.50