ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.1
Committed: Tue Aug 26 12:09:44 2003 UTC (21 years, 2 months ago) by tdb
Content type: text/plain
Branch: MAIN
Log Message:
A sysctl-style program to display system statistics collected through
libstatgrab. Contributed by Adam Sampson, azz@us-lot.org.

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     #include <statgrab.h>
22     #include <string.h>
23     #include <stdio.h>
24     #include <stdarg.h>
25     #include <stdlib.h>
26     #include <unistd.h>
27    
28     typedef enum {
29     LONG_LONG = 0,
30     TIME_T,
31     FLOAT,
32     DOUBLE,
33     STRING,
34     INT
35     } stat_type;
36    
37     typedef enum {
38     DISPLAY_LINUX = 0,
39     DISPLAY_BSD,
40     DISPLAY_MRTG,
41     DISPLAY_PLAIN
42     } display_mode_type;
43    
44     typedef enum {
45     REPEAT_NONE = 0,
46     REPEAT_ONCE,
47     REPEAT_FOREVER
48     } repeat_mode_type;
49    
50     typedef struct {
51     char *name;
52     stat_type type;
53     void *stat;
54     } stat;
55    
56     stat *stats = NULL;
57     int num_stats = 0;
58     int alloc_stats = 0;
59     #define INCREMENT_STATS 64
60    
61     display_mode_type display_mode = DISPLAY_LINUX;
62     repeat_mode_type repeat_mode = REPEAT_NONE;
63     int repeat_time = 1;
64     int use_cpu_percent = 0;
65    
66     /* Exit with an error message. */
67     void die(const char *s) {
68     fprintf(stderr, "fatal: %s\n", s);
69     exit(1);
70     }
71    
72     /* Remove all the recorded stats. */
73     void clear_stats() {
74     int i;
75    
76     for (i = 0; i < num_stats; i++)
77     free(stats[i].name);
78     free(stats);
79     stats = NULL;
80     num_stats = 0;
81     alloc_stats = 0;
82     }
83    
84     /* Add a stat. The varargs make up the name, joined with dots; the name is
85     terminated with a NULL. */
86     void add_stat(stat_type type, void *stat, ...) {
87     va_list ap;
88     int len = 0;
89     char *name, *p;
90    
91     /* Figure out how long the name will be, including dots and trailing
92     \0. */
93     va_start(ap, stat);
94     while (1) {
95     const char *part = va_arg(ap, const char *);
96     if (part == NULL)
97     break;
98     len += 1 + strlen(part);
99     }
100     va_end(ap);
101    
102     /* Paste the name together. */
103     name = malloc(len);
104     if (name == NULL)
105     die("out of memory");
106     p = name;
107     va_start(ap, stat);
108     while (1) {
109     const char *part = va_arg(ap, const char *);
110     int partlen;
111     if (part == NULL)
112     break;
113     partlen = strlen(part);
114     memcpy(p, part, partlen);
115     p += partlen;
116     *p++ = '.';
117     }
118     va_end(ap);
119     *--p = '\0';
120    
121     /* Replace spaces with underscores. */
122     for (p = name; *p != '\0'; p++) {
123     if (*p == ' ')
124     *p = '_';
125     }
126    
127     /* Stretch the stats array if necessary. */
128     if (num_stats >= alloc_stats) {
129     alloc_stats += INCREMENT_STATS;
130     stats = realloc(stats, alloc_stats * sizeof *stats);
131     if (stats == NULL)
132     die("out of memory");
133     }
134    
135     stats[num_stats].name = name;
136     stats[num_stats].type = type;
137     stats[num_stats].stat = stat;
138     ++num_stats;
139     }
140    
141     /* Compare two stats by name, for sorting purposes. */
142     int stats_compare(const void *a, const void *b) {
143     return strcmp(((stat *)a)->name, ((stat *)b)->name);
144     }
145    
146     /* Clear and rebuild the stats array. */
147     void get_stats(int use_diffs) {
148     cpu_states_t *cpu_s;
149     cpu_percent_t *cpu_p;
150     mem_stat_t *mem;
151     load_stat_t *load;
152     user_stat_t *user;
153     swap_stat_t *swap;
154     general_stat_t *gen;
155     disk_stat_t *disk;
156     diskio_stat_t *diskio;
157     process_stat_t *proc;
158     network_stat_t *net;
159     page_stat_t *page;
160     static int zero = 0;
161     int n, i;
162    
163     clear_stats();
164    
165     /* Constants, for use with MRTG mode. */
166     add_stat(INT, &zero, "const", "0", NULL);
167    
168     /* FIXME when only fetching some stats, it'd be more efficient to only
169     do the libstatgrab calls needed, rather than fetching everything. */
170    
171     if (use_cpu_percent) {
172     cpu_p = cpu_percent_usage();
173     if (cpu_p != NULL) {
174     add_stat(FLOAT, &cpu_p->user,
175     "cpu", "user", NULL);
176     add_stat(FLOAT, &cpu_p->kernel,
177     "cpu", "kernel", NULL);
178     add_stat(FLOAT, &cpu_p->idle,
179     "cpu", "idle", NULL);
180     add_stat(FLOAT, &cpu_p->iowait,
181     "cpu", "iowait", NULL);
182     add_stat(FLOAT, &cpu_p->swap,
183     "cpu", "swap", NULL);
184     add_stat(FLOAT, &cpu_p->nice,
185     "cpu", "nice", NULL);
186     add_stat(TIME_T, &cpu_s->systime,
187     "cpu", "time_taken", NULL);
188     }
189     } else {
190     cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
191     if (cpu_s != NULL) {
192     add_stat(LONG_LONG, &cpu_s->user,
193     "cpu", "user", NULL);
194     add_stat(LONG_LONG, &cpu_s->kernel,
195     "cpu", "kernel", NULL);
196     add_stat(LONG_LONG, &cpu_s->idle,
197     "cpu", "idle", NULL);
198     add_stat(LONG_LONG, &cpu_s->iowait,
199     "cpu", "iowait", NULL);
200     add_stat(LONG_LONG, &cpu_s->swap,
201     "cpu", "swap", NULL);
202     add_stat(LONG_LONG, &cpu_s->nice,
203     "cpu", "nice", NULL);
204     add_stat(LONG_LONG, &cpu_s->total,
205     "cpu", "total", NULL);
206     add_stat(TIME_T, &cpu_s->systime,
207     "cpu", "systime", NULL);
208     }
209     }
210    
211     mem = get_memory_stats();
212     if (mem != NULL) {
213     add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
214     add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
215     add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
216     add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
217     }
218    
219     load = get_load_stats();
220     if (load != NULL) {
221     add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
222     add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
223     add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
224     }
225    
226     user = get_user_stats();
227     if (user != NULL) {
228     add_stat(INT, &user->num_entries, "user", "num", NULL);
229     add_stat(STRING, &user->name_list, "user", "names", NULL);
230     }
231    
232     swap = get_swap_stats();
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    
239     gen = get_general_stats();
240     if (gen != NULL) {
241     add_stat(STRING, &gen->os_name,
242     "general", "os_name", NULL);
243     add_stat(STRING, &gen->os_release,
244     "general", "os_release", NULL);
245     add_stat(STRING, &gen->os_version,
246     "general", "os_version", NULL);
247     add_stat(STRING, &gen->platform, "general", "platform", NULL);
248     add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
249     add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
250     }
251    
252     disk = get_disk_stats(&n);
253     if (disk != NULL) {
254     for (i = 0; i < n; i++) {
255     /* FIXME it'd be nicer if libstatgrab did this */
256     const char *name = disk[i].device_name,
257     *p = strrchr(name, '/');
258     if (p != NULL)
259     name = p + 1;
260     if (*name == '\0')
261     name = "root";
262    
263     add_stat(STRING, &disk[i].device_name,
264     "fs", name, "device_name", NULL);
265     add_stat(STRING, &disk[i].fs_type,
266     "fs", name, "fs_type", NULL);
267     add_stat(STRING, &disk[i].mnt_point,
268     "fs", name, "mnt_point", NULL);
269     add_stat(LONG_LONG, &disk[i].size,
270     "fs", name, "size", NULL);
271     add_stat(LONG_LONG, &disk[i].used,
272     "fs", name, "used", NULL);
273     add_stat(LONG_LONG, &disk[i].avail,
274     "fs", name, "avail", NULL);
275     add_stat(LONG_LONG, &disk[i].total_inodes,
276     "fs", name, "total_inodes", NULL);
277     add_stat(LONG_LONG, &disk[i].used_inodes,
278     "fs", name, "used_inodes", NULL);
279     add_stat(LONG_LONG, &disk[i].free_inodes,
280     "fs", name, "free_inodes", NULL);
281     }
282     }
283    
284     diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
285     if (diskio != NULL) {
286     for (i = 0; i < n; i++) {
287     const char *name = diskio[i].disk_name;
288    
289     add_stat(STRING, &diskio[i].disk_name,
290     "disk", name, "disk_name", NULL);
291     add_stat(LONG_LONG, &diskio[i].read_bytes,
292     "disk", name, "read_bytes", NULL);
293     add_stat(LONG_LONG, &diskio[i].write_bytes,
294     "disk", name, "write_bytes", NULL);
295     add_stat(TIME_T, &diskio[i].systime,
296     "disk", name, "systime", NULL);
297     }
298     }
299    
300     proc = get_process_stats();
301     if (proc != NULL) {
302     add_stat(INT, &proc->total, "proc", "total", NULL);
303     add_stat(INT, &proc->running, "proc", "running", NULL);
304     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
305     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
306     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
307     }
308    
309     net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
310     if (net != NULL) {
311     for (i = 0; i < n; i++) {
312     const char *name = net[i].interface_name;
313    
314     add_stat(STRING, &net[i].interface_name,
315     "net", name, "interface_name", NULL);
316     add_stat(LONG_LONG, &net[i].tx,
317     "net", name, "tx", NULL);
318     add_stat(LONG_LONG, &net[i].rx,
319     "net", name, "rx", NULL);
320     add_stat(TIME_T, &net[i].systime,
321     "net", name, "systime", NULL);
322     }
323     }
324    
325     page = use_diffs ? get_page_stats_diff() : get_page_stats();
326     if (page != NULL) {
327     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
328     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
329     add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
330     }
331    
332     qsort(stats, num_stats, sizeof *stats, stats_compare);
333     }
334    
335     /* Print the value of a stat. */
336     void print_stat_value(const stat *s) {
337     void *v = s->stat;
338    
339     switch (s->type) {
340     case LONG_LONG:
341     printf("%lld", *(long long *)v);
342     break;
343     case TIME_T:
344     /* FIXME option for formatted time? */
345     printf("%ld", *(time_t *)v);
346     break;
347     case FLOAT:
348     printf("%f", *(float *)v);
349     break;
350     case DOUBLE:
351     printf("%f", *(double *)v);
352     break;
353     case STRING:
354     /* FIXME escaping? */
355     printf("%s", *(char **)v);
356     break;
357     case INT:
358     printf("%d", *(int *)v);
359     break;
360     }
361     }
362    
363     /* Print the name and value of a stat. */
364     void print_stat(const stat *s) {
365     switch (display_mode) {
366     case DISPLAY_LINUX:
367     printf("%s = ", s->name);
368     break;
369     case DISPLAY_BSD:
370     printf("%s: ", s->name);
371     break;
372     case DISPLAY_MRTG:
373     case DISPLAY_PLAIN:
374     break;
375     }
376     print_stat_value(s);
377     printf("\n");
378     }
379    
380     /* Print stats as specified on the provided command line. */
381     void print_stats(int argc, char **argv) {
382     int i;
383    
384     if (argc == optind) {
385     /* Print all stats. */
386     for (i = 0; i < num_stats; i++)
387     print_stat(&stats[i]);
388     } else {
389     /* Print selected stats. */
390     for (i = optind; i < argc; i++) {
391     stat key;
392     const stat *s;
393    
394     key.name = argv[i];
395     s = (const stat *)bsearch(&key, stats, num_stats,
396     sizeof *stats,
397     stats_compare);
398     if (s != NULL) {
399     print_stat(s);
400     }
401     }
402     }
403     }
404    
405     void usage() {
406     printf("Usage: statgrab [OPTION]... [STAT]...\n"
407     "Display system statistics (all statistics by default).\n"
408     "\n");
409     printf(" -l Linux sysctl-style output (default)\n"
410     " -b BSD sysctl-style output\n"
411     " -m MRTG-compatible output\n"
412     " -u Plain output (only show values)\n"
413     " -n Display cumulative stats once (default)\n"
414     " -s Display stat differences repeatedly\n"
415     " -o Display stat differences once\n"
416     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
417     " -p Display CPU usage as percentages rather than absolute values\n"
418     "\n");
419     printf("Report bugs to <bugs@i-scream.org>.\n");
420     exit(1);
421     }
422    
423     int main(int argc, char **argv) {
424     opterr = 0;
425     while (1) {
426     int c = getopt(argc, argv, "lbmunsot:p");
427     if (c == -1)
428     break;
429     switch (c) {
430     case 'l':
431     display_mode = DISPLAY_LINUX;
432     break;
433     case 'b':
434     display_mode = DISPLAY_BSD;
435     break;
436     case 'm':
437     display_mode = DISPLAY_MRTG;
438     break;
439     case 'u':
440     display_mode = DISPLAY_PLAIN;
441     break;
442     case 'n':
443     repeat_mode = REPEAT_NONE;
444     break;
445     case 's':
446     repeat_mode = REPEAT_FOREVER;
447     break;
448     case 'o':
449     repeat_mode = REPEAT_ONCE;
450     break;
451     case 't':
452     repeat_time = atoi(optarg);
453     break;
454     case 'p':
455     use_cpu_percent = 1;
456     break;
457     default:
458     usage();
459     }
460     }
461    
462     if (display_mode == DISPLAY_MRTG) {
463     if ((argc - optind) != 2)
464     die("mrtg mode: must specify exactly two stats");
465     if (repeat_mode != REPEAT_NONE)
466     die("mrtg mode: cannot repeat display");
467     }
468    
469     switch (repeat_mode) {
470     case REPEAT_NONE:
471     get_stats(0);
472     print_stats(argc, argv);
473     break;
474     case REPEAT_ONCE:
475     get_stats(1);
476     sleep(repeat_time);
477     get_stats(1);
478     print_stats(argc, argv);
479     break;
480     case REPEAT_FOREVER:
481     while (1) {
482     get_stats(1);
483     print_stats(argc, argv);
484     printf("\n");
485     sleep(repeat_time);
486     }
487     }
488    
489     if (display_mode == DISPLAY_MRTG) {
490     printf("\n");
491     printf("statgrab\n");
492     }
493    
494     return 0;
495     }
496