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

# User Rev Content
1 tdb 1.48 /*
2 tdb 1.63 * i-scream libstatgrab
3 tdb 1.18 * http://www.i-scream.org
4 tdb 1.48 * Copyright (C) 2000-2004 i-scream
5 pajs 1.1 *
6 tdb 1.48 * This library is free software; you can redistribute it and/or
7     * modify it under the terms of the GNU Lesser General Public
8     * License as published by the Free Software Foundation; either
9     * version 2.1 of the License, or (at your option) any later version.
10 pajs 1.1 *
11 tdb 1.48 * This library is distributed in the hope that it will be useful,
12 pajs 1.1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 tdb 1.48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * Lesser General Public License for more details.
15 pajs 1.1 *
16 tdb 1.48 * You should have received a copy of the GNU Lesser General Public
17     * License along with this library; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19     * 02111-1307 USA
20 tdb 1.49 *
21 tdb 1.87 * $Id: disk_stats.c,v 1.86 2008/08/03 17:43:26 tdb Exp $
22 pajs 1.1 */
23    
24     #ifdef HAVE_CONFIG_H
25     #include "config.h"
26     #endif
27    
28 pajs 1.17 #include <stdio.h>
29 pajs 1.1 #include <stdlib.h>
30     #include <string.h>
31 ats 1.40 #include <time.h>
32 tdb 1.7 #include "statgrab.h"
33 ats 1.56 #include "vector.h"
34 ats 1.60 #include "tools.h"
35 pajs 1.1
36     #ifdef SOLARIS
37     #include <sys/mnttab.h>
38     #include <sys/statvfs.h>
39 pajs 1.4 #include <kstat.h>
40 tdb 1.86 #define VALID_FS_TYPES {"ufs", "tmpfs", "vxfs", "nfs", "zfs"}
41 pajs 1.1 #endif
42    
43 ats 1.40 #if defined(LINUX) || defined(CYGWIN)
44     #include <mntent.h>
45 pajs 1.9 #include <sys/vfs.h>
46 tdb 1.82 #include <sys/statvfs.h>
47 ats 1.40 #endif
48    
49     #ifdef LINUX
50 ats 1.32 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
51 tdb 1.65 "ext3", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
52     "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
53     "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
54 tdb 1.80 "umsdos", "vfat", "xfs", "jfs", "nfs"}
55 pajs 1.9 #endif
56 ats 1.40
57     #ifdef CYGWIN
58     #define VALID_FS_TYPES {"user"}
59 tdb 1.39 #endif
60 pajs 1.9
61 ats 1.31 #ifdef ALLBSD
62 pajs 1.16 #include <sys/param.h>
63     #include <sys/ucred.h>
64     #include <sys/mount.h>
65 ats 1.31 #endif
66 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
67 pajs 1.17 #include <sys/dkstat.h>
68     #include <devstat.h>
69 ats 1.36 #define VALID_FS_TYPES {"hpfs", "msdosfs", "ntfs", "udf", "ext2fs", \
70 tdb 1.87 "ufs", "mfs", "nfs", "zfs"}
71 pajs 1.16 #endif
72 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
73 ats 1.34 #include <sys/param.h>
74     #include <sys/sysctl.h>
75     #include <sys/disk.h>
76 ats 1.35 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
77 tdb 1.80 "ntfs", "nfs"}
78 ats 1.34 #endif
79 ats 1.31
80 tdb 1.73 #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 ats 1.84 #define VALID_FS_TYPES {"vxfs", "hfs", "nfs"}
91 ats 1.75 #define DISK_BATCH 30
92 tdb 1.73 #endif
93    
94 tdb 1.83 #ifdef WIN32
95     #include "win32.h"
96     /*#define VALID_FS_TYPES {"NTFS", "FAT", "FAT32"} unused*/
97     #define BUFSIZE 512
98     #endif
99    
100 tdb 1.82 #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 ats 1.61 static void disk_stat_init(sg_fs_stats *d) {
141 ats 1.56 d->device_name = NULL;
142     d->fs_type = NULL;
143     d->mnt_point = NULL;
144     }
145 pajs 1.1
146 ats 1.61 static void disk_stat_destroy(sg_fs_stats *d) {
147 ats 1.56 free(d->device_name);
148     free(d->fs_type);
149     free(d->mnt_point);
150 pajs 1.1 }
151    
152 tdb 1.83 #ifndef WIN32 /* not used by WIN32, so stop compiler throwing warnings */
153 ats 1.61 static int is_valid_fs_type(const char *type) {
154 ats 1.41 const char *types[] = VALID_FS_TYPES;
155     int i;
156    
157 tdb 1.78 for (i = 0; i < (int) (sizeof types / sizeof *types); i++) {
158 ats 1.41 if (strcmp(types[i], type) == 0) {
159     return 1;
160     }
161     }
162     return 0;
163     }
164 tdb 1.83 #endif
165 ats 1.41
166 ats 1.61 sg_fs_stats *sg_get_fs_stats(int *entries){
167     VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10,
168 tdb 1.65 disk_stat_init, disk_stat_destroy);
169 pajs 1.1
170     int num_disks=0;
171 tdb 1.73 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN) || defined(HPUX)
172 pajs 1.1 FILE *f;
173 pajs 1.16 #endif
174 pajs 1.1
175 ats 1.61 sg_fs_stats *disk_ptr;
176 pajs 1.1
177 pajs 1.9 #ifdef SOLARIS
178 tdb 1.13 struct mnttab mp;
179 pajs 1.9 struct statvfs fs;
180     #endif
181 tdb 1.73 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
182 pajs 1.9 struct mntent *mp;
183 tdb 1.82 struct statvfs fs;
184 pajs 1.9 #endif
185 ats 1.31 #ifdef ALLBSD
186 pajs 1.16 int nummnt;
187 tdb 1.70 #ifdef HAVE_STATVFS
188 tdb 1.82 struct statvfs *mp, **fs;
189 tdb 1.70 #else
190 tdb 1.82 struct statfs *mp, **fs;
191 tdb 1.70 #endif
192 pajs 1.16 #endif
193 tdb 1.83 #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 pajs 1.9
202 ats 1.31 #ifdef ALLBSD
203 tdb 1.80 nummnt=getmntinfo(&mp, MNT_WAIT);
204 pajs 1.16 if (nummnt<=0){
205 ats 1.72 sg_set_error_with_errno(SG_ERROR_GETMNTINFO, NULL);
206 pajs 1.16 return NULL;
207     }
208 tdb 1.82 for(fs = &mp; nummnt--; (*fs)++){
209 pajs 1.16 #endif
210    
211 tdb 1.73 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
212     #ifdef MNT_MNTTAB
213     if ((f=setmntent(MNT_MNTTAB, "r" ))==NULL){
214     #else
215 pajs 1.9 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
216 tdb 1.73 #endif
217 tdb 1.66 sg_set_error(SG_ERROR_SETMNTENT, NULL);
218 pajs 1.9 return NULL;
219     }
220 pajs 1.1
221 pajs 1.9 while((mp=getmntent(f))){
222 tdb 1.82 if((statvfs(mp->mnt_dir, &fs)) !=0){
223 pajs 1.9 continue;
224     }
225 pajs 1.1
226 pajs 1.9 #endif
227    
228     #ifdef SOLARIS
229 pajs 1.1 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
230 ats 1.72 sg_set_error_with_errno(SG_ERROR_OPEN, "/etc/mnttab");
231 pajs 1.1 return NULL;
232     }
233     while((getmntent(f, &mp)) == 0){
234     if ((statvfs(mp.mnt_mountp, &fs)) !=0){
235     continue;
236     }
237 pajs 1.9 #endif
238 pajs 1.1
239 tdb 1.83 #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 tdb 1.82 if(is_valid_fs_type(SG_MP_FSTYPENAME(mp))){
254 tdb 1.83 #endif
255 ats 1.56 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
256     return NULL;
257 pajs 1.1 }
258 ats 1.56 disk_ptr=disk_stats+num_disks;
259 pajs 1.1
260 tdb 1.83 #ifndef WIN32
261 tdb 1.82 /* 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 pajs 1.16 return NULL;
267     }
268 tdb 1.82 if (sg_update_string(&disk_ptr->fs_type, SG_MP_FSTYPENAME(mp)) < 0) {
269 pajs 1.16 return NULL;
270     }
271 tdb 1.82 if (sg_update_string(&disk_ptr->mnt_point, SG_MP_MOUNTP(mp)) < 0) {
272 pajs 1.16 return NULL;
273     }
274    
275 tdb 1.82 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 tdb 1.83
279 tdb 1.82 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 pajs 1.9
285 tdb 1.82 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 tdb 1.83 #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 pajs 1.1
300 tdb 1.83 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 pajs 1.1 num_disks++;
334     }
335 tdb 1.83 #ifdef WIN32
336     while(*p++);
337     } while(*p);
338     #else
339 pajs 1.1 }
340 tdb 1.83 #endif
341 pajs 1.1
342     *entries=num_disks;
343    
344 tdb 1.39 /* If this fails, there is very little i can do about it, so
345 tdb 1.65 I'll ignore it :) */
346 tdb 1.73 #if defined(LINUX) || defined(CYGWIN) || defined(HPUX)
347 tdb 1.39 endmntent(f);
348     #endif
349     #if defined(SOLARIS)
350 pajs 1.1 fclose(f);
351 pajs 1.16 #endif
352 pajs 1.1
353     return disk_stats;
354    
355     }
356 pajs 1.4
357 ats 1.67 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 ats 1.61 static void diskio_stat_init(sg_disk_io_stats *d) {
372 ats 1.56 d->disk_name = NULL;
373 pajs 1.4 }
374    
375 ats 1.61 static void diskio_stat_destroy(sg_disk_io_stats *d) {
376 ats 1.56 free(d->disk_name);
377 pajs 1.4 }
378    
379 ats 1.61 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
380 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
381 pajs 1.4
382 ats 1.20 #ifdef LINUX
383     typedef struct {
384     int major;
385     int minor;
386     } partition;
387     #endif
388    
389 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
390 ats 1.56 int num_diskio;
391 ats 1.38 #ifndef LINUX
392 ats 1.61 sg_disk_io_stats *diskio_stats_ptr;
393 ats 1.38 #endif
394 pajs 1.4
395 tdb 1.73 #ifdef HPUX
396     long long rbytes = 0, wbytes = 0;
397     struct dirent *dinfo = NULL;
398     struct stat lstatinfo;
399 ats 1.75 struct pst_diskinfo pstat_diskinfo[DISK_BATCH];
400 tdb 1.73 char fullpathbuf[1024] = {0};
401     dev_t diskid;
402     DIR *dh = NULL;
403 ats 1.75 int diskidx = 0;
404     int num, i;
405 tdb 1.73 #endif
406 pajs 1.10 #ifdef SOLARIS
407 tdb 1.65 kstat_ctl_t *kc;
408     kstat_t *ksp;
409 pajs 1.4 kstat_io_t kios;
410 pajs 1.10 #endif
411     #ifdef LINUX
412     FILE *f;
413     char *line_ptr;
414     int major, minor;
415 ats 1.20 int has_pp_stats = 1;
416 ats 1.58 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
417 ats 1.20 int i, n;
418     time_t now;
419 ats 1.38 const char *format;
420 ats 1.81 static regex_t not_part_re, part_re;
421     static int re_compiled = 0;
422 pajs 1.10 #endif
423 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
424 ats 1.21 static struct statinfo stats;
425     static int stats_init = 0;
426 pajs 1.17 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 ats 1.31 #endif
432     #ifdef NETBSD
433 ats 1.34 struct disk_sysctl *stats;
434 tdb 1.51 #endif
435     #ifdef OPENBSD
436 tdb 1.52 int diskcount;
437     char *disknames, *name, *bufpp;
438     char **dk_name;
439 tdb 1.51 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 ats 1.34 int num_disks, i;
449 tdb 1.51 int mib[MIBSIZE];
450 ats 1.34 size_t size;
451 pajs 1.17 #endif
452 tdb 1.83 #ifdef WIN32
453     char *name;
454     long long rbytes;
455     long long wbytes;
456     #endif
457 ats 1.34
458 pajs 1.10 num_diskio=0;
459 ats 1.34
460 tdb 1.73 #ifdef HPUX
461 ats 1.75 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 tdb 1.73 break;
470     }
471    
472 ats 1.75 for (i = 0; i < num; i++) {
473     struct pst_diskinfo *di = &pstat_diskinfo[i];
474 tdb 1.73
475 ats 1.75 /* 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 ats 1.79 * 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 ats 1.75
487     /* Skip unused disks. */
488     if (rbytes == 0 && wbytes == 0) {
489 tdb 1.73 continue;
490     }
491 ats 1.75
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 ats 1.77 /* FIXME This should use a static cache, like the Linux
506     * code below. */
507 ats 1.75 if (diskio_stats_ptr->disk_name == NULL) {
508     dh = opendir("/dev/dsk");
509     if (dh == NULL) {
510     continue;
511 tdb 1.73 }
512 ats 1.75
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 tdb 1.73 }
531 ats 1.75 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 tdb 1.73 return NULL;
536     }
537     }
538     }
539     }
540 ats 1.75 diskidx = pstat_diskinfo[num - 1].psd_idx + 1;
541 tdb 1.73 }
542     #endif
543 tdb 1.52 #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 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
550 tdb 1.52 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 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
558 tdb 1.52 return NULL;
559     }
560    
561 ats 1.64 disknames = sg_malloc(size);
562 tdb 1.52 if (disknames == NULL) {
563     return NULL;
564     }
565    
566     if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
567 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
568 tdb 1.52 return NULL;
569     }
570    
571 ats 1.64 dk_name = sg_malloc(diskcount * sizeof(char *));
572 tdb 1.52 bufpp = disknames;
573     for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
574     dk_name[i] = name;
575     }
576     #endif
577    
578 tdb 1.51 #if defined(NETBSD) || defined(OPENBSD)
579 ats 1.34 mib[0] = CTL_HW;
580     mib[1] = HW_DISKSTATS;
581 tdb 1.51 #ifdef NETBSD
582 ats 1.34 mib[2] = sizeof(struct disk_sysctl);
583 tdb 1.51 #endif
584 ats 1.34
585 tdb 1.51 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
586 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
587 ats 1.34 return NULL;
588     }
589 tdb 1.51
590     #ifdef NETBSD
591 ats 1.34 num_disks = size / sizeof(struct disk_sysctl);
592 tdb 1.51 #else
593     num_disks = size / sizeof(struct diskstats);
594     #endif
595 ats 1.34
596 ats 1.64 stats = sg_malloc(size);
597 ats 1.34 if (stats == NULL) {
598     return NULL;
599     }
600    
601 tdb 1.51 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
602 ats 1.72 sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
603 ats 1.34 return NULL;
604     }
605    
606     for (i = 0; i < num_disks; i++) {
607 ats 1.60 const char *name;
608 ats 1.34 u_int64_t rbytes, wbytes;
609    
610 tdb 1.51 #ifdef NETBSD
611 ats 1.34 #ifdef HAVE_DK_RBYTES
612     rbytes = stats[i].dk_rbytes;
613     wbytes = stats[i].dk_wbytes;
614     #else
615 tdb 1.69 /* Before 2.0, NetBSD merged reads and writes. */
616 ats 1.34 rbytes = wbytes = stats[i].dk_bytes;
617     #endif
618 tdb 1.51 #else
619 tdb 1.69 #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 tdb 1.51 rbytes = wbytes = stats[i].ds_bytes;
625 tdb 1.69 #endif
626 tdb 1.51 #endif
627 ats 1.34
628     /* Don't keep stats for disks that have never been used. */
629     if (rbytes == 0 && wbytes == 0) {
630     continue;
631     }
632    
633 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
634 ats 1.34 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 tdb 1.51 #ifdef NETBSD
641 ats 1.60 name = stats[i].dk_name;
642 tdb 1.51 #else
643 ats 1.60 name = dk_name[i];
644 tdb 1.51 #endif
645 ats 1.62 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
646 ats 1.60 return NULL;
647     }
648 ats 1.34 diskio_stats_ptr->systime = time(NULL);
649    
650     num_diskio++;
651     }
652    
653     free(stats);
654 tdb 1.52 #ifdef OPENBSD
655     free(dk_name);
656     free(disknames);
657     #endif
658 ats 1.34 #endif
659 pajs 1.4
660 tdb 1.53 #if defined(FREEBSD) || defined(DFBSD)
661 ats 1.21 if (!stats_init) {
662 ats 1.64 stats.dinfo=sg_malloc(sizeof(struct devinfo));
663 ats 1.33 if(stats.dinfo==NULL) return NULL;
664 tdb 1.25 bzero(stats.dinfo, sizeof(struct devinfo));
665 ats 1.21 stats_init = 1;
666     }
667 pajs 1.28 #ifdef FREEBSD5
668 tdb 1.66 if ((devstat_getdevs(NULL, &stats)) < 0) {
669 ats 1.72 /* FIXME devstat functions return a string error in
670     devstat_errbuf */
671 tdb 1.66 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
672     return NULL;
673     }
674 ats 1.29 /* 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 tdb 1.66 if (devstat_selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
679     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
680     return NULL;
681     }
682 pajs 1.28 #else
683 tdb 1.66 if ((getdevs(&stats)) < 0) {
684     sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
685     return NULL;
686     }
687 pajs 1.17 /* 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 tdb 1.66 if (selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
692     sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
693     return NULL;
694     }
695 pajs 1.27 #endif
696 pajs 1.17
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 pajs 1.27 #ifdef FREEBSD5
705     if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
706     #else
707 pajs 1.17 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
708 pajs 1.27 #endif
709 ats 1.56
710     if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
711 pajs 1.17 return NULL;
712     }
713     diskio_stats_ptr=diskio_stats+num_diskio;
714 pajs 1.27
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 pajs 1.17 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
720     diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
721 pajs 1.27 #endif
722 pajs 1.17 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
723 ats 1.60 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
724 ats 1.72 sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL);
725 ats 1.60 return NULL;
726     }
727 pajs 1.17 diskio_stats_ptr->systime=time(NULL);
728    
729     num_diskio++;
730     }
731     free(dev_sel);
732    
733     #endif
734 pajs 1.10 #ifdef SOLARIS
735 pajs 1.4 if ((kc = kstat_open()) == NULL) {
736 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
737 tdb 1.65 return NULL;
738     }
739 pajs 1.4
740     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
741 tdb 1.65 if (!strcmp(ksp->ks_class, "disk")) {
742 pajs 1.4
743     if(ksp->ks_type != KSTAT_TYPE_IO) continue;
744 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
745 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
746 tdb 1.65 if((kstat_read(kc, ksp, &kios))==-1){
747 pajs 1.4 }
748    
749 ats 1.56 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
750 pajs 1.4 kstat_close(kc);
751     return NULL;
752     }
753 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
754 pajs 1.4
755     diskio_stats_ptr->read_bytes=kios.nread;
756     diskio_stats_ptr->write_bytes=kios.nwritten;
757 ats 1.61 if (sg_update_string(&diskio_stats_ptr->disk_name,
758 tdb 1.65 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
759 tdb 1.85 kstat_close(kc);
760 ats 1.60 return NULL;
761     }
762     diskio_stats_ptr->systime=time(NULL);
763 pajs 1.4
764 pajs 1.5 num_diskio++;
765 pajs 1.4 }
766     }
767    
768     kstat_close(kc);
769 pajs 1.10 #endif
770 pajs 1.4
771 pajs 1.10 #ifdef LINUX
772 ats 1.20 num_diskio = 0;
773     n = 0;
774 pajs 1.10
775 ats 1.20 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
776 ats 1.38 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 ats 1.54 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
782 ats 1.38 if (f == NULL) {
783     f = fopen("/proc/partitions", "r");
784 ats 1.54 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
785 ats 1.38 }
786 ats 1.20 if (f == NULL) goto out;
787     now = time(NULL);
788    
789 ats 1.81 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 ats 1.61 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
802 ats 1.54 char name[100];
803 ats 1.20 long long rsect, wsect;
804    
805 ats 1.38 int nr = sscanf(line_ptr, format,
806 ats 1.20 &major, &minor, name, &rsect, &wsect);
807     if (nr < 3) continue;
808    
809     /* Skip device names ending in numbers, since they're
810 ats 1.81 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 ats 1.38
819     if (nr < 5) {
820     has_pp_stats = 0;
821     rsect = 0;
822     wsect = 0;
823     }
824 ats 1.20
825 ats 1.56 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
826     goto out;
827     }
828 ats 1.58 if (VECTOR_RESIZE(parts, n + 1) < 0) {
829     goto out;
830 pajs 1.11 }
831 pajs 1.12
832 ats 1.62 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
833 ats 1.60 goto out;
834     }
835 ats 1.20 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 pajs 1.10
841 ats 1.20 n++;
842     }
843 pajs 1.10
844 ats 1.45 fclose(f);
845 ats 1.46 f = NULL;
846 pajs 1.44
847 ats 1.20 if (!has_pp_stats) {
848 ats 1.45 /* 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 ats 1.20
852     f = fopen("/proc/stat", "r");
853     if (f == NULL) goto out;
854     now = time(NULL);
855 tdb 1.83
856 ats 1.61 line_ptr = sg_f_read_line(f, "disk_io:");
857 ats 1.20 if (line_ptr == NULL) goto out;
858 tdb 1.83
859 ats 1.20 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
860     long long rsect, wsect;
861    
862     if (*++line_ptr == '\0') break;
863 tdb 1.83
864 ats 1.20 if((sscanf(line_ptr,
865     "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
866     &major, &minor, &rsect, &wsect)) != 4) {
867     continue;
868     }
869 pajs 1.10
870 ats 1.20 /* 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 pajs 1.10 case 22:
881 ats 1.20 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 pajs 1.11 break;
892 ats 1.20 case 9:
893     case 43:
894 pajs 1.11 break;
895 pajs 1.10 default:
896 ats 1.20 minor *= 16;
897 pajs 1.11 break;
898 ats 1.20 }
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 pajs 1.10 }
912     }
913 pajs 1.16
914 ats 1.20 num_diskio = n;
915     out:
916     if (f != NULL) fclose(f);
917 ats 1.42 #endif
918 pajs 1.10
919 ats 1.42 #ifdef CYGWIN
920 tdb 1.66 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
921 ats 1.42 return NULL;
922 pajs 1.10 #endif
923 ats 1.42
924 tdb 1.83 #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 pajs 1.5 *entries=num_diskio;
948 pajs 1.4
949     return diskio_stats;
950 pajs 1.5 }
951    
952 ats 1.61 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
953 tdb 1.83 #ifndef WIN32
954 ats 1.61 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
955 tdb 1.65 diskio_stat_init, diskio_stat_destroy);
956 ats 1.61 sg_disk_io_stats *src = NULL, *dest;
957 ats 1.56 int i, j, diff_count, new_count;
958 ats 1.50
959     if (diskio_stats == NULL) {
960     /* No previous stats, so we can't calculate a difference. */
961 ats 1.61 return sg_get_disk_io_stats(entries);
962 pajs 1.5 }
963    
964 ats 1.50 /* Resize the results array to match the previous stats. */
965 ats 1.56 diff_count = VECTOR_SIZE(diskio_stats);
966     if (VECTOR_RESIZE(diff, diff_count) < 0) {
967 pajs 1.5 return NULL;
968     }
969    
970 ats 1.50 /* 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 ats 1.62 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
976 ats 1.60 return NULL;
977 ats 1.50 }
978     dest->read_bytes = src->read_bytes;
979     dest->write_bytes = src->write_bytes;
980     dest->systime = src->systime;
981 pajs 1.5 }
982    
983 ats 1.50 /* Get a new set of stats. */
984 ats 1.61 if (sg_get_disk_io_stats(&new_count) == NULL) {
985 ats 1.47 return NULL;
986     }
987 pajs 1.5
988 ats 1.50 /* 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 pajs 1.5 }
1000     }
1001 ats 1.50 if (j == new_count) {
1002     /* No match found. */
1003     continue;
1004     }
1005 pajs 1.5
1006 ats 1.50 /* ... 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 pajs 1.5
1013 ats 1.50 *entries = diff_count;
1014     return diff;
1015 tdb 1.83 #else /* WIN32 */
1016     return sg_get_disk_io_stats(entries);
1017     #endif
1018 ats 1.67 }
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 pajs 1.2 }
1026 ats 1.50