ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.87
Committed: Sat May 16 01:01:31 2009 UTC (15 years ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.86: +2 -2 lines
Log Message:
FreeBSD has zfs these days.

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