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

# User Rev Content
1 pajs 1.1 /*
2     * i-scream central monitoring system
3 tdb 1.18 * http://www.i-scream.org
4     * Copyright (C) 2000-2003 i-scream
5 pajs 1.1 *
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 pajs 1.17 #include <stdio.h>
26 pajs 1.1 #include <stdlib.h>
27     #include <string.h>
28 tdb 1.7 #include "statgrab.h"
29 pajs 1.1
30     #ifdef SOLARIS
31     #include <sys/mnttab.h>
32     #include <sys/statvfs.h>
33 pajs 1.4 #include <kstat.h>
34 pajs 1.1 #define VALID_FS_TYPES {"ufs", "tmpfs"}
35     #endif
36    
37 pajs 1.9 #ifdef LINUX
38     #include <sys/vfs.h>
39     #include <mntent.h>
40 pajs 1.10 #include "tools.h"
41 pajs 1.9 #define VALID_FS_TYPES {"ext2", "ext3", "xfs", "reiserfs", "vfat", "tmpfs"}
42     #endif
43    
44 pajs 1.16 #ifdef FREEBSD
45     #include <sys/param.h>
46     #include <sys/ucred.h>
47     #include <sys/mount.h>
48 pajs 1.17 #include <sys/dkstat.h>
49     #include <devstat.h>
50 pajs 1.16 #define VALID_FS_TYPES {"ufs", "mfs"}
51     #endif
52 pajs 1.1 #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 pajs 1.16 #if defined(LINUX) || defined (SOLARIS)
87 pajs 1.1 FILE *f;
88 pajs 1.16 #endif
89 pajs 1.1
90     disk_stat_t *disk_ptr;
91    
92 pajs 1.9 #ifdef SOLARIS
93 tdb 1.13 struct mnttab mp;
94 pajs 1.9 struct statvfs fs;
95     #endif
96     #ifdef LINUX
97     struct mntent *mp;
98     struct statfs fs;
99     #endif
100 pajs 1.16 #ifdef FREEBSD
101     int nummnt;
102     struct statfs *mp;
103     #endif
104 pajs 1.9
105 pajs 1.1 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 pajs 1.16 #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 pajs 1.9 #ifdef LINUX
129     if ((f=setmntent("/etc/mtab", "r" ))==NULL){
130     return NULL;
131     }
132 pajs 1.1
133 pajs 1.9 while((mp=getmntent(f))){
134     if((statfs(mp->mnt_dir, &fs)) !=0){
135     continue;
136     }
137 pajs 1.1
138 pajs 1.9 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 pajs 1.1 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 pajs 1.9 #endif
163 pajs 1.1
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 pajs 1.16 #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 pajs 1.9 #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 pajs 1.1 /* 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 pajs 1.9 * 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 pajs 1.1 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 pajs 1.9 #endif
247 pajs 1.1 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 pajs 1.16 #if defined(LINUX) || defined(SOLARIS)
255 pajs 1.1 fclose(f);
256 pajs 1.16 #endif
257 pajs 1.1
258     return disk_stats;
259    
260     }
261 pajs 1.4 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 pajs 1.5 static int num_diskio=0;
298 pajs 1.4
299 ats 1.20 #ifdef LINUX
300     typedef struct {
301     int major;
302     int minor;
303     } partition;
304     #endif
305    
306 tdb 1.3 diskio_stat_t *get_diskio_stats(int *entries){
307 pajs 1.2
308 pajs 1.4 static int sizeof_diskio_stats=0;
309     diskio_stat_t *diskio_stats_ptr;
310    
311 pajs 1.10 #ifdef SOLARIS
312 pajs 1.4 kstat_ctl_t *kc;
313     kstat_t *ksp;
314     kstat_io_t kios;
315 pajs 1.10 #endif
316     #ifdef LINUX
317     FILE *f;
318     char *line_ptr;
319     int major, minor;
320     char dev_letter;
321 ats 1.20 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 pajs 1.10 #endif
327 pajs 1.17 #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 pajs 1.10 num_diskio=0;
336 pajs 1.4
337 pajs 1.17 #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 pajs 1.10 #ifdef SOLARIS
373 pajs 1.4 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 pajs 1.5 /* We dont want metadevices appearins as num_diskio */
382 pajs 1.4 if(strcmp(ksp->ks_module, "md")==0) continue;
383     if((kstat_read(kc, ksp, &kios))==-1){
384     }
385    
386 pajs 1.5 if((diskio_stats=diskio_stat_malloc(num_diskio+1, &sizeof_diskio_stats, diskio_stats))==NULL){
387 pajs 1.4 kstat_close(kc);
388     return NULL;
389     }
390 pajs 1.5 diskio_stats_ptr=diskio_stats+num_diskio;
391 pajs 1.4
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 pajs 1.14 diskio_stats_ptr->systime=time(NULL);
400 pajs 1.5 num_diskio++;
401 pajs 1.4 }
402     }
403    
404     kstat_close(kc);
405 pajs 1.10 #endif
406 pajs 1.4
407 pajs 1.10 #ifdef LINUX
408 ats 1.20 num_diskio = 0;
409     n = 0;
410 pajs 1.10
411 ats 1.20 /* 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 pajs 1.10
414 ats 1.20 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 pajs 1.11 }
451 pajs 1.12
452 ats 1.20 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 pajs 1.10
461 ats 1.20 n++;
462     }
463 pajs 1.10
464 ats 1.20 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 pajs 1.10
486 ats 1.20 /* 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 pajs 1.10 case 22:
497 ats 1.20 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 pajs 1.11 break;
508 ats 1.20 case 9:
509     case 43:
510 pajs 1.11 break;
511 pajs 1.10 default:
512 ats 1.20 minor *= 16;
513 pajs 1.11 break;
514 ats 1.20 }
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 pajs 1.10 }
528     }
529 pajs 1.16
530 ats 1.20 num_diskio = n;
531     out:
532     if (f != NULL) fclose(f);
533 pajs 1.10
534     #endif
535 pajs 1.5 *entries=num_diskio;
536 pajs 1.4
537     return diskio_stats;
538 pajs 1.5 }
539    
540 pajs 1.6 diskio_stat_t *get_diskio_stats_diff(int *entries){
541 pajs 1.5 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 pajs 1.8
602     *entries=sizeof_diskio_stats_diff;
603 pajs 1.5 return diskio_stats_diff;
604 pajs 1.2 }