ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.75
Committed: Sat Nov 6 15:32:33 2004 UTC (19 years, 6 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.74: +67 -61 lines
Log Message:
Rework the HP-UX disk stats code to fetch batches of disks. (Untested.)

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