ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.31
Committed: Sun Oct 19 02:03:02 2003 UTC (20 years, 7 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.30: +13 -5 lines
Log Message:
Initial support for NetBSD. This adds NetBSD support for everything
except diskio stats (since they're even more disturbingly complex to get
at on NetBSD than the three OSs we already support). Tested against
NetBSD 1.6 on i386.

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