ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.22
Committed: Sat Mar 6 22:34:36 2004 UTC (20 years, 9 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_9
Changes since 1.21: +11 -1 lines
Log Message:
Chase new network interface stats.

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