ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.27
Committed: Thu Oct 9 14:59:52 2003 UTC (20 years, 7 months ago) by pajs
Content type: text/plain
Branch: MAIN
Changes since 1.26: +14 -1 lines
Log Message:
Freebsd 5 patch for disk stats. Thanks to Tim Bishop for doing the hard
work on this patch.

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