ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.84
Committed: Fri Mar 17 13:29:26 2006 UTC (18 years, 2 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_13
Changes since 1.83: +3 -3 lines
Log Message:
Have nfs be a legal filesystem type on all platforms.

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.83 2005/09/24 13:29:22 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"}
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"}
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 return NULL;
760 }
761 diskio_stats_ptr->systime=time(NULL);
762
763 num_diskio++;
764 }
765 }
766
767 kstat_close(kc);
768 #endif
769
770 #ifdef LINUX
771 num_diskio = 0;
772 n = 0;
773
774 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
775 have statistics in here too, so we can use those directly.
776 2.6 kernels have /proc/diskstats instead with almost (but not quite)
777 the same format. */
778
779 f = fopen("/proc/diskstats", "r");
780 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
781 if (f == NULL) {
782 f = fopen("/proc/partitions", "r");
783 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
784 }
785 if (f == NULL) goto out;
786 now = time(NULL);
787
788 if (!re_compiled) {
789 if (regcomp(&part_re, "^(.*/)?[^/]*[0-9]$", REG_EXTENDED | REG_NOSUB) != 0) {
790 sg_set_error(SG_ERROR_PARSE, NULL);
791 goto out;
792 }
793 if (regcomp(&not_part_re, "^(.*/)?[^/0-9]+[0-9]+d[0-9]+$", REG_EXTENDED | REG_NOSUB) != 0) {
794 sg_set_error(SG_ERROR_PARSE, NULL);
795 goto out;
796 }
797 re_compiled = 1;
798 }
799
800 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
801 char name[100];
802 long long rsect, wsect;
803
804 int nr = sscanf(line_ptr, format,
805 &major, &minor, name, &rsect, &wsect);
806 if (nr < 3) continue;
807
808 /* Skip device names ending in numbers, since they're
809 partitions, unless they match the c0d0 pattern that some
810 RAID devices use. */
811 /* FIXME: For 2.6+, we should probably be using sysfs to detect
812 this... */
813 if ((regexec(&part_re, name, 0, NULL, 0) == 0)
814 && (regexec(&not_part_re, name, 0, NULL, 0) != 0)) {
815 continue;
816 }
817
818 if (nr < 5) {
819 has_pp_stats = 0;
820 rsect = 0;
821 wsect = 0;
822 }
823
824 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
825 goto out;
826 }
827 if (VECTOR_RESIZE(parts, n + 1) < 0) {
828 goto out;
829 }
830
831 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
832 goto out;
833 }
834 diskio_stats[n].read_bytes = rsect * 512;
835 diskio_stats[n].write_bytes = wsect * 512;
836 diskio_stats[n].systime = now;
837 parts[n].major = major;
838 parts[n].minor = minor;
839
840 n++;
841 }
842
843 fclose(f);
844 f = NULL;
845
846 if (!has_pp_stats) {
847 /* This is an older kernel where /proc/partitions doesn't
848 contain stats. Read what we can from /proc/stat instead, and
849 fill in the appropriate bits of the list allocated above. */
850
851 f = fopen("/proc/stat", "r");
852 if (f == NULL) goto out;
853 now = time(NULL);
854
855 line_ptr = sg_f_read_line(f, "disk_io:");
856 if (line_ptr == NULL) goto out;
857
858 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
859 long long rsect, wsect;
860
861 if (*++line_ptr == '\0') break;
862
863 if((sscanf(line_ptr,
864 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
865 &major, &minor, &rsect, &wsect)) != 4) {
866 continue;
867 }
868
869 /* Find the corresponding device from earlier.
870 Just to add to the fun, "minor" is actually the disk
871 number, not the device minor, so we need to figure
872 out the real minor number based on the major!
873 This list is not exhaustive; if you're running
874 an older kernel you probably don't have fancy
875 I2O hardware anyway... */
876 switch (major) {
877 case 3:
878 case 21:
879 case 22:
880 case 33:
881 case 34:
882 case 36:
883 case 56:
884 case 57:
885 case 88:
886 case 89:
887 case 90:
888 case 91:
889 minor *= 64;
890 break;
891 case 9:
892 case 43:
893 break;
894 default:
895 minor *= 16;
896 break;
897 }
898 for (i = 0; i < n; i++) {
899 if (major == parts[i].major
900 && minor == parts[i].minor)
901 break;
902 }
903 if (i == n) continue;
904
905 /* We read the number of blocks. Blocks are stored in
906 512 bytes */
907 diskio_stats[i].read_bytes = rsect * 512;
908 diskio_stats[i].write_bytes = wsect * 512;
909 diskio_stats[i].systime = now;
910 }
911 }
912
913 num_diskio = n;
914 out:
915 if (f != NULL) fclose(f);
916 #endif
917
918 #ifdef CYGWIN
919 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
920 return NULL;
921 #endif
922
923 #ifdef WIN32
924 sg_set_error(SG_ERROR_NONE, NULL);
925
926 while((name = get_diskio(num_diskio, &rbytes, &wbytes)) != NULL) {
927 if (VECTOR_RESIZE(diskio_stats, num_diskio+1)) {
928 return NULL;
929 }
930
931 diskio_stats_ptr = diskio_stats + num_diskio;
932
933 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
934 return NULL;
935 }
936 sg_update_string(&name, NULL);
937 diskio_stats_ptr->read_bytes = rbytes;
938 diskio_stats_ptr->write_bytes = wbytes;
939
940 diskio_stats_ptr->systime = 0;
941
942 num_diskio++;
943 }
944 #endif
945
946 *entries=num_diskio;
947
948 return diskio_stats;
949 }
950
951 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
952 #ifndef WIN32
953 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
954 diskio_stat_init, diskio_stat_destroy);
955 sg_disk_io_stats *src = NULL, *dest;
956 int i, j, diff_count, new_count;
957
958 if (diskio_stats == NULL) {
959 /* No previous stats, so we can't calculate a difference. */
960 return sg_get_disk_io_stats(entries);
961 }
962
963 /* Resize the results array to match the previous stats. */
964 diff_count = VECTOR_SIZE(diskio_stats);
965 if (VECTOR_RESIZE(diff, diff_count) < 0) {
966 return NULL;
967 }
968
969 /* Copy the previous stats into the result. */
970 for (i = 0; i < diff_count; i++) {
971 src = &diskio_stats[i];
972 dest = &diff[i];
973
974 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
975 return NULL;
976 }
977 dest->read_bytes = src->read_bytes;
978 dest->write_bytes = src->write_bytes;
979 dest->systime = src->systime;
980 }
981
982 /* Get a new set of stats. */
983 if (sg_get_disk_io_stats(&new_count) == NULL) {
984 return NULL;
985 }
986
987 /* For each previous stat... */
988 for (i = 0; i < diff_count; i++) {
989 dest = &diff[i];
990
991 /* ... find the corresponding new stat ... */
992 for (j = 0; j < new_count; j++) {
993 /* Try the new stat in the same position first,
994 since that's most likely to be it. */
995 src = &diskio_stats[(i + j) % new_count];
996 if (strcmp(src->disk_name, dest->disk_name) == 0) {
997 break;
998 }
999 }
1000 if (j == new_count) {
1001 /* No match found. */
1002 continue;
1003 }
1004
1005 /* ... and subtract the previous stat from it to get the
1006 difference. */
1007 dest->read_bytes = src->read_bytes - dest->read_bytes;
1008 dest->write_bytes = src->write_bytes - dest->write_bytes;
1009 dest->systime = src->systime - dest->systime;
1010 }
1011
1012 *entries = diff_count;
1013 return diff;
1014 #else /* WIN32 */
1015 return sg_get_disk_io_stats(entries);
1016 #endif
1017 }
1018
1019 int sg_disk_io_compare_name(const void *va, const void *vb) {
1020 const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
1021 const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
1022
1023 return strcmp(a->disk_name, b->disk_name);
1024 }
1025