ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.8
Committed: Fri Aug 29 06:56:12 2003 UTC (21 years, 4 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.7: +118 -32 lines
Log Message:
Only fetch the stats that we're actually interested in.

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     /* Compare two stats by name, for sorting purposes. */
144     int stats_compare(const void *a, const void *b) {
145     return strcmp(((stat *)a)->name, ((stat *)b)->name);
146     }
147    
148 ats 1.8 void populate_const() {
149 tdb 1.1 static int zero = 0;
150    
151     /* Constants, for use with MRTG mode. */
152     add_stat(INT, &zero, "const", "0", NULL);
153 ats 1.8 }
154 tdb 1.1
155 ats 1.8 void populate_cpu() {
156     if (use_cpu_percent) {
157     cpu_percent_t *cpu_p = cpu_percent_usage();
158 tdb 1.1
159     if (cpu_p != NULL) {
160     add_stat(FLOAT, &cpu_p->user,
161     "cpu", "user", NULL);
162     add_stat(FLOAT, &cpu_p->kernel,
163     "cpu", "kernel", NULL);
164     add_stat(FLOAT, &cpu_p->idle,
165     "cpu", "idle", NULL);
166     add_stat(FLOAT, &cpu_p->iowait,
167     "cpu", "iowait", NULL);
168     add_stat(FLOAT, &cpu_p->swap,
169     "cpu", "swap", NULL);
170     add_stat(FLOAT, &cpu_p->nice,
171     "cpu", "nice", NULL);
172 ats 1.7 add_stat(TIME_T, &cpu_p->time_taken,
173 tdb 1.1 "cpu", "time_taken", NULL);
174     }
175     } else {
176 ats 1.8 cpu_states_t *cpu_s;
177    
178 tdb 1.1 cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
179     if (cpu_s != NULL) {
180     add_stat(LONG_LONG, &cpu_s->user,
181     "cpu", "user", NULL);
182     add_stat(LONG_LONG, &cpu_s->kernel,
183     "cpu", "kernel", NULL);
184     add_stat(LONG_LONG, &cpu_s->idle,
185     "cpu", "idle", NULL);
186     add_stat(LONG_LONG, &cpu_s->iowait,
187     "cpu", "iowait", NULL);
188     add_stat(LONG_LONG, &cpu_s->swap,
189     "cpu", "swap", NULL);
190     add_stat(LONG_LONG, &cpu_s->nice,
191     "cpu", "nice", NULL);
192     add_stat(LONG_LONG, &cpu_s->total,
193     "cpu", "total", NULL);
194     add_stat(TIME_T, &cpu_s->systime,
195     "cpu", "systime", NULL);
196     }
197     }
198 ats 1.8 }
199    
200     void populate_mem() {
201     mem_stat_t *mem = get_memory_stats();
202 tdb 1.1
203     if (mem != NULL) {
204     add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
205     add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
206     add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
207     add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
208     }
209 ats 1.8 }
210    
211     void populate_load() {
212     load_stat_t *load = get_load_stats();
213 tdb 1.1
214     if (load != NULL) {
215     add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
216     add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
217     add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
218     }
219 ats 1.8 }
220    
221     void populate_user() {
222     user_stat_t *user = get_user_stats();
223 tdb 1.1
224     if (user != NULL) {
225     add_stat(INT, &user->num_entries, "user", "num", NULL);
226     add_stat(STRING, &user->name_list, "user", "names", NULL);
227     }
228 ats 1.8 }
229    
230     void populate_swap() {
231     swap_stat_t *swap = get_swap_stats();
232 tdb 1.1
233     if (swap != NULL) {
234     add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
235     add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
236     add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
237     }
238 ats 1.8 }
239    
240     void populate_general() {
241     general_stat_t *gen = get_general_stats();
242 tdb 1.1
243     if (gen != NULL) {
244     add_stat(STRING, &gen->os_name,
245     "general", "os_name", NULL);
246     add_stat(STRING, &gen->os_release,
247     "general", "os_release", NULL);
248     add_stat(STRING, &gen->os_version,
249     "general", "os_version", NULL);
250     add_stat(STRING, &gen->platform, "general", "platform", NULL);
251     add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
252     add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
253     }
254 ats 1.8 }
255    
256     void populate_fs() {
257     int n, i;
258     disk_stat_t *disk = get_disk_stats(&n);
259 tdb 1.1
260     if (disk != NULL) {
261     for (i = 0; i < n; i++) {
262     /* FIXME it'd be nicer if libstatgrab did this */
263     const char *name = disk[i].device_name,
264     *p = strrchr(name, '/');
265     if (p != NULL)
266     name = p + 1;
267     if (*name == '\0')
268     name = "root";
269    
270     add_stat(STRING, &disk[i].device_name,
271     "fs", name, "device_name", NULL);
272     add_stat(STRING, &disk[i].fs_type,
273     "fs", name, "fs_type", NULL);
274     add_stat(STRING, &disk[i].mnt_point,
275     "fs", name, "mnt_point", NULL);
276     add_stat(LONG_LONG, &disk[i].size,
277     "fs", name, "size", NULL);
278     add_stat(LONG_LONG, &disk[i].used,
279     "fs", name, "used", NULL);
280     add_stat(LONG_LONG, &disk[i].avail,
281     "fs", name, "avail", NULL);
282     add_stat(LONG_LONG, &disk[i].total_inodes,
283     "fs", name, "total_inodes", NULL);
284     add_stat(LONG_LONG, &disk[i].used_inodes,
285     "fs", name, "used_inodes", NULL);
286     add_stat(LONG_LONG, &disk[i].free_inodes,
287     "fs", name, "free_inodes", NULL);
288     }
289     }
290 ats 1.8 }
291    
292     void populate_disk() {
293     int n, i;
294     diskio_stat_t *diskio;
295 tdb 1.1
296     diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
297     if (diskio != NULL) {
298     for (i = 0; i < n; i++) {
299     const char *name = diskio[i].disk_name;
300    
301     add_stat(STRING, &diskio[i].disk_name,
302     "disk", name, "disk_name", NULL);
303     add_stat(LONG_LONG, &diskio[i].read_bytes,
304     "disk", name, "read_bytes", NULL);
305     add_stat(LONG_LONG, &diskio[i].write_bytes,
306     "disk", name, "write_bytes", NULL);
307     add_stat(TIME_T, &diskio[i].systime,
308     "disk", name, "systime", NULL);
309     }
310     }
311 ats 1.8 }
312    
313     void populate_proc() {
314     process_stat_t *proc = get_process_stats();
315 tdb 1.1
316     if (proc != NULL) {
317     add_stat(INT, &proc->total, "proc", "total", NULL);
318     add_stat(INT, &proc->running, "proc", "running", NULL);
319     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
320     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
321     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
322     }
323 ats 1.8 }
324    
325     void populate_net() {
326     int n, i;
327     network_stat_t *net;
328 tdb 1.1
329     net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
330     if (net != NULL) {
331     for (i = 0; i < n; i++) {
332     const char *name = net[i].interface_name;
333    
334     add_stat(STRING, &net[i].interface_name,
335     "net", name, "interface_name", NULL);
336     add_stat(LONG_LONG, &net[i].tx,
337     "net", name, "tx", NULL);
338     add_stat(LONG_LONG, &net[i].rx,
339     "net", name, "rx", NULL);
340     add_stat(TIME_T, &net[i].systime,
341     "net", name, "systime", NULL);
342     }
343     }
344 ats 1.8 }
345    
346     void populate_page() {
347     page_stat_t *page;
348 tdb 1.1
349     page = use_diffs ? get_page_stats_diff() : get_page_stats();
350     if (page != NULL) {
351     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
352     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
353     add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
354     }
355 ats 1.8 }
356    
357     typedef struct {
358     const char *name;
359     void (*populate)();
360     int interesting;
361     } toplevel;
362     toplevel toplevels[] = {
363     {"const.", populate_const, 0},
364     {"cpu.", populate_cpu, 0},
365     {"mem.", populate_mem, 0},
366     {"load.", populate_load, 0},
367     {"user.", populate_user, 0},
368     {"swap.", populate_swap, 0},
369     {"general.", populate_general, 0},
370     {"fs.", populate_fs, 0},
371     {"disk.", populate_disk, 0},
372     {"proc.", populate_proc, 0},
373     {"net.", populate_net, 0},
374     {"page.", populate_page, 0},
375     {NULL, NULL, 0}
376     };
377    
378     /* Set the "interesting" flag on the sections that we actually need to
379     fetch. */
380     void select_interesting(int argc, char **argv) {
381     toplevel *t;
382    
383     if (argc == 0) {
384     for (t = &toplevels[0]; t->name != NULL; t++)
385     t->interesting = 1;
386     } else {
387     int i;
388    
389     for (i = 0; i < argc; i++) {
390     for (t = &toplevels[0]; t->name != NULL; t++) {
391     if (strncmp(argv[i], t->name,
392     strlen(t->name)) == 0) {
393     t->interesting = 1;
394     break;
395     }
396     }
397     }
398     }
399     }
400    
401     /* Clear and rebuild the stats array. */
402     void get_stats() {
403     toplevel *t;
404    
405     clear_stats();
406    
407     for (t = &toplevels[0]; t->name != NULL; t++) {
408     if (t->interesting)
409     t->populate();
410     }
411 tdb 1.1
412     qsort(stats, num_stats, sizeof *stats, stats_compare);
413     }
414    
415     /* Print the value of a stat. */
416     void print_stat_value(const stat *s) {
417     void *v = s->stat;
418    
419     switch (s->type) {
420     case LONG_LONG:
421     printf("%lld", *(long long *)v);
422     break;
423     case TIME_T:
424     /* FIXME option for formatted time? */
425     printf("%ld", *(time_t *)v);
426     break;
427     case FLOAT:
428     printf("%f", *(float *)v);
429     break;
430     case DOUBLE:
431     printf("%f", *(double *)v);
432     break;
433     case STRING:
434     /* FIXME escaping? */
435     printf("%s", *(char **)v);
436     break;
437     case INT:
438     printf("%d", *(int *)v);
439     break;
440     }
441     }
442    
443     /* Print the name and value of a stat. */
444     void print_stat(const stat *s) {
445     switch (display_mode) {
446     case DISPLAY_LINUX:
447     printf("%s = ", s->name);
448     break;
449     case DISPLAY_BSD:
450     printf("%s: ", s->name);
451     break;
452     case DISPLAY_MRTG:
453     case DISPLAY_PLAIN:
454     break;
455     }
456     print_stat_value(s);
457     printf("\n");
458     }
459    
460     /* Print stats as specified on the provided command line. */
461     void print_stats(int argc, char **argv) {
462     int i;
463    
464     if (argc == optind) {
465     /* Print all stats. */
466     for (i = 0; i < num_stats; i++)
467     print_stat(&stats[i]);
468     } else {
469     /* Print selected stats. */
470     for (i = optind; i < argc; i++) {
471     stat key;
472     const stat *s;
473    
474     key.name = argv[i];
475     s = (const stat *)bsearch(&key, stats, num_stats,
476     sizeof *stats,
477     stats_compare);
478     if (s != NULL) {
479     print_stat(s);
480     }
481     }
482     }
483     }
484    
485     void usage() {
486     printf("Usage: statgrab [OPTION]... [STAT]...\n"
487     "Display system statistics (all statistics by default).\n"
488     "\n");
489     printf(" -l Linux sysctl-style output (default)\n"
490     " -b BSD sysctl-style output\n"
491     " -m MRTG-compatible output\n"
492     " -u Plain output (only show values)\n"
493     " -n Display cumulative stats once (default)\n"
494     " -s Display stat differences repeatedly\n"
495     " -o Display stat differences once\n"
496     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
497 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
498     " absolute values\n"
499 tdb 1.1 "\n");
500 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
501 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
502 tdb 1.1 exit(1);
503     }
504    
505     int main(int argc, char **argv) {
506     opterr = 0;
507     while (1) {
508     int c = getopt(argc, argv, "lbmunsot:p");
509     if (c == -1)
510     break;
511     switch (c) {
512     case 'l':
513     display_mode = DISPLAY_LINUX;
514     break;
515     case 'b':
516     display_mode = DISPLAY_BSD;
517     break;
518     case 'm':
519     display_mode = DISPLAY_MRTG;
520     break;
521     case 'u':
522     display_mode = DISPLAY_PLAIN;
523     break;
524     case 'n':
525     repeat_mode = REPEAT_NONE;
526     break;
527     case 's':
528     repeat_mode = REPEAT_FOREVER;
529     break;
530     case 'o':
531     repeat_mode = REPEAT_ONCE;
532     break;
533     case 't':
534     repeat_time = atoi(optarg);
535     break;
536     case 'p':
537     use_cpu_percent = 1;
538     break;
539     default:
540     usage();
541     }
542     }
543    
544     if (display_mode == DISPLAY_MRTG) {
545     if ((argc - optind) != 2)
546     die("mrtg mode: must specify exactly two stats");
547 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
548 tdb 1.1 die("mrtg mode: cannot repeat display");
549     }
550 ats 1.5
551     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
552     die("CPU percentage usage display requires stat differences");
553 tdb 1.1
554 ats 1.8 if (repeat_mode == REPEAT_NONE)
555     use_diffs = 0;
556     else
557     use_diffs = 1;
558    
559     select_interesting(argc - optind, &argv[optind]);
560    
561 tdb 1.1 switch (repeat_mode) {
562     case REPEAT_NONE:
563 ats 1.8 get_stats();
564 tdb 1.1 print_stats(argc, argv);
565     break;
566     case REPEAT_ONCE:
567 ats 1.8 get_stats();
568 tdb 1.1 sleep(repeat_time);
569 ats 1.8 get_stats();
570 tdb 1.1 print_stats(argc, argv);
571     break;
572     case REPEAT_FOREVER:
573     while (1) {
574 ats 1.8 get_stats();
575 tdb 1.1 print_stats(argc, argv);
576     printf("\n");
577     sleep(repeat_time);
578     }
579     }
580    
581     if (display_mode == DISPLAY_MRTG) {
582     printf("\n");
583     printf("statgrab\n");
584     }
585    
586     return 0;
587     }
588