ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.9
Committed: Sun Aug 31 13:23:19 2003 UTC (20 years, 8 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_6, LIBSTATGRAB_0_5_1
Changes since 1.8: +41 -7 lines
Log Message:
If invoked as "statgrab foo.", print all stats starting with "foo.".
Print multiple values with the same name correctly.

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     const char *name = disk[i].device_name,
272     *p = strrchr(name, '/');
273     if (p != NULL)
274     name = p + 1;
275     if (*name == '\0')
276     name = "root";
277    
278     add_stat(STRING, &disk[i].device_name,
279     "fs", name, "device_name", NULL);
280     add_stat(STRING, &disk[i].fs_type,
281     "fs", name, "fs_type", NULL);
282     add_stat(STRING, &disk[i].mnt_point,
283     "fs", name, "mnt_point", NULL);
284     add_stat(LONG_LONG, &disk[i].size,
285     "fs", name, "size", NULL);
286     add_stat(LONG_LONG, &disk[i].used,
287     "fs", name, "used", NULL);
288     add_stat(LONG_LONG, &disk[i].avail,
289     "fs", name, "avail", NULL);
290     add_stat(LONG_LONG, &disk[i].total_inodes,
291     "fs", name, "total_inodes", NULL);
292     add_stat(LONG_LONG, &disk[i].used_inodes,
293     "fs", name, "used_inodes", NULL);
294     add_stat(LONG_LONG, &disk[i].free_inodes,
295     "fs", name, "free_inodes", NULL);
296     }
297     }
298 ats 1.8 }
299    
300     void populate_disk() {
301     int n, i;
302     diskio_stat_t *diskio;
303 tdb 1.1
304     diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
305     if (diskio != NULL) {
306     for (i = 0; i < n; i++) {
307     const char *name = diskio[i].disk_name;
308    
309     add_stat(STRING, &diskio[i].disk_name,
310     "disk", name, "disk_name", NULL);
311     add_stat(LONG_LONG, &diskio[i].read_bytes,
312     "disk", name, "read_bytes", NULL);
313     add_stat(LONG_LONG, &diskio[i].write_bytes,
314     "disk", name, "write_bytes", NULL);
315     add_stat(TIME_T, &diskio[i].systime,
316     "disk", name, "systime", NULL);
317     }
318     }
319 ats 1.8 }
320    
321     void populate_proc() {
322     process_stat_t *proc = get_process_stats();
323 tdb 1.1
324     if (proc != NULL) {
325     add_stat(INT, &proc->total, "proc", "total", NULL);
326     add_stat(INT, &proc->running, "proc", "running", NULL);
327     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
328     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
329     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
330     }
331 ats 1.8 }
332    
333     void populate_net() {
334     int n, i;
335     network_stat_t *net;
336 tdb 1.1
337     net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
338     if (net != NULL) {
339     for (i = 0; i < n; i++) {
340     const char *name = net[i].interface_name;
341    
342     add_stat(STRING, &net[i].interface_name,
343     "net", name, "interface_name", NULL);
344     add_stat(LONG_LONG, &net[i].tx,
345     "net", name, "tx", NULL);
346     add_stat(LONG_LONG, &net[i].rx,
347     "net", name, "rx", NULL);
348     add_stat(TIME_T, &net[i].systime,
349     "net", name, "systime", NULL);
350     }
351     }
352 ats 1.8 }
353    
354     void populate_page() {
355     page_stat_t *page;
356 tdb 1.1
357     page = use_diffs ? get_page_stats_diff() : get_page_stats();
358     if (page != NULL) {
359     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
360     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
361     add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
362     }
363 ats 1.8 }
364    
365     typedef struct {
366     const char *name;
367     void (*populate)();
368     int interesting;
369     } toplevel;
370     toplevel toplevels[] = {
371     {"const.", populate_const, 0},
372     {"cpu.", populate_cpu, 0},
373     {"mem.", populate_mem, 0},
374     {"load.", populate_load, 0},
375     {"user.", populate_user, 0},
376     {"swap.", populate_swap, 0},
377     {"general.", populate_general, 0},
378     {"fs.", populate_fs, 0},
379     {"disk.", populate_disk, 0},
380     {"proc.", populate_proc, 0},
381     {"net.", populate_net, 0},
382     {"page.", populate_page, 0},
383     {NULL, NULL, 0}
384     };
385    
386     /* Set the "interesting" flag on the sections that we actually need to
387     fetch. */
388     void select_interesting(int argc, char **argv) {
389     toplevel *t;
390    
391     if (argc == 0) {
392     for (t = &toplevels[0]; t->name != NULL; t++)
393     t->interesting = 1;
394     } else {
395     int i;
396    
397     for (i = 0; i < argc; i++) {
398     for (t = &toplevels[0]; t->name != NULL; t++) {
399     if (strncmp(argv[i], t->name,
400     strlen(t->name)) == 0) {
401     t->interesting = 1;
402     break;
403     }
404     }
405     }
406     }
407     }
408    
409     /* Clear and rebuild the stats array. */
410     void get_stats() {
411     toplevel *t;
412    
413     clear_stats();
414    
415     for (t = &toplevels[0]; t->name != NULL; t++) {
416     if (t->interesting)
417     t->populate();
418     }
419 tdb 1.1
420     qsort(stats, num_stats, sizeof *stats, stats_compare);
421     }
422    
423     /* Print the value of a stat. */
424     void print_stat_value(const stat *s) {
425     void *v = s->stat;
426    
427     switch (s->type) {
428     case LONG_LONG:
429     printf("%lld", *(long long *)v);
430     break;
431     case TIME_T:
432     /* FIXME option for formatted time? */
433     printf("%ld", *(time_t *)v);
434     break;
435     case FLOAT:
436     printf("%f", *(float *)v);
437     break;
438     case DOUBLE:
439     printf("%f", *(double *)v);
440     break;
441     case STRING:
442     /* FIXME escaping? */
443     printf("%s", *(char **)v);
444     break;
445     case INT:
446     printf("%d", *(int *)v);
447     break;
448     }
449     }
450    
451     /* Print the name and value of a stat. */
452     void print_stat(const stat *s) {
453     switch (display_mode) {
454     case DISPLAY_LINUX:
455     printf("%s = ", s->name);
456     break;
457     case DISPLAY_BSD:
458     printf("%s: ", s->name);
459     break;
460     case DISPLAY_MRTG:
461     case DISPLAY_PLAIN:
462     break;
463     }
464     print_stat_value(s);
465     printf("\n");
466     }
467    
468     /* Print stats as specified on the provided command line. */
469     void print_stats(int argc, char **argv) {
470     int i;
471    
472     if (argc == optind) {
473     /* Print all stats. */
474     for (i = 0; i < num_stats; i++)
475     print_stat(&stats[i]);
476     } else {
477     /* Print selected stats. */
478     for (i = optind; i < argc; i++) {
479 ats 1.9 char *name = argv[i];
480 tdb 1.1 stat key;
481 ats 1.9 const stat *s, *end;
482     int (*compare)(const void *, const void *);
483    
484     key.name = name;
485     if (name[strlen(name) - 1] == '.')
486     compare = stats_compare_prefix;
487     else
488     compare = stats_compare;
489 tdb 1.1
490     s = (const stat *)bsearch(&key, stats, num_stats,
491 ats 1.9 sizeof *stats, compare);
492     if (s == NULL) {
493     printf("Unknown stat %s\n", name);
494     continue;
495     }
496    
497     /* Find the range of stats the user wanted. */
498     for (; s >= stats; s--) {
499     if (compare(&key, s) != 0)
500     break;
501     }
502     s++;
503     for (end = s; end < &stats[num_stats]; end++) {
504     if (compare(&key, end) != 0)
505     break;
506     }
507    
508     /* And print them. */
509     for (; s < end; s++) {
510 tdb 1.1 print_stat(s);
511     }
512     }
513     }
514     }
515    
516     void usage() {
517     printf("Usage: statgrab [OPTION]... [STAT]...\n"
518 ats 1.9 "Display system statistics.\n"
519     "\n"
520     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
521     "statistics starting with that prefix.\n"
522 tdb 1.1 "\n");
523     printf(" -l Linux sysctl-style output (default)\n"
524     " -b BSD sysctl-style output\n"
525     " -m MRTG-compatible output\n"
526     " -u Plain output (only show values)\n"
527     " -n Display cumulative stats once (default)\n"
528     " -s Display stat differences repeatedly\n"
529     " -o Display stat differences once\n"
530     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
531 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
532     " absolute values\n"
533 tdb 1.1 "\n");
534 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
535 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
536 tdb 1.1 exit(1);
537     }
538    
539     int main(int argc, char **argv) {
540     opterr = 0;
541     while (1) {
542     int c = getopt(argc, argv, "lbmunsot:p");
543     if (c == -1)
544     break;
545     switch (c) {
546     case 'l':
547     display_mode = DISPLAY_LINUX;
548     break;
549     case 'b':
550     display_mode = DISPLAY_BSD;
551     break;
552     case 'm':
553     display_mode = DISPLAY_MRTG;
554     break;
555     case 'u':
556     display_mode = DISPLAY_PLAIN;
557     break;
558     case 'n':
559     repeat_mode = REPEAT_NONE;
560     break;
561     case 's':
562     repeat_mode = REPEAT_FOREVER;
563     break;
564     case 'o':
565     repeat_mode = REPEAT_ONCE;
566     break;
567     case 't':
568     repeat_time = atoi(optarg);
569     break;
570     case 'p':
571     use_cpu_percent = 1;
572     break;
573     default:
574     usage();
575     }
576     }
577    
578     if (display_mode == DISPLAY_MRTG) {
579     if ((argc - optind) != 2)
580     die("mrtg mode: must specify exactly two stats");
581 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
582 tdb 1.1 die("mrtg mode: cannot repeat display");
583     }
584 ats 1.5
585     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
586     die("CPU percentage usage display requires stat differences");
587 tdb 1.1
588 ats 1.8 if (repeat_mode == REPEAT_NONE)
589     use_diffs = 0;
590     else
591     use_diffs = 1;
592    
593     select_interesting(argc - optind, &argv[optind]);
594    
595 tdb 1.1 switch (repeat_mode) {
596     case REPEAT_NONE:
597 ats 1.8 get_stats();
598 tdb 1.1 print_stats(argc, argv);
599     break;
600     case REPEAT_ONCE:
601 ats 1.8 get_stats();
602 tdb 1.1 sleep(repeat_time);
603 ats 1.8 get_stats();
604 tdb 1.1 print_stats(argc, argv);
605     break;
606     case REPEAT_FOREVER:
607     while (1) {
608 ats 1.8 get_stats();
609 tdb 1.1 print_stats(argc, argv);
610     printf("\n");
611     sleep(repeat_time);
612     }
613     }
614    
615     if (display_mode == DISPLAY_MRTG) {
616     printf("\n");
617     printf("statgrab\n");
618     }
619    
620     return 0;
621     }
622