ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.30
Committed: Sun Oct 19 00:10:30 2003 UTC (20 years, 7 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.29: +1 -0 lines
Log Message:
Fix warning when building on Linux.

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