ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.29
Committed: Sat Oct 18 22:16:44 2003 UTC (20 years, 7 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.28: +4 -1 lines
Log Message:
Fix a little bit of slightly-confused comment formatting.

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 #ifdef FREEBSD5
346 if ((devstat_getdevs(NULL, &stats)) < 0) return NULL;
347 /* Not aware of a get all devices, so i said 999. If we ever
348 * find a machine with more than 999 disks, then i'll change
349 * this number :)
350 */
351 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;
352 #else
353 if ((getdevs(&stats)) < 0) return NULL;
354 /* Not aware of a get all devices, so i said 999. If we ever
355 * find a machine with more than 999 disks, then i'll change
356 * this number :)
357 */
358 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;
359 #endif
360
361 for(counter=0;counter<stats.dinfo->numdevs;counter++){
362 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
363
364 /* Throw away devices that have done nothing, ever.. Eg "odd"
365 * devices.. like mem, proc.. and also doesn't report floppy
366 * drives etc unless they are doing stuff :)
367 */
368 #ifdef FREEBSD5
369 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
370 #else
371 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
372 #endif
373 if((diskio_stats=diskio_stat_malloc(num_diskio+1, &sizeof_diskio_stats, diskio_stats))==NULL){
374 return NULL;
375 }
376 diskio_stats_ptr=diskio_stats+num_diskio;
377
378 #ifdef FREEBSD5
379 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
380 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
381 #else
382 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
383 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
384 #endif
385 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
386 asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number);
387 diskio_stats_ptr->systime=time(NULL);
388
389 num_diskio++;
390 }
391 free(dev_sel);
392
393 #endif
394 #ifdef SOLARIS
395 if ((kc = kstat_open()) == NULL) {
396 return NULL;
397 }
398
399 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
400 if (!strcmp(ksp->ks_class, "disk")) {
401
402 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
403 /* We dont want metadevices appearins as num_diskio */
404 if(strcmp(ksp->ks_module, "md")==0) continue;
405 if((kstat_read(kc, ksp, &kios))==-1){
406 }
407
408 if((diskio_stats=diskio_stat_malloc(num_diskio+1, &sizeof_diskio_stats, diskio_stats))==NULL){
409 kstat_close(kc);
410 return NULL;
411 }
412 diskio_stats_ptr=diskio_stats+num_diskio;
413
414 diskio_stats_ptr->read_bytes=kios.nread;
415
416 diskio_stats_ptr->write_bytes=kios.nwritten;
417
418 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
419
420 diskio_stats_ptr->disk_name=strdup(ksp->ks_name);
421 diskio_stats_ptr->systime=time(NULL);
422 num_diskio++;
423 }
424 }
425
426 kstat_close(kc);
427 #endif
428
429 #ifdef LINUX
430 num_diskio = 0;
431 n = 0;
432
433 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
434 have statistics in here too, so we can use those directly. */
435
436 f = fopen("/proc/partitions", "r");
437 if (f == NULL) goto out;
438 now = time(NULL);
439
440 while ((line_ptr = f_read_line(f, "")) != NULL) {
441 char name[20];
442 char *s;
443 long long rsect, wsect;
444
445 int nr = sscanf(line_ptr,
446 " %d %d %*d %19s %*d %*d %lld %*d %*d %*d %lld",
447 &major, &minor, name, &rsect, &wsect);
448 if (nr < 3) continue;
449 if (nr < 5) {
450 has_pp_stats = 0;
451 rsect = 0;
452 wsect = 0;
453 }
454
455 /* Skip device names ending in numbers, since they're
456 partitions. */
457 s = name;
458 while (*s != '\0') s++;
459 --s;
460 if (*s >= '0' && *s <= '9') continue;
461
462 diskio_stats = diskio_stat_malloc(n + 1, &sizeof_diskio_stats,
463 diskio_stats);
464 if (diskio_stats == NULL) goto out;
465 if (n >= alloc_parts) {
466 alloc_parts += 16;
467 parts = realloc(parts, alloc_parts * sizeof *parts);
468 if (parts == NULL) {
469 alloc_parts = 0;
470 goto out;
471 }
472 }
473
474 if (diskio_stats[n].disk_name != NULL)
475 free(diskio_stats[n].disk_name);
476 diskio_stats[n].disk_name = strdup(name);
477 diskio_stats[n].read_bytes = rsect * 512;
478 diskio_stats[n].write_bytes = wsect * 512;
479 diskio_stats[n].systime = now;
480 parts[n].major = major;
481 parts[n].minor = minor;
482
483 n++;
484 }
485
486 if (!has_pp_stats) {
487 /* This is an older kernel without stats in /proc/partitions.
488 Read what we can from /proc/stat instead. */
489
490 f = fopen("/proc/stat", "r");
491 if (f == NULL) goto out;
492 now = time(NULL);
493
494 line_ptr = f_read_line(f, "disk_io:");
495 if (line_ptr == NULL) goto out;
496
497 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
498 long long rsect, wsect;
499
500 if (*++line_ptr == '\0') break;
501
502 if((sscanf(line_ptr,
503 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
504 &major, &minor, &rsect, &wsect)) != 4) {
505 continue;
506 }
507
508 /* Find the corresponding device from earlier.
509 Just to add to the fun, "minor" is actually the disk
510 number, not the device minor, so we need to figure
511 out the real minor number based on the major!
512 This list is not exhaustive; if you're running
513 an older kernel you probably don't have fancy
514 I2O hardware anyway... */
515 switch (major) {
516 case 3:
517 case 21:
518 case 22:
519 case 33:
520 case 34:
521 case 36:
522 case 56:
523 case 57:
524 case 88:
525 case 89:
526 case 90:
527 case 91:
528 minor *= 64;
529 break;
530 case 9:
531 case 43:
532 break;
533 default:
534 minor *= 16;
535 break;
536 }
537 for (i = 0; i < n; i++) {
538 if (major == parts[i].major
539 && minor == parts[i].minor)
540 break;
541 }
542 if (i == n) continue;
543
544 /* We read the number of blocks. Blocks are stored in
545 512 bytes */
546 diskio_stats[i].read_bytes = rsect * 512;
547 diskio_stats[i].write_bytes = wsect * 512;
548 diskio_stats[i].systime = now;
549 }
550 }
551
552 num_diskio = n;
553 out:
554 if (f != NULL) fclose(f);
555
556 #endif
557 *entries=num_diskio;
558
559 return diskio_stats;
560 }
561
562 diskio_stat_t *get_diskio_stats_diff(int *entries){
563 static diskio_stat_t *diskio_stats_diff=NULL;
564 static int sizeof_diskio_stats_diff=0;
565 diskio_stat_t *diskio_stats_diff_ptr, *diskio_stats_ptr;
566 int disks, x, y;
567
568 if(diskio_stats==NULL){
569 diskio_stats_ptr=get_diskio_stats(&disks);
570 *entries=disks;
571 return diskio_stats_ptr;
572 }
573
574 diskio_stats_diff=diskio_stat_malloc(num_diskio, &sizeof_diskio_stats_diff, diskio_stats_diff);
575 if(diskio_stats_diff==NULL){
576 return NULL;
577 }
578
579 diskio_stats_diff_ptr=diskio_stats_diff;
580 diskio_stats_ptr=diskio_stats;
581
582 for(disks=0;disks<num_diskio;disks++){
583 if(diskio_stats_diff_ptr->disk_name!=NULL){
584 free(diskio_stats_diff_ptr->disk_name);
585 }
586 diskio_stats_diff_ptr->disk_name=strdup(diskio_stats_ptr->disk_name);
587 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes;
588 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes;
589 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime;
590
591 diskio_stats_diff_ptr++;
592 diskio_stats_ptr++;
593 }
594
595 diskio_stats_ptr=get_diskio_stats(&disks);
596 diskio_stats_diff_ptr=diskio_stats_diff;
597
598 for(x=0;x<sizeof_diskio_stats_diff;x++){
599
600 if((strcmp(diskio_stats_diff_ptr->disk_name, diskio_stats_ptr->disk_name))==0){
601 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes-diskio_stats_diff_ptr->read_bytes;
602 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes-diskio_stats_diff_ptr->write_bytes;
603 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime-diskio_stats_diff_ptr->systime;
604 }else{
605 diskio_stats_ptr=diskio_stats;
606 for(y=0;y<disks;y++){
607 if((strcmp(diskio_stats_diff_ptr->disk_name, diskio_stats_ptr->disk_name))==0){
608 diskio_stats_diff_ptr->read_bytes=diskio_stats_ptr->read_bytes-diskio_stats_diff_ptr->read_bytes;
609 diskio_stats_diff_ptr->write_bytes=diskio_stats_ptr->write_bytes-diskio_stats_diff_ptr->write_bytes;
610 diskio_stats_diff_ptr->systime=diskio_stats_ptr->systime-diskio_stats_diff_ptr->systime;
611
612 break;
613 }
614
615 diskio_stats_ptr++;
616 }
617 }
618
619 diskio_stats_ptr++;
620 diskio_stats_diff_ptr++;
621
622 }
623
624 *entries=sizeof_diskio_stats_diff;
625 return diskio_stats_diff;
626 }