ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.78
Committed: Sat Nov 6 23:54:12 2004 UTC (19 years, 6 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.77: +2 -2 lines
Log Message:
Cast numbers to be the same type when doing a comparison.

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