ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.73
Committed: Mon Nov 1 18:30:17 2004 UTC (19 years, 6 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.72: +107 -6 lines
Log Message:
Merge in patch to provide support for HP-UX 11.11.

Contributed by Roy Keene - thanks Roy!

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