ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.82
Committed: Wed Jul 13 13:01:24 2005 UTC (18 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_12
Changes since 1.81: +69 -68 lines
Log Message:
Add more fs stats.

Patches provided by: Roman Neuhauser

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