ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.67
Committed: Thu Apr 8 13:44:44 2004 UTC (20 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.66: +22 -1 lines
Log Message:
Add comparison functions for names where that's likely to be useful.

Make the network_iface_stats example show how to use bsearch to find an
interface by name.

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