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

# 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.87 2009/05/16 01:01:31 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", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
52 "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
53 "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
54 "umsdos", "vfat", "xfs", "jfs", "nfs"}
55 #endif
56
57 #ifdef CYGWIN
58 #define VALID_FS_TYPES {"user"}
59 #endif
60
61 #ifdef ALLBSD
62 #include <sys/param.h>
63 #include <sys/ucred.h>
64 #include <sys/mount.h>
65 #endif
66 #if defined(FREEBSD) || defined(DFBSD)
67 #include <sys/dkstat.h>
68 #include <devstat.h>
69 #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 #endif
76 #if defined(NETBSD) || defined(OPENBSD)
77 #include <sys/param.h>
78 #include <sys/sysctl.h>
79 #include <sys/disk.h>
80 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
81 "ntfs", "nfs"}
82 #endif
83
84 #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 #define VALID_FS_TYPES {"vxfs", "hfs", "nfs"}
95 #define DISK_BATCH 30
96 #endif
97
98 #ifdef WIN32
99 #include "win32.h"
100 /*#define VALID_FS_TYPES {"NTFS", "FAT", "FAT32"} unused*/
101 #define BUFSIZE 512
102 #endif
103
104 #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 static void disk_stat_init(sg_fs_stats *d) {
145 d->device_name = NULL;
146 d->fs_type = NULL;
147 d->mnt_point = NULL;
148 }
149
150 static void disk_stat_destroy(sg_fs_stats *d) {
151 free(d->device_name);
152 free(d->fs_type);
153 free(d->mnt_point);
154 }
155
156 #ifndef WIN32 /* not used by WIN32, so stop compiler throwing warnings */
157 static int is_valid_fs_type(const char *type) {
158 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 const char *types[] = VALID_FS_TYPES;
182
183 for (i = 0; i < (int) (sizeof types / sizeof *types); i++) {
184 if (strcmp(types[i], type) == 0) {
185 return 1;
186 }
187 }
188 #endif
189 return 0;
190 }
191 #endif
192
193 sg_fs_stats *sg_get_fs_stats(int *entries){
194 VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10,
195 disk_stat_init, disk_stat_destroy);
196
197 int num_disks=0;
198 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN) || defined(HPUX)
199 FILE *f;
200 #endif
201
202 sg_fs_stats *disk_ptr;
203
204 #ifdef SOLARIS
205 struct mnttab mp;
206 struct statvfs fs;
207 #endif
208 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
209 struct mntent *mp;
210 struct statvfs fs;
211 #endif
212 #ifdef ALLBSD
213 int nummnt;
214 #ifdef HAVE_STATVFS
215 struct statvfs *mp, **fs;
216 #else
217 struct statfs *mp, **fs;
218 #endif
219 #endif
220 #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
229 #ifdef ALLBSD
230 nummnt=getmntinfo(&mp, MNT_WAIT);
231 if (nummnt<=0){
232 sg_set_error_with_errno(SG_ERROR_GETMNTINFO, NULL);
233 return NULL;
234 }
235 for(fs = &mp; nummnt--; (*fs)++){
236 #endif
237
238 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
239 #ifdef MNT_MNTTAB
240 if ((f=setmntent(MNT_MNTTAB, "r" ))==NULL){
241 #else
242 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
243 #endif
244 sg_set_error(SG_ERROR_SETMNTENT, NULL);
245 return NULL;
246 }
247
248 while((mp=getmntent(f))){
249 if((statvfs(mp->mnt_dir, &fs)) !=0){
250 continue;
251 }
252
253 #endif
254
255 #ifdef SOLARIS
256 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
257 sg_set_error_with_errno(SG_ERROR_OPEN, "/etc/mnttab");
258 return NULL;
259 }
260 while((getmntent(f, &mp)) == 0){
261 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
262 continue;
263 }
264 #endif
265
266 #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 if(is_valid_fs_type(SG_MP_FSTYPENAME(mp))){
281 #endif
282 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
283 return NULL;
284 }
285 disk_ptr=disk_stats+num_disks;
286
287 #ifndef WIN32
288 /* 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 return NULL;
294 }
295 if (sg_update_string(&disk_ptr->fs_type, SG_MP_FSTYPENAME(mp)) < 0) {
296 return NULL;
297 }
298 if (sg_update_string(&disk_ptr->mnt_point, SG_MP_MOUNTP(mp)) < 0) {
299 return NULL;
300 }
301
302 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
306 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
312 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 #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
327 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 num_disks++;
361 }
362 #ifdef WIN32
363 while(*p++);
364 } while(*p);
365 #else
366 }
367 #endif
368
369 *entries=num_disks;
370
371 /* If this fails, there is very little i can do about it, so
372 I'll ignore it :) */
373 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
374 endmntent(f);
375 #endif
376 #if defined(SOLARIS)
377 fclose(f);
378 #endif
379
380 return disk_stats;
381
382 }
383
384 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 static void diskio_stat_init(sg_disk_io_stats *d) {
399 d->disk_name = NULL;
400 }
401
402 static void diskio_stat_destroy(sg_disk_io_stats *d) {
403 free(d->disk_name);
404 }
405
406 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
407 diskio_stat_init, diskio_stat_destroy);
408
409 #ifdef LINUX
410 typedef struct {
411 int major;
412 int minor;
413 } partition;
414 #endif
415
416 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
417 int num_diskio;
418 #ifndef LINUX
419 sg_disk_io_stats *diskio_stats_ptr;
420 #endif
421
422 #ifdef HPUX
423 long long rbytes = 0, wbytes = 0;
424 struct dirent *dinfo = NULL;
425 struct stat lstatinfo;
426 struct pst_diskinfo pstat_diskinfo[DISK_BATCH];
427 char fullpathbuf[1024] = {0};
428 dev_t diskid;
429 DIR *dh = NULL;
430 int diskidx = 0;
431 int num, i;
432 #endif
433 #ifdef SOLARIS
434 kstat_ctl_t *kc;
435 kstat_t *ksp;
436 kstat_io_t kios;
437 #endif
438 #ifdef LINUX
439 FILE *f;
440 char *line_ptr;
441 int major, minor;
442 int has_pp_stats = 1;
443 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
444 int i, n;
445 time_t now;
446 const char *format;
447 static regex_t not_part_re, part_re;
448 static int re_compiled = 0;
449 #endif
450 #if defined(FREEBSD) || defined(DFBSD)
451 static struct statinfo stats;
452 static int stats_init = 0;
453 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 #endif
459 #ifdef NETBSD
460 struct disk_sysctl *stats;
461 #endif
462 #ifdef OPENBSD
463 int diskcount;
464 char *disknames, *name, *bufpp;
465 char **dk_name;
466 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 int num_disks, i;
476 int mib[MIBSIZE];
477 size_t size;
478 #endif
479 #ifdef WIN32
480 char *name;
481 long long rbytes;
482 long long wbytes;
483 #endif
484
485 num_diskio=0;
486
487 #ifdef HPUX
488 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 break;
497 }
498
499 for (i = 0; i < num; i++) {
500 struct pst_diskinfo *di = &pstat_diskinfo[i];
501
502 /* 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 * 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
514 /* Skip unused disks. */
515 if (rbytes == 0 && wbytes == 0) {
516 continue;
517 }
518
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 /* FIXME This should use a static cache, like the Linux
533 * code below. */
534 if (diskio_stats_ptr->disk_name == NULL) {
535 dh = opendir("/dev/dsk");
536 if (dh == NULL) {
537 continue;
538 }
539
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 }
558 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 return NULL;
563 }
564 }
565 }
566 }
567 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
568 }
569 #endif
570 #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 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
577 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 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
585 return NULL;
586 }
587
588 disknames = sg_malloc(size);
589 if (disknames == NULL) {
590 return NULL;
591 }
592
593 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
594 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
595 return NULL;
596 }
597
598 dk_name = sg_malloc(diskcount * sizeof(char *));
599 bufpp = disknames;
600 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
601 dk_name[i] = name;
602 }
603 #endif
604
605 #if defined(NETBSD) || defined(OPENBSD)
606 mib[0] = CTL_HW;
607 mib[1] = HW_DISKSTATS;
608 #ifdef NETBSD
609 mib[2] = sizeof(struct disk_sysctl);
610 #endif
611
612 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
613 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
614 return NULL;
615 }
616
617 #ifdef NETBSD
618 num_disks = size / sizeof(struct disk_sysctl);
619 #else
620 num_disks = size / sizeof(struct diskstats);
621 #endif
622
623 stats = sg_malloc(size);
624 if (stats == NULL) {
625 return NULL;
626 }
627
628 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
629 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
630 return NULL;
631 }
632
633 for (i = 0; i < num_disks; i++) {
634 const char *name;
635 u_int64_t rbytes, wbytes;
636
637 #ifdef NETBSD
638 #ifdef HAVE_DK_RBYTES
639 rbytes = stats[i].dk_rbytes;
640 wbytes = stats[i].dk_wbytes;
641 #else
642 /* Before 2.0, NetBSD merged reads and writes. */
643 rbytes = wbytes = stats[i].dk_bytes;
644 #endif
645 #else
646 #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 rbytes = wbytes = stats[i].ds_bytes;
652 #endif
653 #endif
654
655 /* Don't keep stats for disks that have never been used. */
656 if (rbytes == 0 && wbytes == 0) {
657 continue;
658 }
659
660 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
661 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 #ifdef NETBSD
668 name = stats[i].dk_name;
669 #else
670 name = dk_name[i];
671 #endif
672 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
673 return NULL;
674 }
675 diskio_stats_ptr->systime = time(NULL);
676
677 num_diskio++;
678 }
679
680 free(stats);
681 #ifdef OPENBSD
682 free(dk_name);
683 free(disknames);
684 #endif
685 #endif
686
687 #if defined(FREEBSD) || defined(DFBSD)
688 if (!stats_init) {
689 stats.dinfo=sg_malloc(sizeof(struct devinfo));
690 if(stats.dinfo==NULL) return NULL;
691 bzero(stats.dinfo, sizeof(struct devinfo));
692 stats_init = 1;
693 }
694 #ifdef FREEBSD5
695 if ((devstat_getdevs(NULL, &stats)) < 0) {
696 /* FIXME devstat functions return a string error in
697 devstat_errbuf */
698 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
699 return NULL;
700 }
701 /* 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 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 #else
710 if ((getdevs(&stats)) < 0) {
711 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
712 return NULL;
713 }
714 /* 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 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 #endif
723
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 #ifdef FREEBSD5
732 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
733 #else
734 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
735 #endif
736
737 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
738 return NULL;
739 }
740 diskio_stats_ptr=diskio_stats+num_diskio;
741
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 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
747 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
748 #endif
749 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
750 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
751 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
752 return NULL;
753 }
754 diskio_stats_ptr->systime=time(NULL);
755
756 num_diskio++;
757 }
758 free(dev_sel);
759
760 #endif
761 #ifdef SOLARIS
762 if ((kc = kstat_open()) == NULL) {
763 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
764 return NULL;
765 }
766
767 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
768 if (!strcmp(ksp->ks_class, "disk")) {
769
770 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
771 /* We dont want metadevices appearins as num_diskio */
772 if(strcmp(ksp->ks_module, "md")==0) continue;
773 if((kstat_read(kc, ksp, &kios))==-1){
774 }
775
776 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
777 kstat_close(kc);
778 return NULL;
779 }
780 diskio_stats_ptr=diskio_stats+num_diskio;
781
782 diskio_stats_ptr->read_bytes=kios.nread;
783 diskio_stats_ptr->write_bytes=kios.nwritten;
784 if (sg_update_string(&diskio_stats_ptr->disk_name,
785 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
786 kstat_close(kc);
787 return NULL;
788 }
789 diskio_stats_ptr->systime=time(NULL);
790
791 num_diskio++;
792 }
793 }
794
795 kstat_close(kc);
796 #endif
797
798 #ifdef LINUX
799 num_diskio = 0;
800 n = 0;
801
802 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
803 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 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
809 if (f == NULL) {
810 f = fopen("/proc/partitions", "r");
811 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
812 }
813 if (f == NULL) goto out;
814 now = time(NULL);
815
816 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 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
829 char name[100];
830 long long rsect, wsect;
831
832 int nr = sscanf(line_ptr, format,
833 &major, &minor, name, &rsect, &wsect);
834 if (nr < 3) continue;
835
836 /* Skip device names ending in numbers, since they're
837 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
846 if (nr < 5) {
847 has_pp_stats = 0;
848 rsect = 0;
849 wsect = 0;
850 }
851
852 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
853 goto out;
854 }
855 if (VECTOR_RESIZE(parts, n + 1) < 0) {
856 goto out;
857 }
858
859 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
860 goto out;
861 }
862 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
868 n++;
869 }
870
871 fclose(f);
872 f = NULL;
873
874 if (!has_pp_stats) {
875 /* 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
879 f = fopen("/proc/stat", "r");
880 if (f == NULL) goto out;
881 now = time(NULL);
882
883 line_ptr = sg_f_read_line(f, "disk_io:");
884 if (line_ptr == NULL) goto out;
885
886 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
887 long long rsect, wsect;
888
889 if (*++line_ptr == '\0') break;
890
891 if((sscanf(line_ptr,
892 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
893 &major, &minor, &rsect, &wsect)) != 4) {
894 continue;
895 }
896
897 /* 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 case 22:
908 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 break;
919 case 9:
920 case 43:
921 break;
922 default:
923 minor *= 16;
924 break;
925 }
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 }
939 }
940
941 num_diskio = n;
942 out:
943 if (f != NULL) fclose(f);
944 #endif
945
946 #ifdef CYGWIN
947 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
948 return NULL;
949 #endif
950
951 #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 *entries=num_diskio;
975
976 return diskio_stats;
977 }
978
979 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
980 #ifndef WIN32
981 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
982 diskio_stat_init, diskio_stat_destroy);
983 sg_disk_io_stats *src = NULL, *dest;
984 int i, j, diff_count, new_count;
985
986 if (diskio_stats == NULL) {
987 /* No previous stats, so we can't calculate a difference. */
988 return sg_get_disk_io_stats(entries);
989 }
990
991 /* Resize the results array to match the previous stats. */
992 diff_count = VECTOR_SIZE(diskio_stats);
993 if (VECTOR_RESIZE(diff, diff_count) < 0) {
994 return NULL;
995 }
996
997 /* 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 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
1003 return NULL;
1004 }
1005 dest->read_bytes = src->read_bytes;
1006 dest->write_bytes = src->write_bytes;
1007 dest->systime = src->systime;
1008 }
1009
1010 /* Get a new set of stats. */
1011 if (sg_get_disk_io_stats(&new_count) == NULL) {
1012 return NULL;
1013 }
1014
1015 /* 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 }
1027 }
1028 if (j == new_count) {
1029 /* No match found. */
1030 continue;
1031 }
1032
1033 /* ... 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
1040 *entries = diff_count;
1041 return diff;
1042 #else /* WIN32 */
1043 return sg_get_disk_io_stats(entries);
1044 #endif
1045 }
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 }
1053