ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.20
Committed: Thu Aug 28 23:05:56 2003 UTC (20 years, 8 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.19: +122 -61 lines
Log Message:
Make disk_stats read /proc/partitions on Linux, which contains information for
  all disks on recent 2.4 kernels. (It'll fall back to /proc/stat if it can't
  find the information there.)

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