ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.26
Committed: Wed Oct 8 09:20:56 2003 UTC (20 years, 7 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.25: +1 -151 lines
Log Message:
Unroll Pete's commits for doing drive mappings on Solaris. It doesn't
work right and he wants to start again. Also, we don't want it in the
next release :) This effectively removes revisions 1.22 and 1.23.

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