ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.81
Committed: Wed Apr 13 11:50:31 2005 UTC (19 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.80: +23 -7 lines
Log Message:
Rework Linux partition detection to detect devices called "c0d0" (etc.)
and not assume they're partitions -- the DAC* and Compaq RAID devices do
this.

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.81 * $Id: disk_stats.c,v 1.80 2005/02/24 12:34:45 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 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 tdb 1.80 "umsdos", "vfat", "xfs", "jfs", "nfs"}
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.80 "ufs", "mfs", "nfs"}
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.80 "ntfs", "nfs"}
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 tdb 1.80 nummnt=getmntinfo(&mp, MNT_WAIT);
148 pajs 1.16 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 ats 1.81 static regex_t not_part_re, part_re;
342     static int re_compiled = 0;
343 pajs 1.10 #endif
344 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
345 ats 1.21 static struct statinfo stats;
346     static int stats_init = 0;
347 pajs 1.17 int counter;
348     struct device_selection *dev_sel = NULL;
349     int n_selected, n_selections;
350     long sel_gen;
351     struct devstat *dev_ptr;
352 ats 1.31 #endif
353     #ifdef NETBSD
354 ats 1.34 struct disk_sysctl *stats;
355 tdb 1.51 #endif
356     #ifdef OPENBSD
357 tdb 1.52 int diskcount;
358     char *disknames, *name, *bufpp;
359     char **dk_name;
360 tdb 1.51 struct diskstats *stats;
361     #endif
362     #ifdef NETBSD
363     #define MIBSIZE 3
364     #endif
365     #ifdef OPENBSD
366     #define MIBSIZE 2
367     #endif
368     #if defined(NETBSD) || defined(OPENBSD)
369 ats 1.34 int num_disks, i;
370 tdb 1.51 int mib[MIBSIZE];
371 ats 1.34 size_t size;
372 pajs 1.17 #endif
373 ats 1.34
374 pajs 1.10 num_diskio=0;
375 ats 1.34
376 tdb 1.73 #ifdef HPUX
377 ats 1.75 while (1) {
378     num = pstat_getdisk(pstat_diskinfo, sizeof pstat_diskinfo[0],
379     DISK_BATCH, diskidx);
380     if (num == -1) {
381     sg_set_error_with_errno(SG_ERROR_PSTAT,
382     "pstat_getdisk");
383     return NULL;
384     } else if (num == 0) {
385 tdb 1.73 break;
386     }
387    
388 ats 1.75 for (i = 0; i < num; i++) {
389     struct pst_diskinfo *di = &pstat_diskinfo[i];
390 tdb 1.73
391 ats 1.75 /* Skip "disabled" disks. */
392     if (di->psd_status == 0) {
393     continue;
394     }
395    
396     /* We can't seperate the reads from the writes, we'll
397 ats 1.79 * just give the same to each. (This value is in
398     * 64-byte chunks according to the pstat header file,
399     * and can wrap to be negative.)
400     */
401     rbytes = wbytes = ((unsigned long) di->psd_dkwds) * 64LL;
402 ats 1.75
403     /* Skip unused disks. */
404     if (rbytes == 0 && wbytes == 0) {
405 tdb 1.73 continue;
406     }
407 ats 1.75
408     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
409     return NULL;
410     }
411    
412     diskio_stats_ptr = diskio_stats + num_diskio;
413    
414     diskio_stats_ptr->read_bytes = rbytes;
415     diskio_stats_ptr->write_bytes = wbytes;
416    
417     diskio_stats_ptr->systime = time(NULL);
418    
419     num_diskio++;
420    
421 ats 1.77 /* FIXME This should use a static cache, like the Linux
422     * code below. */
423 ats 1.75 if (diskio_stats_ptr->disk_name == NULL) {
424     dh = opendir("/dev/dsk");
425     if (dh == NULL) {
426     continue;
427 tdb 1.73 }
428 ats 1.75
429     diskid = (di->psd_dev.psd_major << 24) | di->psd_dev.psd_minor;
430     while (1) {
431     dinfo = readdir(dh);
432     if (dinfo == NULL) {
433     break;
434     }
435     snprintf(fullpathbuf, sizeof(fullpathbuf), "/dev/dsk/%s", dinfo->d_name);
436     if (lstat(fullpathbuf, &lstatinfo) < 0) {
437     continue;
438     }
439    
440     if (lstatinfo.st_rdev == diskid) {
441     if (sg_update_string(&diskio_stats_ptr->disk_name, dinfo->d_name) < 0) {
442     return NULL;
443     }
444     break;
445     }
446 tdb 1.73 }
447 ats 1.75 closedir(dh);
448    
449     if (diskio_stats_ptr->disk_name == NULL) {
450     if (sg_update_string(&diskio_stats_ptr->disk_name, di->psd_hw_path.psh_name) < 0) {
451 tdb 1.73 return NULL;
452     }
453     }
454     }
455     }
456 ats 1.75 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
457 tdb 1.73 }
458     #endif
459 tdb 1.52 #ifdef OPENBSD
460     mib[0] = CTL_HW;
461     mib[1] = HW_DISKCOUNT;
462    
463     size = sizeof(diskcount);
464     if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
465 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
466 tdb 1.52 return NULL;
467     }
468    
469     mib[0] = CTL_HW;
470     mib[1] = HW_DISKNAMES;
471    
472     if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
473 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
474 tdb 1.52 return NULL;
475     }
476    
477 ats 1.64 disknames = sg_malloc(size);
478 tdb 1.52 if (disknames == NULL) {
479     return NULL;
480     }
481    
482     if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
483 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
484 tdb 1.52 return NULL;
485     }
486    
487 ats 1.64 dk_name = sg_malloc(diskcount * sizeof(char *));
488 tdb 1.52 bufpp = disknames;
489     for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
490     dk_name[i] = name;
491     }
492     #endif
493    
494 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
495 ats 1.34 mib[0] = CTL_HW;
496     mib[1] = HW_DISKSTATS;
497 tdb 1.51 #ifdef NETBSD
498 ats 1.34 mib[2] = sizeof(struct disk_sysctl);
499 tdb 1.51 #endif
500 ats 1.34
501 tdb 1.51 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
502 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
503 ats 1.34 return NULL;
504     }
505 tdb 1.51
506     #ifdef NETBSD
507 ats 1.34 num_disks = size / sizeof(struct disk_sysctl);
508 tdb 1.51 #else
509     num_disks = size / sizeof(struct diskstats);
510     #endif
511 ats 1.34
512 ats 1.64 stats = sg_malloc(size);
513 ats 1.34 if (stats == NULL) {
514     return NULL;
515     }
516    
517 tdb 1.51 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
518 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
519 ats 1.34 return NULL;
520     }
521    
522     for (i = 0; i < num_disks; i++) {
523 ats 1.60 const char *name;
524 ats 1.34 u_int64_t rbytes, wbytes;
525    
526 tdb 1.51 #ifdef NETBSD
527 ats 1.34 #ifdef HAVE_DK_RBYTES
528     rbytes = stats[i].dk_rbytes;
529     wbytes = stats[i].dk_wbytes;
530     #else
531 tdb 1.69 /* Before 2.0, NetBSD merged reads and writes. */
532 ats 1.34 rbytes = wbytes = stats[i].dk_bytes;
533     #endif
534 tdb 1.51 #else
535 tdb 1.69 #ifdef HAVE_DS_RBYTES
536     rbytes = stats[i].ds_rbytes;
537     wbytes = stats[i].ds_wbytes;
538     #else
539     /* Before 3.5, OpenBSD merged reads and writes */
540 tdb 1.51 rbytes = wbytes = stats[i].ds_bytes;
541 tdb 1.69 #endif
542 tdb 1.51 #endif
543 ats 1.34
544     /* Don't keep stats for disks that have never been used. */
545     if (rbytes == 0 && wbytes == 0) {
546     continue;
547     }
548    
549 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
550 ats 1.34 return NULL;
551     }
552     diskio_stats_ptr = diskio_stats + num_diskio;
553    
554     diskio_stats_ptr->read_bytes = rbytes;
555     diskio_stats_ptr->write_bytes = wbytes;
556 tdb 1.51 #ifdef NETBSD
557 ats 1.60 name = stats[i].dk_name;
558 tdb 1.51 #else
559 ats 1.60 name = dk_name[i];
560 tdb 1.51 #endif
561 ats 1.62 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
562 ats 1.60 return NULL;
563     }
564 ats 1.34 diskio_stats_ptr->systime = time(NULL);
565    
566     num_diskio++;
567     }
568    
569     free(stats);
570 tdb 1.52 #ifdef OPENBSD
571     free(dk_name);
572     free(disknames);
573     #endif
574 ats 1.34 #endif
575 pajs 1.4
576 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
577 ats 1.21 if (!stats_init) {
578 ats 1.64 stats.dinfo=sg_malloc(sizeof(struct devinfo));
579 ats 1.33 if(stats.dinfo==NULL) return NULL;
580 tdb 1.25 bzero(stats.dinfo, sizeof(struct devinfo));
581 ats 1.21 stats_init = 1;
582     }
583 pajs 1.28 #ifdef FREEBSD5
584 tdb 1.66 if ((devstat_getdevs(NULL, &stats)) < 0) {
585 ats 1.72 /* FIXME devstat functions return a string error in
586     devstat_errbuf */
587 tdb 1.66 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
588     return NULL;
589     }
590 ats 1.29 /* Not aware of a get all devices, so i said 999. If we ever
591     * find a machine with more than 999 disks, then i'll change
592     * this number :)
593     */
594 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) {
595     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
596     return NULL;
597     }
598 pajs 1.28 #else
599 tdb 1.66 if ((getdevs(&stats)) < 0) {
600     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
601     return NULL;
602     }
603 pajs 1.17 /* Not aware of a get all devices, so i said 999. If we ever
604     * find a machine with more than 999 disks, then i'll change
605     * this number :)
606     */
607 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) {
608     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
609     return NULL;
610     }
611 pajs 1.27 #endif
612 pajs 1.17
613     for(counter=0;counter<stats.dinfo->numdevs;counter++){
614     dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
615    
616     /* Throw away devices that have done nothing, ever.. Eg "odd"
617     * devices.. like mem, proc.. and also doesn't report floppy
618     * drives etc unless they are doing stuff :)
619     */
620 pajs 1.27 #ifdef FREEBSD5
621     if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
622     #else
623 pajs 1.17 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
624 pajs 1.27 #endif
625 ats 1.56
626     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
627 pajs 1.17 return NULL;
628     }
629     diskio_stats_ptr=diskio_stats+num_diskio;
630 pajs 1.27
631     #ifdef FREEBSD5
632     diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
633     diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
634     #else
635 pajs 1.17 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
636     diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
637 pajs 1.27 #endif
638 pajs 1.17 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
639 ats 1.60 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
640 ats 1.72 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
641 ats 1.60 return NULL;
642     }
643 pajs 1.17 diskio_stats_ptr->systime=time(NULL);
644    
645     num_diskio++;
646     }
647     free(dev_sel);
648    
649     #endif
650 pajs 1.10 #ifdef SOLARIS
651 pajs 1.4 if ((kc = kstat_open()) == NULL) {
652 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
653 tdb 1.65 return NULL;
654     }
655 pajs 1.4
656     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
657 tdb 1.65 if (!strcmp(ksp->ks_class, "disk")) {
658 pajs 1.4
659     if(ksp->ks_type != KSTAT_TYPE_IO) continue;
660 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
661 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
662 tdb 1.65 if((kstat_read(kc, ksp, &kios))==-1){
663 pajs 1.4 }
664    
665 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
666 pajs 1.4 kstat_close(kc);
667     return NULL;
668     }
669 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
670 pajs 1.4
671     diskio_stats_ptr->read_bytes=kios.nread;
672     diskio_stats_ptr->write_bytes=kios.nwritten;
673 ats 1.61 if (sg_update_string(&diskio_stats_ptr->disk_name,
674 tdb 1.65 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
675 ats 1.60 return NULL;
676     }
677     diskio_stats_ptr->systime=time(NULL);
678 pajs 1.4
679 pajs 1.5 num_diskio++;
680 pajs 1.4 }
681     }
682    
683     kstat_close(kc);
684 pajs 1.10 #endif
685 pajs 1.4
686 pajs 1.10 #ifdef LINUX
687 ats 1.20 num_diskio = 0;
688     n = 0;
689 pajs 1.10
690 ats 1.20 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
691 ats 1.38 have statistics in here too, so we can use those directly.
692     2.6 kernels have /proc/diskstats instead with almost (but not quite)
693     the same format. */
694    
695     f = fopen("/proc/diskstats", "r");
696 ats 1.54 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
697 ats 1.38 if (f == NULL) {
698     f = fopen("/proc/partitions", "r");
699 ats 1.54 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
700 ats 1.38 }
701 ats 1.20 if (f == NULL) goto out;
702     now = time(NULL);
703    
704 ats 1.81 if (!re_compiled) {
705     if (regcomp(&part_re, "^(.*/)?[^/]*[0-9]$", REG_EXTENDED | REG_NOSUB) != 0) {
706     sg_set_error(SG_ERROR_PARSE, NULL);
707     goto out;
708     }
709     if (regcomp(&not_part_re, "^(.*/)?[^/0-9]+[0-9]+d[0-9]+$", REG_EXTENDED | REG_NOSUB) != 0) {
710     sg_set_error(SG_ERROR_PARSE, NULL);
711     goto out;
712     }
713     re_compiled = 1;
714     }
715    
716 ats 1.61 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
717 ats 1.54 char name[100];
718 ats 1.20 long long rsect, wsect;
719    
720 ats 1.38 int nr = sscanf(line_ptr, format,
721 ats 1.20 &major, &minor, name, &rsect, &wsect);
722     if (nr < 3) continue;
723    
724     /* Skip device names ending in numbers, since they're
725 ats 1.81 partitions, unless they match the c0d0 pattern that some
726     RAID devices use. */
727     /* FIXME: For 2.6+, we should probably be using sysfs to detect
728     this... */
729     if ((regexec(&part_re, name, 0, NULL, 0) == 0)
730     && (regexec(&not_part_re, name, 0, NULL, 0) != 0)) {
731     continue;
732     }
733 ats 1.38
734     if (nr < 5) {
735     has_pp_stats = 0;
736     rsect = 0;
737     wsect = 0;
738     }
739 ats 1.20
740 ats 1.56 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
741     goto out;
742     }
743 ats 1.58 if (VECTOR_RESIZE(parts, n + 1) < 0) {
744     goto out;
745 pajs 1.11 }
746 pajs 1.12
747 ats 1.62 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
748 ats 1.60 goto out;
749     }
750 ats 1.20 diskio_stats[n].read_bytes = rsect * 512;
751     diskio_stats[n].write_bytes = wsect * 512;
752     diskio_stats[n].systime = now;
753     parts[n].major = major;
754     parts[n].minor = minor;
755 pajs 1.10
756 ats 1.20 n++;
757     }
758 pajs 1.10
759 ats 1.45 fclose(f);
760 ats 1.46 f = NULL;
761 pajs 1.44
762 ats 1.20 if (!has_pp_stats) {
763 ats 1.45 /* This is an older kernel where /proc/partitions doesn't
764     contain stats. Read what we can from /proc/stat instead, and
765     fill in the appropriate bits of the list allocated above. */
766 ats 1.20
767     f = fopen("/proc/stat", "r");
768     if (f == NULL) goto out;
769     now = time(NULL);
770    
771 ats 1.61 line_ptr = sg_f_read_line(f, "disk_io:");
772 ats 1.20 if (line_ptr == NULL) goto out;
773    
774     while((line_ptr=strchr(line_ptr, ' '))!=NULL){
775     long long rsect, wsect;
776    
777     if (*++line_ptr == '\0') break;
778    
779     if((sscanf(line_ptr,
780     "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
781     &major, &minor, &rsect, &wsect)) != 4) {
782     continue;
783     }
784 pajs 1.10
785 ats 1.20 /* Find the corresponding device from earlier.
786     Just to add to the fun, "minor" is actually the disk
787     number, not the device minor, so we need to figure
788     out the real minor number based on the major!
789     This list is not exhaustive; if you're running
790     an older kernel you probably don't have fancy
791     I2O hardware anyway... */
792     switch (major) {
793     case 3:
794     case 21:
795 pajs 1.10 case 22:
796 ats 1.20 case 33:
797     case 34:
798     case 36:
799     case 56:
800     case 57:
801     case 88:
802     case 89:
803     case 90:
804     case 91:
805     minor *= 64;
806 pajs 1.11 break;
807 ats 1.20 case 9:
808     case 43:
809 pajs 1.11 break;
810 pajs 1.10 default:
811 ats 1.20 minor *= 16;
812 pajs 1.11 break;
813 ats 1.20 }
814     for (i = 0; i < n; i++) {
815     if (major == parts[i].major
816     && minor == parts[i].minor)
817     break;
818     }
819     if (i == n) continue;
820    
821     /* We read the number of blocks. Blocks are stored in
822     512 bytes */
823     diskio_stats[i].read_bytes = rsect * 512;
824     diskio_stats[i].write_bytes = wsect * 512;
825     diskio_stats[i].systime = now;
826 pajs 1.10 }
827     }
828 pajs 1.16
829 ats 1.20 num_diskio = n;
830     out:
831     if (f != NULL) fclose(f);
832 ats 1.42 #endif
833 pajs 1.10
834 ats 1.42 #ifdef CYGWIN
835 tdb 1.66 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
836 ats 1.42 return NULL;
837 pajs 1.10 #endif
838 ats 1.42
839 pajs 1.5 *entries=num_diskio;
840 pajs 1.4
841     return diskio_stats;
842 pajs 1.5 }
843    
844 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
845     VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
846 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
847 ats 1.61 sg_disk_io_stats *src = NULL, *dest;
848 ats 1.56 int i, j, diff_count, new_count;
849 ats 1.50
850     if (diskio_stats == NULL) {
851     /* No previous stats, so we can't calculate a difference. */
852 ats 1.61 return sg_get_disk_io_stats(entries);
853 pajs 1.5 }
854    
855 ats 1.50 /* Resize the results array to match the previous stats. */
856 ats 1.56 diff_count = VECTOR_SIZE(diskio_stats);
857     if (VECTOR_RESIZE(diff, diff_count) < 0) {
858 pajs 1.5 return NULL;
859     }
860    
861 ats 1.50 /* Copy the previous stats into the result. */
862     for (i = 0; i < diff_count; i++) {
863     src = &diskio_stats[i];
864     dest = &diff[i];
865    
866 ats 1.62 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
867 ats 1.60 return NULL;
868 ats 1.50 }
869     dest->read_bytes = src->read_bytes;
870     dest->write_bytes = src->write_bytes;
871     dest->systime = src->systime;
872 pajs 1.5 }
873    
874 ats 1.50 /* Get a new set of stats. */
875 ats 1.61 if (sg_get_disk_io_stats(&new_count) == NULL) {
876 ats 1.47 return NULL;
877     }
878 pajs 1.5
879 ats 1.50 /* For each previous stat... */
880     for (i = 0; i < diff_count; i++) {
881     dest = &diff[i];
882    
883     /* ... find the corresponding new stat ... */
884     for (j = 0; j < new_count; j++) {
885     /* Try the new stat in the same position first,
886     since that's most likely to be it. */
887     src = &diskio_stats[(i + j) % new_count];
888     if (strcmp(src->disk_name, dest->disk_name) == 0) {
889     break;
890 pajs 1.5 }
891     }
892 ats 1.50 if (j == new_count) {
893     /* No match found. */
894     continue;
895     }
896 pajs 1.5
897 ats 1.50 /* ... and subtract the previous stat from it to get the
898     difference. */
899     dest->read_bytes = src->read_bytes - dest->read_bytes;
900     dest->write_bytes = src->write_bytes - dest->write_bytes;
901     dest->systime = src->systime - dest->systime;
902     }
903 pajs 1.5
904 ats 1.50 *entries = diff_count;
905     return diff;
906 ats 1.67 }
907    
908     int sg_disk_io_compare_name(const void *va, const void *vb) {
909     const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
910     const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
911    
912     return strcmp(a->disk_name, b->disk_name);
913 pajs 1.2 }
914 ats 1.50