ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.32
Committed: Sun Oct 19 11:15:30 2003 UTC (20 years, 7 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.31: +5 -1 lines
Log Message:
Update the Linux VALID_FS_TYPES list based on the writable disk-based
filesystems in 2.4.22 (plus xfs and jfs).

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2003 i-scream
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "statgrab.h"
29
30 #ifdef SOLARIS
31 #include <sys/mnttab.h>
32 #include <sys/statvfs.h>
33 #include <kstat.h>
34 #define VALID_FS_TYPES {"ufs", "tmpfs"}
35 #endif
36
37 #ifdef LINUX
38 #include <time.h>
39 #include <sys/vfs.h>
40 #include <mntent.h>
41 #include "tools.h"
42 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
43 "ext3", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
44 "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
45 "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
46 "umsdos", "vfat", "xfs", "jfs"}
47 #endif
48
49 #ifdef ALLBSD
50 #include <sys/param.h>
51 #include <sys/ucred.h>
52 #include <sys/mount.h>
53 #define VALID_FS_TYPES {"ufs", "mfs", "ffs"}
54 #endif
55 #ifdef FREEBSD
56 #include <sys/dkstat.h>
57 #include <devstat.h>
58 #endif
59
60 #define START_VAL 1
61
62 char *copy_string(char *orig_ptr, const char *newtext){
63
64 /* Maybe free if not NULL, and strdup rather than realloc and strcpy? */
65 orig_ptr=realloc(orig_ptr, (1+strlen(newtext)));
66 if(orig_ptr==NULL){
67 return NULL;
68 }
69 strcpy(orig_ptr, newtext);
70
71 return orig_ptr;
72 }
73
74
75 void init_disk_stat(int start, int end, disk_stat_t *disk_stats){
76
77 for(disk_stats+=start; start<=end; start++){
78 disk_stats->device_name=NULL;
79 disk_stats->fs_type=NULL;
80 disk_stats->mnt_point=NULL;
81
82 disk_stats++;
83 }
84 }
85
86 disk_stat_t *get_disk_stats(int *entries){
87
88 static disk_stat_t *disk_stats;
89 static int watermark=-1;
90
91 char *fs_types[] = VALID_FS_TYPES;
92 int x, valid_type;
93 int num_disks=0;
94 #if defined(LINUX) || defined (SOLARIS)
95 FILE *f;
96 #endif
97
98 disk_stat_t *disk_ptr;
99
100 #ifdef SOLARIS
101 struct mnttab mp;
102 struct statvfs fs;
103 #endif
104 #ifdef LINUX
105 struct mntent *mp;
106 struct statfs fs;
107 #endif
108 #ifdef ALLBSD
109 int nummnt;
110 struct statfs *mp;
111 #endif
112
113 if(watermark==-1){
114 disk_stats=malloc(START_VAL * sizeof(disk_stat_t));
115 if(disk_stats==NULL){
116 return NULL;
117 }
118 watermark=START_VAL;
119 init_disk_stat(0, watermark-1, disk_stats);
120 }
121 #ifdef ALLBSD
122 nummnt=getmntinfo(&mp , MNT_LOCAL);
123 if (nummnt<=0){
124 return NULL;
125 }
126 for(;nummnt--; mp++){
127 valid_type=0;
128 for(x=0;x<((sizeof(fs_types))/(sizeof(char*)));x++){
129 if(strcmp(mp->f_fstypename, fs_types[x]) ==0){
130 valid_type=1;
131 break;
132 }
133 }
134 #endif
135
136 #ifdef LINUX
137 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
138 return NULL;
139 }
140
141 while((mp=getmntent(f))){
142 if((statfs(mp->mnt_dir, &fs)) !=0){
143 continue;
144 }
145
146 valid_type=0;
147 for(x=0;x<((sizeof(fs_types))/(sizeof(char*)));x++){
148 if(strcmp(mp->mnt_type, fs_types[x]) ==0){
149 valid_type=1;
150 break;
151 }
152 }
153 #endif
154
155 #ifdef SOLARIS
156 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
157 return NULL;
158 }
159 while((getmntent(f, &mp)) == 0){
160 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
161 continue;
162 }
163 valid_type=0;
164 for(x=0;x<((sizeof(fs_types))/(sizeof(char*)));x++){
165 if(strcmp(mp.mnt_fstype, fs_types[x]) ==0){
166 valid_type=1;
167 break;
168 }
169 }
170 #endif
171
172 if(valid_type){
173 if(num_disks>watermark-1){
174 disk_ptr=disk_stats;
175 if((disk_stats=realloc(disk_stats, (watermark*2 * sizeof(disk_stat_t))))==NULL){
176 disk_stats=disk_ptr;
177 return NULL;
178 }
179
180 watermark=watermark*2;
181 init_disk_stat(num_disks, watermark-1, disk_stats);
182 }
183
184 disk_ptr=disk_stats+num_disks;
185 #ifdef ALLBSD
186 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp->f_mntfromname))==NULL){
187 return NULL;
188 }
189
190 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp->f_fstypename))==NULL){
191 return NULL;
192 }
193
194 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp->f_mntonname))==NULL){
195 return NULL;
196 }
197
198 disk_ptr->size = (long long)mp->f_bsize * (long long) mp->f_blocks;
199 disk_ptr->avail = (long long)mp->f_bsize * (long long) mp->f_bavail;
200 disk_ptr->used = (disk_ptr->size) - ((long long)mp->f_bsize * (long long)mp->f_bfree);
201
202 disk_ptr->total_inodes=(long long)mp->f_files;
203 disk_ptr->free_inodes=(long long)mp->f_ffree;
204 /* Freebsd doesn't have a "available" inodes */
205 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
206 #endif
207 #ifdef LINUX
208 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp->mnt_fsname))==NULL){
209 return NULL;
210 }
211
212 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp->mnt_type))==NULL){
213 return NULL;
214 }
215
216 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp->mnt_dir))==NULL){
217 return NULL;
218 }
219 disk_ptr->size = (long long)fs.f_bsize * (long long)fs.f_blocks;
220 disk_ptr->avail = (long long)fs.f_bsize * (long long)fs.f_bavail;
221 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_bsize * (long long)fs.f_bfree);
222
223 disk_ptr->total_inodes=(long long)fs.f_files;
224 disk_ptr->free_inodes=(long long)fs.f_ffree;
225 /* Linux doesn't have a "available" inodes */
226 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
227 #endif
228
229 #ifdef SOLARIS
230 /* Memory leak in event of realloc failing */
231 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end?
232 * Downside is its a bit hungry for a lot of mounts, as MNT_MAX_SIZE would prob
233 * be upwards of a k each
234 */
235 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp.mnt_special))==NULL){
236 return NULL;
237 }
238
239 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp.mnt_fstype))==NULL){
240 return NULL;
241 }
242
243 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp.mnt_mountp))==NULL){
244 return NULL;
245 }
246
247 disk_ptr->size = (long long)fs.f_frsize * (long long)fs.f_blocks;
248 disk_ptr->avail = (long long)fs.f_frsize * (long long)fs.f_bavail;
249 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_frsize * (long long)fs.f_bfree);
250
251 disk_ptr->total_inodes=(long long)fs.f_files;
252 disk_ptr->used_inodes=disk_ptr->total_inodes - (long long)fs.f_ffree;
253 disk_ptr->free_inodes=(long long)fs.f_favail;
254 #endif
255 num_disks++;
256 }
257 }
258
259 *entries=num_disks;
260
261 /* If this fails, there is very little i can do about it, so i'll ignore it :) */
262 #if defined(LINUX) || defined(SOLARIS)
263 fclose(f);
264 #endif
265
266 return disk_stats;
267
268 }
269 void diskio_stat_init(int start, int end, diskio_stat_t *diskio_stats){
270
271 for(diskio_stats+=start; start<end; start++){
272 diskio_stats->disk_name=NULL;
273
274 diskio_stats++;
275 }
276 }
277
278 diskio_stat_t *diskio_stat_malloc(int needed_entries, int *cur_entries, diskio_stat_t *diskio_stats){
279
280 if(diskio_stats==NULL){
281
282 if((diskio_stats=malloc(needed_entries * sizeof(diskio_stat_t)))==NULL){
283 return NULL;
284 }
285 diskio_stat_init(0, needed_entries, diskio_stats);
286 *cur_entries=needed_entries;
287
288 return diskio_stats;
289 }
290
291
292 if(*cur_entries<needed_entries){
293 diskio_stats=realloc(diskio_stats, (sizeof(diskio_stat_t)*needed_entries));
294 if(diskio_stats==NULL){
295 return NULL;
296 }
297 diskio_stat_init(*cur_entries, needed_entries, diskio_stats);
298 *cur_entries=needed_entries;
299 }
300
301 return diskio_stats;
302 }
303
304 static diskio_stat_t *diskio_stats=NULL;
305 static int num_diskio=0;
306
307 #ifdef LINUX
308 typedef struct {
309 int major;
310 int minor;
311 } partition;
312 #endif
313
314 diskio_stat_t *get_diskio_stats(int *entries){
315
316 static int sizeof_diskio_stats=0;
317 diskio_stat_t *diskio_stats_ptr;
318
319 #ifdef SOLARIS
320 kstat_ctl_t *kc;
321 kstat_t *ksp;
322 kstat_io_t kios;
323 #endif
324 #ifdef LINUX
325 FILE *f;
326 char *line_ptr;
327 int major, minor;
328 char dev_letter;
329 int has_pp_stats = 1;
330 static partition *parts = NULL;
331 static int alloc_parts = 0;
332 int i, n;
333 time_t now;
334 #endif
335 #ifdef FREEBSD
336 static struct statinfo stats;
337 static int stats_init = 0;
338 int counter;
339 struct device_selection *dev_sel = NULL;
340 int n_selected, n_selections;
341 long sel_gen;
342 struct devstat *dev_ptr;
343 #endif
344 #ifdef NETBSD
345 /* FIXME get_diskio_stats NYI on NetBSD.
346 * See vmstat/dkstats.c in NetBSD source for examples.
347 */
348 #endif
349 num_diskio=0;
350
351 #ifdef FREEBSD
352 if (!stats_init) {
353 stats.dinfo=malloc(sizeof(struct devinfo));
354 bzero(stats.dinfo, sizeof(struct devinfo));
355 if(stats.dinfo==NULL) return NULL;
356 stats_init = 1;
357 }
358 #ifdef FREEBSD5
359 if ((devstat_getdevs(NULL, &stats)) < 0) return NULL;
360 /* Not aware of a get all devices, so i said 999. If we ever
361 * find a machine with more than 999 disks, then i'll change
362 * this number :)
363 */
364 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) return NULL;
365 #else
366 if ((getdevs(&stats)) < 0) return NULL;
367 /* Not aware of a get all devices, so i said 999. If we ever
368 * find a machine with more than 999 disks, then i'll change
369 * this number :)
370 */
371 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) return NULL;
372 #endif
373
374 for(counter=0;counter<stats.dinfo->numdevs;counter++){
375 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
376
377 /* Throw away devices that have done nothing, ever.. Eg "odd"
378 * devices.. like mem, proc.. and also doesn't report floppy
379 * drives etc unless they are doing stuff :)
380 */
381 #ifdef FREEBSD5
382 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
383 #else
384 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
385 #endif
386 if((diskio_stats=diskio_stat_malloc(num_diskio+1, &sizeof_diskio_stats, diskio_stats))==NULL){
387 return NULL;
388 }
389 diskio_stats_ptr=diskio_stats+num_diskio;
390
391 #ifdef FREEBSD5
392 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
393 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
394 #else
395 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
396 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
397 #endif
398 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
399 asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number);
400 diskio_stats_ptr->systime=time(NULL);
401
402 num_diskio++;
403 }
404 free(dev_sel);
405
406 #endif
407 #ifdef SOLARIS
408 if ((kc = kstat_open()) == NULL) {
409 return NULL;
410 }
411
412 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
413 if (!strcmp(ksp->ks_class, "disk")) {
414
415 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
416 /* We dont want metadevices appearins as num_diskio */
417 if(strcmp(ksp->ks_module, "md")==0) continue;
418 if((kstat_read(kc, ksp, &kios))==-1){
419 }
420
421 if((diskio_stats=diskio_stat_malloc(num_diskio+1, &sizeof_diskio_stats, diskio_stats))==NULL){
422 kstat_close(kc);
423 return NULL;
424 }
425 diskio_stats_ptr=diskio_stats+num_diskio;
426
427 diskio_stats_ptr->read_bytes=kios.nread;
428
429 diskio_stats_ptr->write_bytes=kios.nwritten;
430
431 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
432
433 diskio_stats_ptr->disk_name=strdup(ksp->ks_name);
434 diskio_stats_ptr->systime=time(NULL);
435 num_diskio++;
436 }
437 }
438
439 kstat_close(kc);
440 #endif
441
442 #ifdef LINUX
443 num_diskio = 0;
444 n = 0;
445
446 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
447 have statistics in here too, so we can use those directly. */
448
449 f = fopen("/proc/partitions", "r");
450 if (f == NULL) goto out;
451 now = time(NULL);
452
453 while ((line_ptr = f_read_line(f, "")) != NULL) {
454 char name[20];
455 char *s;
456 long long rsect, wsect;
457
458 int nr = sscanf(line_ptr,
459 " %d %d %*d %19s %*d %*d %lld %*d %*d %*d %lld",
460 &major, &minor, name, &rsect, &wsect);
461 if (nr < 3) continue;
462 if (nr < 5) {
463 has_pp_stats = 0;
464 rsect = 0;
465 wsect = 0;
466 }
467
468 /* Skip device names ending in numbers, since they're
469 partitions. */
470 s = name;
471 while (*s != '\0') s++;
472 --s;
473 if (*s >= '0' && *s <= '9') continue;
474
475 diskio_stats = diskio_stat_malloc(n + 1, &sizeof_diskio_stats,
476 diskio_stats);
477 if (diskio_stats == NULL) goto out;
478 if (n >= alloc_parts) {
479 alloc_parts += 16;
480 parts = realloc(parts, alloc_parts * sizeof *parts);
481 if (parts == NULL) {
482 alloc_parts = 0;
483 goto out;
484 }
485 }
486
487 if (diskio_stats[n].disk_name != NULL)
488 free(diskio_stats[n].disk_name);
489 diskio_stats[n].disk_name = strdup(name);
490 diskio_stats[n].read_bytes = rsect * 512;
491 diskio_stats[n].write_bytes = wsect * 512;
492 diskio_stats[n].systime = now;
493 parts[n].major = major;
494 parts[n].minor = minor;
495
496 n++;
497 }
498
499 if (!has_pp_stats) {
500 /* This is an older kernel without stats in /proc/partitions.
501 Read what we can from /proc/stat instead. */
502
503 f = fopen("/proc/stat", "r");
504 if (f == NULL) goto out;
505 now = time(NULL);
506
507 line_ptr = f_read_line(f, "disk_io:");
508 if (line_ptr == NULL) goto out;
509
510 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
511 long long rsect, wsect;
512
513 if (*++line_ptr == '\0') break;
514
515 if((sscanf(line_ptr,
516 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
517 &major, &minor, &rsect, &wsect)) != 4) {
518 continue;
519 }
520
521 /* Find the corresponding device from earlier.
522 Just to add to the fun, "minor" is actually the disk
523 number, not the device minor, so we need to figure
524 out the real minor number based on the major!
525 This list is not exhaustive; if you're running
526 an older kernel you probably don't have fancy
527 I2O hardware anyway... */
528 switch (major) {
529 case 3:
530 case 21:
531 case 22:
532 case 33:
533 case 34:
534 case 36:
535 case 56:
536 case 57:
537 case 88:
538 case 89:
539 case 90:
540 case 91:
541 minor *= 64;
542 break;
543 case 9:
544 case 43:
545 break;
546 default:
547 minor *= 16;
548 break;
549 }
550 for (i = 0; i < n; i++) {
551 if (major == parts[i].major
552 && minor == parts[i].minor)
553 break;
554 }
555 if (i == n) continue;
556
557 /* We read the number of blocks. Blocks are stored in
558 512 bytes */
559 diskio_stats[i].read_bytes = rsect * 512;
560 diskio_stats[i].write_bytes = wsect * 512;
561 diskio_stats[i].systime = now;
562 }
563 }
564
565 num_diskio = n;
566 out:
567 if (f != NULL) fclose(f);
568
569 #endif
570 *entries=num_diskio;
571
572 return diskio_stats;
573 }
574
575 diskio_stat_t *get_diskio_stats_diff(int *entries){
576 static diskio_stat_t *diskio_stats_diff=NULL;
577 static int sizeof_diskio_stats_diff=0;
578 diskio_stat_t *diskio_stats_diff_ptr, *diskio_stats_ptr;
579 int disks, x, y;
580
581 if(diskio_stats==NULL){
582 diskio_stats_ptr=get_diskio_stats(&disks);
583 *entries=disks;
584 return diskio_stats_ptr;
585 }
586
587 diskio_stats_diff=diskio_stat_malloc(num_diskio, &sizeof_diskio_stats_diff, diskio_stats_diff);
588 if(diskio_stats_diff==NULL){
589 return NULL;
590 }
591
592 diskio_stats_diff_ptr=diskio_stats_diff;
593 diskio_stats_ptr=diskio_stats;
594
595 for(disks=0;disks<num_diskio;disks++){
596 if(diskio_stats_diff_ptr->disk_name!=NULL){
597 free(diskio_stats_diff_ptr->disk_name);
598 }
599 diskio_stats_diff_ptr->disk_name=strdup(diskio_stats_ptr->disk_name);
600 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes;
601 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes;
602 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime;
603
604 diskio_stats_diff_ptr++;
605 diskio_stats_ptr++;
606 }
607
608 diskio_stats_ptr=get_diskio_stats(&disks);
609 diskio_stats_diff_ptr=diskio_stats_diff;
610
611 for(x=0;x<sizeof_diskio_stats_diff;x++){
612
613 if((strcmp(diskio_stats_diff_ptr->disk_name, diskio_stats_ptr->disk_name))==0){
614 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes-diskio_stats_diff_ptr->read_bytes;
615 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes-diskio_stats_diff_ptr->write_bytes;
616 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime-diskio_stats_diff_ptr->systime;
617 }else{
618 diskio_stats_ptr=diskio_stats;
619 for(y=0;y<disks;y++){
620 if((strcmp(diskio_stats_diff_ptr->disk_name, diskio_stats_ptr->disk_name))==0){
621 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes-diskio_stats_diff_ptr->read_bytes;
622 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes-diskio_stats_diff_ptr->write_bytes;
623 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime-diskio_stats_diff_ptr->systime;
624
625 break;
626 }
627
628 diskio_stats_ptr++;
629 }
630 }
631
632 diskio_stats_ptr++;
633 diskio_stats_diff_ptr++;
634
635 }
636
637 *entries=sizeof_diskio_stats_diff;
638 return diskio_stats_diff;
639 }