ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.89
Committed: Sun Oct 3 18:35:57 2010 UTC (13 years, 7 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.88: +235 -15 lines
Log Message:
Add support for AIX 5.x - 9.x.

Many thanks to Jens Rehsack <rehsack@googlemail.com> for providing the
patch for this work. Thanks!

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