ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.88
Committed: Fri Dec 4 22:09:30 2009 UTC (14 years, 5 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_17
Changes since 1.87: +31 -4 lines
Log Message:
Make list of valid filesystem types dynamic on FreeBSD. Based on patch
by Baptiste Daroussin.

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