ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.17
Committed: Mon Jan 19 16:49:23 2004 UTC (20 years, 4 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_8_2, LIBSTATGRAB_0_8_1
Changes since 1.16: +2 -0 lines
Log Message:
A whole bunch of minor cosmetic changes.

File Contents

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