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, 6 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

# Content
1 /*
2 * i-scream libstatgrab
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * 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 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * 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 *
21 * $Id: disk_stats.c,v 1.88 2009/12/04 22:09:30 tdb Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include "statgrab.h"
33 #include "vector.h"
34 #include "tools.h"
35
36 #ifdef SOLARIS
37 #include <sys/mnttab.h>
38 #include <sys/statvfs.h>
39 #include <kstat.h>
40 #define VALID_FS_TYPES {"ufs", "tmpfs", "vxfs", "nfs", "zfs"}
41 #endif
42
43 #if defined(LINUX) || defined(CYGWIN)
44 #include <mntent.h>
45 #include <sys/vfs.h>
46 #include <sys/statvfs.h>
47 #endif
48
49 #ifdef LINUX
50 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
51 "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 #endif
57
58 #ifdef CYGWIN
59 #define VALID_FS_TYPES {"user"}
60 #endif
61
62 #ifdef ALLBSD
63 #include <sys/param.h>
64 #include <sys/ucred.h>
65 #include <sys/mount.h>
66 #endif
67 #if defined(FREEBSD) || defined(DFBSD)
68 #include <sys/dkstat.h>
69 #include <devstat.h>
70 #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 static char **VALID_FS_TYPES;
77 static size_t n_valid_fs_types;
78 #endif
79 #if defined(NETBSD) || defined(OPENBSD)
80 #include <sys/param.h>
81 #include <sys/sysctl.h>
82 #include <sys/disk.h>
83 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
84 "ntfs", "nfs"}
85 #endif
86
87 #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 #define VALID_FS_TYPES {"vxfs", "hfs", "nfs"}
98 #define DISK_BATCH 30
99 #endif
100
101 #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 #ifdef WIN32
156 #include "win32.h"
157 /*#define VALID_FS_TYPES {"NTFS", "FAT", "FAT32"} unused*/
158 #define BUFSIZE 512
159 #endif
160
161 #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 #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
214 static void disk_stat_init(sg_fs_stats *d) {
215 d->device_name = NULL;
216 d->fs_type = NULL;
217 d->mnt_point = NULL;
218 }
219
220 static void disk_stat_destroy(sg_fs_stats *d) {
221 free(d->device_name);
222 free(d->fs_type);
223 free(d->mnt_point);
224 }
225
226 static int valid_fs_types_initialized = 0;
227
228 static int init_valid_fs_types() {
229 #if defined(FREEBSD) || defined(DFBSD)
230 struct xvfsconf *xvfsp;
231 size_t buflen;
232 int nbvfs = 0;
233
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 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 }
271
272 fclose(fh);
273 }
274 return 1;
275 #else
276 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 const char *types[] = VALID_FS_TYPES;
288 size_t n_valid_fs_types = sizeof types / sizeof *types;
289 #else
290 char **types = VALID_FS_TYPES;
291 #endif
292
293 for (i = 0; i < n_valid_fs_types; i++) {
294 if( types[i] == NULL) {
295 continue;
296 }
297 if (strcmp(types[i], type) == 0) {
298 return 1;
299 }
300 }
301
302 return 0;
303 }
304 #endif
305
306 sg_fs_stats *sg_get_fs_stats(int *entries){
307 VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10,
308 disk_stat_init, disk_stat_destroy);
309
310 int num_disks=0;
311 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN) || defined(HPUX)
312 FILE *f;
313 #endif
314
315 sg_fs_stats *disk_ptr;
316
317 #ifdef SOLARIS
318 struct mnttab mp;
319 struct statvfs fs;
320 #endif
321 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
322 struct mntent *mp;
323 struct statvfs fs;
324 #endif
325 #ifdef ALLBSD
326 int nummnt;
327 #ifdef HAVE_STATVFS
328 struct statvfs *mp, **fs;
329 #else
330 struct statfs *mp, **fs;
331 #endif
332 #endif
333 #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 #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
353 #ifdef ALLBSD
354 nummnt=getmntinfo(&mp, MNT_WAIT);
355 if (nummnt<=0){
356 sg_set_error_with_errno(SG_ERROR_GETMNTINFO, NULL);
357 return NULL;
358 }
359 for(fs = &mp; nummnt--; (*fs)++){
360 #endif
361
362 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
363 #ifdef MNT_MNTTAB
364 if ((f=setmntent(MNT_MNTTAB, "r" ))==NULL){
365 #else
366 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
367 #endif
368 sg_set_error(SG_ERROR_SETMNTENT, NULL);
369 return NULL;
370 }
371
372 while((mp=getmntent(f))){
373 if((statvfs(mp->mnt_dir, &fs)) !=0){
374 continue;
375 }
376
377 #endif
378
379 #ifdef SOLARIS
380 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
381 sg_set_error_with_errno(SG_ERROR_OPEN, "/etc/mnttab");
382 return NULL;
383 }
384 while((getmntent(f, &mp)) == 0){
385 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
386 continue;
387 }
388 #endif
389
390 #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 #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 if(is_valid_fs_type(SG_MP_FSTYPENAME(mp))){
446 #endif
447 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
448 return NULL;
449 }
450 disk_ptr=disk_stats+num_disks;
451
452 #ifndef WIN32
453 /* 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 return NULL;
459 }
460 if (sg_update_string(&disk_ptr->fs_type, SG_MP_FSTYPENAME(mp)) < 0) {
461 return NULL;
462 }
463 if (sg_update_string(&disk_ptr->mnt_point, SG_MP_MOUNTP(mp)) < 0) {
464 return NULL;
465 }
466
467 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
471 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
477 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 #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
492 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 num_disks++;
526 }
527 #ifdef WIN32
528 while(*p++);
529 } while(*p);
530 #else
531 }
532 #endif
533
534 *entries=num_disks;
535
536 /* If this fails, there is very little i can do about it, so
537 I'll ignore it :) */
538 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
539 endmntent(f);
540 #endif
541 #if defined(SOLARIS)
542 fclose(f);
543 #endif
544
545 return disk_stats;
546
547 }
548
549 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 static void diskio_stat_init(sg_disk_io_stats *d) {
564 d->disk_name = NULL;
565 }
566
567 static void diskio_stat_destroy(sg_disk_io_stats *d) {
568 free(d->disk_name);
569 }
570
571 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
572 diskio_stat_init, diskio_stat_destroy);
573
574 #ifdef LINUX
575 typedef struct {
576 int major;
577 int minor;
578 } partition;
579 #endif
580
581 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
582 int num_diskio;
583 #ifndef LINUX
584 sg_disk_io_stats *diskio_stats_ptr;
585 #endif
586
587 #ifdef HPUX
588 long long rbytes = 0, wbytes = 0;
589 struct dirent *dinfo = NULL;
590 struct stat lstatinfo;
591 struct pst_diskinfo pstat_diskinfo[DISK_BATCH];
592 char fullpathbuf[1024] = {0};
593 dev_t diskid;
594 DIR *dh = NULL;
595 int diskidx = 0;
596 int num, i;
597 #endif
598 #ifdef SOLARIS
599 kstat_ctl_t *kc;
600 kstat_t *ksp;
601 kstat_io_t kios;
602 #endif
603 #ifdef LINUX
604 FILE *f;
605 char *line_ptr;
606 int major, minor;
607 int has_pp_stats = 1;
608 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
609 int i, n;
610 time_t now;
611 const char *format;
612 static regex_t not_part_re, part_re;
613 static int re_compiled = 0;
614 #endif
615 #if defined(FREEBSD) || defined(DFBSD)
616 static struct statinfo stats;
617 static int stats_init = 0;
618 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 #endif
624 #ifdef NETBSD
625 struct disk_sysctl *stats;
626 #endif
627 #ifdef OPENBSD
628 int diskcount;
629 char *disknames, *name, *bufpp;
630 char **dk_name;
631 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 int num_disks, i;
641 int mib[MIBSIZE];
642 size_t size;
643 #endif
644 #ifdef AIX
645 int ret, disks;
646 perfstat_disk_t *dskperf;
647 perfstat_id_t name;
648 #endif
649 #ifdef WIN32
650 char *name;
651 long long rbytes;
652 long long wbytes;
653 #endif
654
655 num_diskio=0;
656
657 #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 #ifdef HPUX
708 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 break;
717 }
718
719 for (i = 0; i < num; i++) {
720 struct pst_diskinfo *di = &pstat_diskinfo[i];
721
722 /* 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 * 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
734 /* Skip unused disks. */
735 if (rbytes == 0 && wbytes == 0) {
736 continue;
737 }
738
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 /* FIXME This should use a static cache, like the Linux
753 * code below. */
754 if (diskio_stats_ptr->disk_name == NULL) {
755 dh = opendir("/dev/dsk");
756 if (dh == NULL) {
757 continue;
758 }
759
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 }
778 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 return NULL;
783 }
784 }
785 }
786 }
787 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
788 }
789 #endif
790 #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 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
797 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 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
805 return NULL;
806 }
807
808 disknames = sg_malloc(size);
809 if (disknames == NULL) {
810 return NULL;
811 }
812
813 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
814 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
815 return NULL;
816 }
817
818 dk_name = sg_malloc(diskcount * sizeof(char *));
819 bufpp = disknames;
820 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
821 dk_name[i] = name;
822 }
823 #endif
824
825 #if defined(NETBSD) || defined(OPENBSD)
826 mib[0] = CTL_HW;
827 mib[1] = HW_DISKSTATS;
828 #ifdef NETBSD
829 mib[2] = sizeof(struct disk_sysctl);
830 #endif
831
832 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
833 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
834 return NULL;
835 }
836
837 #ifdef NETBSD
838 num_disks = size / sizeof(struct disk_sysctl);
839 #else
840 num_disks = size / sizeof(struct diskstats);
841 #endif
842
843 stats = sg_malloc(size);
844 if (stats == NULL) {
845 return NULL;
846 }
847
848 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
849 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
850 return NULL;
851 }
852
853 for (i = 0; i < num_disks; i++) {
854 const char *name;
855 u_int64_t rbytes, wbytes;
856
857 #ifdef NETBSD
858 #ifdef HAVE_DK_RBYTES
859 rbytes = stats[i].dk_rbytes;
860 wbytes = stats[i].dk_wbytes;
861 #else
862 /* Before 2.0, NetBSD merged reads and writes. */
863 rbytes = wbytes = stats[i].dk_bytes;
864 #endif
865 #else
866 #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 rbytes = wbytes = stats[i].ds_bytes;
872 #endif
873 #endif
874
875 /* Don't keep stats for disks that have never been used. */
876 if (rbytes == 0 && wbytes == 0) {
877 continue;
878 }
879
880 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
881 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 #ifdef NETBSD
888 name = stats[i].dk_name;
889 #else
890 name = dk_name[i];
891 #endif
892 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
893 return NULL;
894 }
895 diskio_stats_ptr->systime = time(NULL);
896
897 num_diskio++;
898 }
899
900 free(stats);
901 #ifdef OPENBSD
902 free(dk_name);
903 free(disknames);
904 #endif
905 #endif
906
907 #if defined(FREEBSD) || defined(DFBSD)
908 if (!stats_init) {
909 stats.dinfo=sg_malloc(sizeof(struct devinfo));
910 if(stats.dinfo==NULL) return NULL;
911 bzero(stats.dinfo, sizeof(struct devinfo));
912 stats_init = 1;
913 }
914 #ifdef FREEBSD5
915 if ((devstat_getdevs(NULL, &stats)) < 0) {
916 /* FIXME devstat functions return a string error in
917 devstat_errbuf */
918 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
919 return NULL;
920 }
921 /* 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 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 #else
930 if ((getdevs(&stats)) < 0) {
931 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
932 return NULL;
933 }
934 /* 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 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 #endif
943
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 #ifdef FREEBSD5
952 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
953 #else
954 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
955 #endif
956
957 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
958 return NULL;
959 }
960 diskio_stats_ptr=diskio_stats+num_diskio;
961
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 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
967 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
968 #endif
969 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
970 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
971 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
972 return NULL;
973 }
974 diskio_stats_ptr->systime=time(NULL);
975
976 num_diskio++;
977 }
978 free(dev_sel);
979
980 #endif
981 #ifdef SOLARIS
982 if ((kc = kstat_open()) == NULL) {
983 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
984 return NULL;
985 }
986
987 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
988 if (!strcmp(ksp->ks_class, "disk")) {
989
990 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
991 /* We dont want metadevices appearins as num_diskio */
992 if(strcmp(ksp->ks_module, "md")==0) continue;
993 if((kstat_read(kc, ksp, &kios))==-1){
994 }
995
996 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
997 kstat_close(kc);
998 return NULL;
999 }
1000 diskio_stats_ptr=diskio_stats+num_diskio;
1001
1002 diskio_stats_ptr->read_bytes=kios.nread;
1003 diskio_stats_ptr->write_bytes=kios.nwritten;
1004 if (sg_update_string(&diskio_stats_ptr->disk_name,
1005 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
1006 kstat_close(kc);
1007 return NULL;
1008 }
1009 diskio_stats_ptr->systime=time(NULL);
1010
1011 num_diskio++;
1012 }
1013 }
1014
1015 kstat_close(kc);
1016 #endif
1017
1018 #ifdef LINUX
1019 num_diskio = 0;
1020 n = 0;
1021
1022 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
1023 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 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
1029 if (f == NULL) {
1030 f = fopen("/proc/partitions", "r");
1031 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
1032 }
1033 if (f == NULL) goto out;
1034 now = time(NULL);
1035
1036 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 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
1049 char name[100];
1050 long long rsect, wsect;
1051
1052 int nr = sscanf(line_ptr, format,
1053 &major, &minor, name, &rsect, &wsect);
1054 if (nr < 3) continue;
1055
1056 /* Skip device names ending in numbers, since they're
1057 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
1066 if (nr < 5) {
1067 has_pp_stats = 0;
1068 rsect = 0;
1069 wsect = 0;
1070 }
1071
1072 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
1073 goto out;
1074 }
1075 if (VECTOR_RESIZE(parts, n + 1) < 0) {
1076 goto out;
1077 }
1078
1079 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
1080 goto out;
1081 }
1082 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
1088 n++;
1089 }
1090
1091 fclose(f);
1092 f = NULL;
1093
1094 if (!has_pp_stats) {
1095 /* 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
1099 f = fopen("/proc/stat", "r");
1100 if (f == NULL) goto out;
1101 now = time(NULL);
1102
1103 line_ptr = sg_f_read_line(f, "disk_io:");
1104 if (line_ptr == NULL) goto out;
1105
1106 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
1107 long long rsect, wsect;
1108
1109 if (*++line_ptr == '\0') break;
1110
1111 if((sscanf(line_ptr,
1112 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
1113 &major, &minor, &rsect, &wsect)) != 4) {
1114 continue;
1115 }
1116
1117 /* 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 case 22:
1128 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 break;
1139 case 9:
1140 case 43:
1141 break;
1142 default:
1143 minor *= 16;
1144 break;
1145 }
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 }
1159 }
1160
1161 num_diskio = n;
1162 out:
1163 if (f != NULL) fclose(f);
1164 #endif
1165
1166 #ifdef CYGWIN
1167 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
1168 return NULL;
1169 #endif
1170
1171 #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 *entries=num_diskio;
1195
1196 return diskio_stats;
1197 }
1198
1199 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
1200 #ifndef WIN32
1201 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
1202 diskio_stat_init, diskio_stat_destroy);
1203 sg_disk_io_stats *src = NULL, *dest;
1204 int i, j, diff_count, new_count;
1205
1206 if (diskio_stats == NULL) {
1207 /* No previous stats, so we can't calculate a difference. */
1208 return sg_get_disk_io_stats(entries);
1209 }
1210
1211 /* Resize the results array to match the previous stats. */
1212 diff_count = VECTOR_SIZE(diskio_stats);
1213 if (VECTOR_RESIZE(diff, diff_count) < 0) {
1214 return NULL;
1215 }
1216
1217 /* 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 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
1223 return NULL;
1224 }
1225 dest->read_bytes = src->read_bytes;
1226 dest->write_bytes = src->write_bytes;
1227 dest->systime = src->systime;
1228 }
1229
1230 /* Get a new set of stats. */
1231 if (sg_get_disk_io_stats(&new_count) == NULL) {
1232 return NULL;
1233 }
1234
1235 /* 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 }
1247 }
1248 if (j == new_count) {
1249 /* No match found. */
1250 continue;
1251 }
1252
1253 /* ... 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
1260 *entries = diff_count;
1261 return diff;
1262 #else /* WIN32 */
1263 return sg_get_disk_io_stats(entries);
1264 #endif
1265 }
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 }
1273