ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.20
Committed: Sat Feb 14 00:06:00 2004 UTC (20 years, 3 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.19: +7 -1 lines
Log Message:
Add support for interface .up.

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.20 * $Id: statgrab.c,v 1.19 2004/02/13 15:13:37 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     add_stat(TIME_T, &net[i].systime,
367     "net", name, "systime", NULL);
368     }
369     }
370 ats 1.18
371     iface = get_network_iface_stats(&n);
372     if (iface != NULL) {
373     for (i = 0; i < n; i++) {
374     const char *name = iface[i].interface_name;
375    
376     add_stat(INT, &iface[i].speed,
377     "net", name, "speed", NULL);
378 ats 1.20 add_stat(BOOL, &iface[i].up,
379     "net", name, "up", NULL);
380 ats 1.18 add_stat(DUPLEX, &iface[i].dup,
381     "net", name, "duplex", NULL);
382     }
383     }
384 ats 1.8 }
385    
386     void populate_page() {
387     page_stat_t *page;
388 tdb 1.1
389     page = use_diffs ? get_page_stats_diff() : get_page_stats();
390     if (page != NULL) {
391     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
392     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
393 ats 1.11 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
394 tdb 1.1 }
395 ats 1.8 }
396    
397     typedef struct {
398     const char *name;
399     void (*populate)();
400     int interesting;
401     } toplevel;
402     toplevel toplevels[] = {
403     {"const.", populate_const, 0},
404     {"cpu.", populate_cpu, 0},
405     {"mem.", populate_mem, 0},
406     {"load.", populate_load, 0},
407     {"user.", populate_user, 0},
408     {"swap.", populate_swap, 0},
409     {"general.", populate_general, 0},
410     {"fs.", populate_fs, 0},
411     {"disk.", populate_disk, 0},
412     {"proc.", populate_proc, 0},
413     {"net.", populate_net, 0},
414     {"page.", populate_page, 0},
415     {NULL, NULL, 0}
416     };
417    
418     /* Set the "interesting" flag on the sections that we actually need to
419     fetch. */
420     void select_interesting(int argc, char **argv) {
421     toplevel *t;
422    
423     if (argc == 0) {
424     for (t = &toplevels[0]; t->name != NULL; t++)
425     t->interesting = 1;
426     } else {
427     int i;
428    
429     for (i = 0; i < argc; i++) {
430     for (t = &toplevels[0]; t->name != NULL; t++) {
431     if (strncmp(argv[i], t->name,
432     strlen(t->name)) == 0) {
433     t->interesting = 1;
434     break;
435     }
436     }
437     }
438     }
439     }
440    
441     /* Clear and rebuild the stats array. */
442     void get_stats() {
443     toplevel *t;
444    
445     clear_stats();
446    
447     for (t = &toplevels[0]; t->name != NULL; t++) {
448     if (t->interesting)
449     t->populate();
450     }
451 tdb 1.1
452     qsort(stats, num_stats, sizeof *stats, stats_compare);
453     }
454    
455     /* Print the value of a stat. */
456     void print_stat_value(const stat *s) {
457     void *v = s->stat;
458    
459     switch (s->type) {
460     case LONG_LONG:
461     printf("%lld", *(long long *)v);
462     break;
463     case TIME_T:
464     /* FIXME option for formatted time? */
465     printf("%ld", *(time_t *)v);
466     break;
467     case FLOAT:
468     printf("%f", *(float *)v);
469     break;
470     case DOUBLE:
471     printf("%f", *(double *)v);
472     break;
473     case STRING:
474     /* FIXME escaping? */
475     printf("%s", *(char **)v);
476     break;
477     case INT:
478     printf("%d", *(int *)v);
479 ats 1.20 break;
480     case BOOL:
481     printf("%s", *(int *)v ? "true" : "false");
482 ats 1.18 break;
483     case DUPLEX:
484 ats 1.19 switch (*(statgrab_duplex *) v) {
485 ats 1.18 case FULL_DUPLEX:
486     printf("full");
487     break;
488     case HALF_DUPLEX:
489     printf("half");
490     break;
491     default:
492     printf("unknown");
493     break;
494     }
495 tdb 1.1 break;
496     }
497     }
498    
499     /* Print the name and value of a stat. */
500     void print_stat(const stat *s) {
501     switch (display_mode) {
502     case DISPLAY_LINUX:
503     printf("%s = ", s->name);
504     break;
505     case DISPLAY_BSD:
506     printf("%s: ", s->name);
507     break;
508     case DISPLAY_MRTG:
509     case DISPLAY_PLAIN:
510     break;
511     }
512     print_stat_value(s);
513     printf("\n");
514     }
515    
516     /* Print stats as specified on the provided command line. */
517     void print_stats(int argc, char **argv) {
518     int i;
519    
520     if (argc == optind) {
521     /* Print all stats. */
522     for (i = 0; i < num_stats; i++)
523     print_stat(&stats[i]);
524     } else {
525     /* Print selected stats. */
526     for (i = optind; i < argc; i++) {
527 ats 1.9 char *name = argv[i];
528 tdb 1.1 stat key;
529 ats 1.9 const stat *s, *end;
530     int (*compare)(const void *, const void *);
531    
532     key.name = name;
533     if (name[strlen(name) - 1] == '.')
534     compare = stats_compare_prefix;
535     else
536     compare = stats_compare;
537 tdb 1.1
538     s = (const stat *)bsearch(&key, stats, num_stats,
539 ats 1.9 sizeof *stats, compare);
540     if (s == NULL) {
541     printf("Unknown stat %s\n", name);
542     continue;
543     }
544    
545     /* Find the range of stats the user wanted. */
546     for (; s >= stats; s--) {
547     if (compare(&key, s) != 0)
548     break;
549     }
550     s++;
551     for (end = s; end < &stats[num_stats]; end++) {
552     if (compare(&key, end) != 0)
553     break;
554     }
555    
556     /* And print them. */
557     for (; s < end; s++) {
558 tdb 1.1 print_stat(s);
559     }
560     }
561     }
562     }
563    
564     void usage() {
565     printf("Usage: statgrab [OPTION]... [STAT]...\n"
566 ats 1.9 "Display system statistics.\n"
567     "\n"
568     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
569     "statistics starting with that prefix.\n"
570 tdb 1.1 "\n");
571     printf(" -l Linux sysctl-style output (default)\n"
572     " -b BSD sysctl-style output\n"
573     " -m MRTG-compatible output\n"
574     " -u Plain output (only show values)\n"
575     " -n Display cumulative stats once (default)\n"
576     " -s Display stat differences repeatedly\n"
577     " -o Display stat differences once\n"
578     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
579 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
580     " absolute values\n"
581 tdb 1.1 "\n");
582 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
583 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
584 tdb 1.1 exit(1);
585     }
586    
587     int main(int argc, char **argv) {
588     opterr = 0;
589     while (1) {
590     int c = getopt(argc, argv, "lbmunsot:p");
591     if (c == -1)
592     break;
593     switch (c) {
594     case 'l':
595     display_mode = DISPLAY_LINUX;
596     break;
597     case 'b':
598     display_mode = DISPLAY_BSD;
599     break;
600     case 'm':
601     display_mode = DISPLAY_MRTG;
602     break;
603     case 'u':
604     display_mode = DISPLAY_PLAIN;
605     break;
606     case 'n':
607     repeat_mode = REPEAT_NONE;
608     break;
609     case 's':
610     repeat_mode = REPEAT_FOREVER;
611     break;
612     case 'o':
613     repeat_mode = REPEAT_ONCE;
614     break;
615     case 't':
616     repeat_time = atoi(optarg);
617     break;
618     case 'p':
619     use_cpu_percent = 1;
620     break;
621     default:
622     usage();
623     }
624     }
625    
626     if (display_mode == DISPLAY_MRTG) {
627     if ((argc - optind) != 2)
628     die("mrtg mode: must specify exactly two stats");
629 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
630 tdb 1.1 die("mrtg mode: cannot repeat display");
631     }
632 ats 1.5
633     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
634     die("CPU percentage usage display requires stat differences");
635 tdb 1.1
636 ats 1.8 if (repeat_mode == REPEAT_NONE)
637     use_diffs = 0;
638     else
639     use_diffs = 1;
640    
641     select_interesting(argc - optind, &argv[optind]);
642    
643 ats 1.12 /* We don't care if statgrab_init fails, because we can just display
644     the statistics that can be read as non-root. */
645     statgrab_init();
646 ats 1.15 if (statgrab_drop_privileges() != 0)
647     die("Failed to drop setuid/setgid privileges");
648 ats 1.12
649 tdb 1.1 switch (repeat_mode) {
650     case REPEAT_NONE:
651 ats 1.8 get_stats();
652 tdb 1.1 print_stats(argc, argv);
653     break;
654     case REPEAT_ONCE:
655 ats 1.8 get_stats();
656 tdb 1.1 sleep(repeat_time);
657 ats 1.8 get_stats();
658 tdb 1.1 print_stats(argc, argv);
659     break;
660     case REPEAT_FOREVER:
661     while (1) {
662 ats 1.8 get_stats();
663 tdb 1.1 print_stats(argc, argv);
664     printf("\n");
665     sleep(repeat_time);
666     }
667     }
668    
669     if (display_mode == DISPLAY_MRTG) {
670     printf("\n");
671     printf("statgrab\n");
672     }
673    
674     return 0;
675     }
676