ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.10
Committed: Sat Oct 18 16:12:03 2003 UTC (21 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_6_1
Changes since 1.9: +18 -7 lines
Log Message:
Mangle device names more correctly; now a device like "/dev/vinum/foo"
will become "vinum_foo", rather than "foo".

File Contents

# User Rev Content
1 tdb 1.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 tdb 1.3 #ifdef HAVE_CONFIG_H
22     #include "config.h"
23     #endif
24    
25 tdb 1.1 #include <statgrab.h>
26     #include <string.h>
27     #include <stdio.h>
28     #include <stdarg.h>
29     #include <stdlib.h>
30     #include <unistd.h>
31    
32     typedef enum {
33     LONG_LONG = 0,
34     TIME_T,
35     FLOAT,
36     DOUBLE,
37     STRING,
38     INT
39     } stat_type;
40    
41     typedef enum {
42     DISPLAY_LINUX = 0,
43     DISPLAY_BSD,
44     DISPLAY_MRTG,
45     DISPLAY_PLAIN
46     } display_mode_type;
47    
48     typedef enum {
49     REPEAT_NONE = 0,
50     REPEAT_ONCE,
51     REPEAT_FOREVER
52     } repeat_mode_type;
53    
54     typedef struct {
55     char *name;
56     stat_type type;
57     void *stat;
58     } stat;
59    
60     stat *stats = NULL;
61     int num_stats = 0;
62     int alloc_stats = 0;
63     #define INCREMENT_STATS 64
64    
65     display_mode_type display_mode = DISPLAY_LINUX;
66     repeat_mode_type repeat_mode = REPEAT_NONE;
67     int repeat_time = 1;
68     int use_cpu_percent = 0;
69 ats 1.8 int use_diffs = 0;
70 tdb 1.1
71     /* Exit with an error message. */
72     void die(const char *s) {
73     fprintf(stderr, "fatal: %s\n", s);
74     exit(1);
75     }
76    
77     /* Remove all the recorded stats. */
78     void clear_stats() {
79     int i;
80    
81     for (i = 0; i < num_stats; i++)
82     free(stats[i].name);
83     num_stats = 0;
84     }
85    
86     /* Add a stat. The varargs make up the name, joined with dots; the name is
87     terminated with a NULL. */
88     void add_stat(stat_type type, void *stat, ...) {
89     va_list ap;
90     int len = 0;
91     char *name, *p;
92    
93     /* Figure out how long the name will be, including dots and trailing
94     \0. */
95     va_start(ap, stat);
96     while (1) {
97     const char *part = va_arg(ap, const char *);
98     if (part == NULL)
99     break;
100     len += 1 + strlen(part);
101     }
102     va_end(ap);
103    
104     /* Paste the name together. */
105     name = malloc(len);
106     if (name == NULL)
107     die("out of memory");
108     p = name;
109     va_start(ap, stat);
110     while (1) {
111     const char *part = va_arg(ap, const char *);
112     int partlen;
113     if (part == NULL)
114     break;
115     partlen = strlen(part);
116     memcpy(p, part, partlen);
117     p += partlen;
118     *p++ = '.';
119     }
120     va_end(ap);
121     *--p = '\0';
122    
123     /* Replace spaces with underscores. */
124     for (p = name; *p != '\0'; p++) {
125     if (*p == ' ')
126     *p = '_';
127     }
128    
129     /* Stretch the stats array if necessary. */
130     if (num_stats >= alloc_stats) {
131     alloc_stats += INCREMENT_STATS;
132     stats = realloc(stats, alloc_stats * sizeof *stats);
133     if (stats == NULL)
134     die("out of memory");
135     }
136    
137     stats[num_stats].name = name;
138     stats[num_stats].type = type;
139     stats[num_stats].stat = stat;
140     ++num_stats;
141     }
142    
143 ats 1.9 /* Compare two stats by name for qsort and bsearch. */
144 tdb 1.1 int stats_compare(const void *a, const void *b) {
145     return strcmp(((stat *)a)->name, ((stat *)b)->name);
146     }
147    
148 ats 1.9 /* Compare up to the length of the key for bsearch. */
149     int stats_compare_prefix(const void *key, const void *item) {
150     const char *kn = ((stat *)key)->name;
151     const char *in = ((stat *)item)->name;
152    
153     return strncmp(kn, in, strlen(kn));
154     }
155    
156 ats 1.8 void populate_const() {
157 tdb 1.1 static int zero = 0;
158    
159     /* Constants, for use with MRTG mode. */
160     add_stat(INT, &zero, "const", "0", NULL);
161 ats 1.8 }
162 tdb 1.1
163 ats 1.8 void populate_cpu() {
164     if (use_cpu_percent) {
165     cpu_percent_t *cpu_p = cpu_percent_usage();
166 tdb 1.1
167     if (cpu_p != NULL) {
168     add_stat(FLOAT, &cpu_p->user,
169     "cpu", "user", NULL);
170     add_stat(FLOAT, &cpu_p->kernel,
171     "cpu", "kernel", NULL);
172     add_stat(FLOAT, &cpu_p->idle,
173     "cpu", "idle", NULL);
174     add_stat(FLOAT, &cpu_p->iowait,
175     "cpu", "iowait", NULL);
176     add_stat(FLOAT, &cpu_p->swap,
177     "cpu", "swap", NULL);
178     add_stat(FLOAT, &cpu_p->nice,
179     "cpu", "nice", NULL);
180 ats 1.7 add_stat(TIME_T, &cpu_p->time_taken,
181 tdb 1.1 "cpu", "time_taken", NULL);
182     }
183     } else {
184 ats 1.8 cpu_states_t *cpu_s;
185    
186 tdb 1.1 cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
187     if (cpu_s != NULL) {
188     add_stat(LONG_LONG, &cpu_s->user,
189     "cpu", "user", NULL);
190     add_stat(LONG_LONG, &cpu_s->kernel,
191     "cpu", "kernel", NULL);
192     add_stat(LONG_LONG, &cpu_s->idle,
193     "cpu", "idle", NULL);
194     add_stat(LONG_LONG, &cpu_s->iowait,
195     "cpu", "iowait", NULL);
196     add_stat(LONG_LONG, &cpu_s->swap,
197     "cpu", "swap", NULL);
198     add_stat(LONG_LONG, &cpu_s->nice,
199     "cpu", "nice", NULL);
200     add_stat(LONG_LONG, &cpu_s->total,
201     "cpu", "total", NULL);
202     add_stat(TIME_T, &cpu_s->systime,
203     "cpu", "systime", NULL);
204     }
205     }
206 ats 1.8 }
207    
208     void populate_mem() {
209     mem_stat_t *mem = get_memory_stats();
210 tdb 1.1
211     if (mem != NULL) {
212     add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
213     add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
214     add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
215     add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
216     }
217 ats 1.8 }
218    
219     void populate_load() {
220     load_stat_t *load = get_load_stats();
221 tdb 1.1
222     if (load != NULL) {
223     add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
224     add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
225     add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
226     }
227 ats 1.8 }
228    
229     void populate_user() {
230     user_stat_t *user = get_user_stats();
231 tdb 1.1
232     if (user != NULL) {
233     add_stat(INT, &user->num_entries, "user", "num", NULL);
234     add_stat(STRING, &user->name_list, "user", "names", NULL);
235     }
236 ats 1.8 }
237    
238     void populate_swap() {
239     swap_stat_t *swap = get_swap_stats();
240 tdb 1.1
241     if (swap != NULL) {
242     add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
243     add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
244     add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
245     }
246 ats 1.8 }
247    
248     void populate_general() {
249     general_stat_t *gen = get_general_stats();
250 tdb 1.1
251     if (gen != NULL) {
252     add_stat(STRING, &gen->os_name,
253     "general", "os_name", NULL);
254     add_stat(STRING, &gen->os_release,
255     "general", "os_release", NULL);
256     add_stat(STRING, &gen->os_version,
257     "general", "os_version", NULL);
258     add_stat(STRING, &gen->platform, "general", "platform", NULL);
259     add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
260     add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
261     }
262 ats 1.8 }
263    
264     void populate_fs() {
265     int n, i;
266     disk_stat_t *disk = get_disk_stats(&n);
267 tdb 1.1
268     if (disk != NULL) {
269     for (i = 0; i < n; i++) {
270     /* FIXME it'd be nicer if libstatgrab did this */
271 ats 1.10 char *buf, *name, *p;
272     const char *device = disk[i].device_name;
273    
274     if (strcmp(device, "/") == 0)
275     device = "root";
276    
277     buf = strdup(device);
278     if (buf == NULL)
279     die("out of memory");
280    
281     name = buf;
282     if (strncmp(name, "/dev/", 5) == 0)
283     name += 5;
284     while ((p = strchr(name, '/')) != NULL)
285     *p = '_';
286    
287 tdb 1.1 add_stat(STRING, &disk[i].device_name,
288     "fs", name, "device_name", NULL);
289     add_stat(STRING, &disk[i].fs_type,
290     "fs", name, "fs_type", NULL);
291     add_stat(STRING, &disk[i].mnt_point,
292     "fs", name, "mnt_point", NULL);
293     add_stat(LONG_LONG, &disk[i].size,
294     "fs", name, "size", NULL);
295     add_stat(LONG_LONG, &disk[i].used,
296     "fs", name, "used", NULL);
297     add_stat(LONG_LONG, &disk[i].avail,
298     "fs", name, "avail", NULL);
299     add_stat(LONG_LONG, &disk[i].total_inodes,
300     "fs", name, "total_inodes", NULL);
301     add_stat(LONG_LONG, &disk[i].used_inodes,
302     "fs", name, "used_inodes", NULL);
303     add_stat(LONG_LONG, &disk[i].free_inodes,
304     "fs", name, "free_inodes", NULL);
305 ats 1.10
306     free(buf);
307 tdb 1.1 }
308     }
309 ats 1.8 }
310    
311     void populate_disk() {
312     int n, i;
313     diskio_stat_t *diskio;
314 tdb 1.1
315     diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
316     if (diskio != NULL) {
317     for (i = 0; i < n; i++) {
318     const char *name = diskio[i].disk_name;
319    
320     add_stat(STRING, &diskio[i].disk_name,
321     "disk", name, "disk_name", NULL);
322     add_stat(LONG_LONG, &diskio[i].read_bytes,
323     "disk", name, "read_bytes", NULL);
324     add_stat(LONG_LONG, &diskio[i].write_bytes,
325     "disk", name, "write_bytes", NULL);
326     add_stat(TIME_T, &diskio[i].systime,
327     "disk", name, "systime", NULL);
328     }
329     }
330 ats 1.8 }
331    
332     void populate_proc() {
333     process_stat_t *proc = get_process_stats();
334 tdb 1.1
335     if (proc != NULL) {
336     add_stat(INT, &proc->total, "proc", "total", NULL);
337     add_stat(INT, &proc->running, "proc", "running", NULL);
338     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
339     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
340     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
341     }
342 ats 1.8 }
343    
344     void populate_net() {
345     int n, i;
346     network_stat_t *net;
347 tdb 1.1
348     net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
349     if (net != NULL) {
350     for (i = 0; i < n; i++) {
351     const char *name = net[i].interface_name;
352    
353     add_stat(STRING, &net[i].interface_name,
354     "net", name, "interface_name", NULL);
355     add_stat(LONG_LONG, &net[i].tx,
356     "net", name, "tx", NULL);
357     add_stat(LONG_LONG, &net[i].rx,
358     "net", name, "rx", NULL);
359     add_stat(TIME_T, &net[i].systime,
360     "net", name, "systime", NULL);
361     }
362     }
363 ats 1.8 }
364    
365     void populate_page() {
366     page_stat_t *page;
367 tdb 1.1
368     page = use_diffs ? get_page_stats_diff() : get_page_stats();
369     if (page != NULL) {
370     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
371     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
372     add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
373     }
374 ats 1.8 }
375    
376     typedef struct {
377     const char *name;
378     void (*populate)();
379     int interesting;
380     } toplevel;
381     toplevel toplevels[] = {
382     {"const.", populate_const, 0},
383     {"cpu.", populate_cpu, 0},
384     {"mem.", populate_mem, 0},
385     {"load.", populate_load, 0},
386     {"user.", populate_user, 0},
387     {"swap.", populate_swap, 0},
388     {"general.", populate_general, 0},
389     {"fs.", populate_fs, 0},
390     {"disk.", populate_disk, 0},
391     {"proc.", populate_proc, 0},
392     {"net.", populate_net, 0},
393     {"page.", populate_page, 0},
394     {NULL, NULL, 0}
395     };
396    
397     /* Set the "interesting" flag on the sections that we actually need to
398     fetch. */
399     void select_interesting(int argc, char **argv) {
400     toplevel *t;
401    
402     if (argc == 0) {
403     for (t = &toplevels[0]; t->name != NULL; t++)
404     t->interesting = 1;
405     } else {
406     int i;
407    
408     for (i = 0; i < argc; i++) {
409     for (t = &toplevels[0]; t->name != NULL; t++) {
410     if (strncmp(argv[i], t->name,
411     strlen(t->name)) == 0) {
412     t->interesting = 1;
413     break;
414     }
415     }
416     }
417     }
418     }
419    
420     /* Clear and rebuild the stats array. */
421     void get_stats() {
422     toplevel *t;
423    
424     clear_stats();
425    
426     for (t = &toplevels[0]; t->name != NULL; t++) {
427     if (t->interesting)
428     t->populate();
429     }
430 tdb 1.1
431     qsort(stats, num_stats, sizeof *stats, stats_compare);
432     }
433    
434     /* Print the value of a stat. */
435     void print_stat_value(const stat *s) {
436     void *v = s->stat;
437    
438     switch (s->type) {
439     case LONG_LONG:
440     printf("%lld", *(long long *)v);
441     break;
442     case TIME_T:
443     /* FIXME option for formatted time? */
444     printf("%ld", *(time_t *)v);
445     break;
446     case FLOAT:
447     printf("%f", *(float *)v);
448     break;
449     case DOUBLE:
450     printf("%f", *(double *)v);
451     break;
452     case STRING:
453     /* FIXME escaping? */
454     printf("%s", *(char **)v);
455     break;
456     case INT:
457     printf("%d", *(int *)v);
458     break;
459     }
460     }
461    
462     /* Print the name and value of a stat. */
463     void print_stat(const stat *s) {
464     switch (display_mode) {
465     case DISPLAY_LINUX:
466     printf("%s = ", s->name);
467     break;
468     case DISPLAY_BSD:
469     printf("%s: ", s->name);
470     break;
471     case DISPLAY_MRTG:
472     case DISPLAY_PLAIN:
473     break;
474     }
475     print_stat_value(s);
476     printf("\n");
477     }
478    
479     /* Print stats as specified on the provided command line. */
480     void print_stats(int argc, char **argv) {
481     int i;
482    
483     if (argc == optind) {
484     /* Print all stats. */
485     for (i = 0; i < num_stats; i++)
486     print_stat(&stats[i]);
487     } else {
488     /* Print selected stats. */
489     for (i = optind; i < argc; i++) {
490 ats 1.9 char *name = argv[i];
491 tdb 1.1 stat key;
492 ats 1.9 const stat *s, *end;
493     int (*compare)(const void *, const void *);
494    
495     key.name = name;
496     if (name[strlen(name) - 1] == '.')
497     compare = stats_compare_prefix;
498     else
499     compare = stats_compare;
500 tdb 1.1
501     s = (const stat *)bsearch(&key, stats, num_stats,
502 ats 1.9 sizeof *stats, compare);
503     if (s == NULL) {
504     printf("Unknown stat %s\n", name);
505     continue;
506     }
507    
508     /* Find the range of stats the user wanted. */
509     for (; s >= stats; s--) {
510     if (compare(&key, s) != 0)
511     break;
512     }
513     s++;
514     for (end = s; end < &stats[num_stats]; end++) {
515     if (compare(&key, end) != 0)
516     break;
517     }
518    
519     /* And print them. */
520     for (; s < end; s++) {
521 tdb 1.1 print_stat(s);
522     }
523     }
524     }
525     }
526    
527     void usage() {
528     printf("Usage: statgrab [OPTION]... [STAT]...\n"
529 ats 1.9 "Display system statistics.\n"
530     "\n"
531     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
532     "statistics starting with that prefix.\n"
533 tdb 1.1 "\n");
534     printf(" -l Linux sysctl-style output (default)\n"
535     " -b BSD sysctl-style output\n"
536     " -m MRTG-compatible output\n"
537     " -u Plain output (only show values)\n"
538     " -n Display cumulative stats once (default)\n"
539     " -s Display stat differences repeatedly\n"
540     " -o Display stat differences once\n"
541     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
542 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
543     " absolute values\n"
544 tdb 1.1 "\n");
545 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
546 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
547 tdb 1.1 exit(1);
548     }
549    
550     int main(int argc, char **argv) {
551     opterr = 0;
552     while (1) {
553     int c = getopt(argc, argv, "lbmunsot:p");
554     if (c == -1)
555     break;
556     switch (c) {
557     case 'l':
558     display_mode = DISPLAY_LINUX;
559     break;
560     case 'b':
561     display_mode = DISPLAY_BSD;
562     break;
563     case 'm':
564     display_mode = DISPLAY_MRTG;
565     break;
566     case 'u':
567     display_mode = DISPLAY_PLAIN;
568     break;
569     case 'n':
570     repeat_mode = REPEAT_NONE;
571     break;
572     case 's':
573     repeat_mode = REPEAT_FOREVER;
574     break;
575     case 'o':
576     repeat_mode = REPEAT_ONCE;
577     break;
578     case 't':
579     repeat_time = atoi(optarg);
580     break;
581     case 'p':
582     use_cpu_percent = 1;
583     break;
584     default:
585     usage();
586     }
587     }
588    
589     if (display_mode == DISPLAY_MRTG) {
590     if ((argc - optind) != 2)
591     die("mrtg mode: must specify exactly two stats");
592 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
593 tdb 1.1 die("mrtg mode: cannot repeat display");
594     }
595 ats 1.5
596     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
597     die("CPU percentage usage display requires stat differences");
598 tdb 1.1
599 ats 1.8 if (repeat_mode == REPEAT_NONE)
600     use_diffs = 0;
601     else
602     use_diffs = 1;
603    
604     select_interesting(argc - optind, &argv[optind]);
605    
606 tdb 1.1 switch (repeat_mode) {
607     case REPEAT_NONE:
608 ats 1.8 get_stats();
609 tdb 1.1 print_stats(argc, argv);
610     break;
611     case REPEAT_ONCE:
612 ats 1.8 get_stats();
613 tdb 1.1 sleep(repeat_time);
614 ats 1.8 get_stats();
615 tdb 1.1 print_stats(argc, argv);
616     break;
617     case REPEAT_FOREVER:
618     while (1) {
619 ats 1.8 get_stats();
620 tdb 1.1 print_stats(argc, argv);
621     printf("\n");
622     sleep(repeat_time);
623     }
624     }
625    
626     if (display_mode == DISPLAY_MRTG) {
627     printf("\n");
628     printf("statgrab\n");
629     }
630    
631     return 0;
632     }
633