ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.23
Committed: Sun Apr 4 21:59:16 2004 UTC (20 years, 8 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.22: +4 -2 lines
Log Message:
Fix TIME_T printing on systems where time_t isn't a long.

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.23 * $Id: statgrab.c,v 1.22 2004/03/06 22:34:36 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 ats 1.23 long l;
470 tdb 1.1
471     switch (s->type) {
472     case LONG_LONG:
473     printf("%lld", *(long long *)v);
474     break;
475     case TIME_T:
476     /* FIXME option for formatted time? */
477 ats 1.23 l = *(time_t *)v;
478     printf("%ld", l);
479 tdb 1.1 break;
480     case FLOAT:
481     printf("%f", *(float *)v);
482     break;
483     case DOUBLE:
484     printf("%f", *(double *)v);
485     break;
486     case STRING:
487     /* FIXME escaping? */
488     printf("%s", *(char **)v);
489     break;
490     case INT:
491     printf("%d", *(int *)v);
492 ats 1.20 break;
493     case BOOL:
494     printf("%s", *(int *)v ? "true" : "false");
495 ats 1.18 break;
496     case DUPLEX:
497 ats 1.19 switch (*(statgrab_duplex *) v) {
498 ats 1.18 case FULL_DUPLEX:
499     printf("full");
500     break;
501     case HALF_DUPLEX:
502     printf("half");
503     break;
504     default:
505     printf("unknown");
506     break;
507     }
508 tdb 1.1 break;
509     }
510     }
511    
512     /* Print the name and value of a stat. */
513     void print_stat(const stat *s) {
514     switch (display_mode) {
515     case DISPLAY_LINUX:
516     printf("%s = ", s->name);
517     break;
518     case DISPLAY_BSD:
519     printf("%s: ", s->name);
520     break;
521     case DISPLAY_MRTG:
522     case DISPLAY_PLAIN:
523     break;
524     }
525     print_stat_value(s);
526     printf("\n");
527     }
528    
529     /* Print stats as specified on the provided command line. */
530     void print_stats(int argc, char **argv) {
531     int i;
532    
533     if (argc == optind) {
534     /* Print all stats. */
535     for (i = 0; i < num_stats; i++)
536     print_stat(&stats[i]);
537     } else {
538     /* Print selected stats. */
539     for (i = optind; i < argc; i++) {
540 ats 1.9 char *name = argv[i];
541 tdb 1.1 stat key;
542 ats 1.9 const stat *s, *end;
543     int (*compare)(const void *, const void *);
544    
545     key.name = name;
546     if (name[strlen(name) - 1] == '.')
547     compare = stats_compare_prefix;
548     else
549     compare = stats_compare;
550 tdb 1.1
551 ats 1.21 if (stats == NULL) {
552     s = NULL;
553     } else {
554     s = (const stat *)bsearch(&key, stats,
555     num_stats,
556     sizeof *stats,
557     compare);
558     }
559    
560 ats 1.9 if (s == NULL) {
561     printf("Unknown stat %s\n", name);
562     continue;
563     }
564    
565     /* Find the range of stats the user wanted. */
566     for (; s >= stats; s--) {
567     if (compare(&key, s) != 0)
568     break;
569     }
570     s++;
571     for (end = s; end < &stats[num_stats]; end++) {
572     if (compare(&key, end) != 0)
573     break;
574     }
575    
576     /* And print them. */
577     for (; s < end; s++) {
578 tdb 1.1 print_stat(s);
579     }
580     }
581     }
582     }
583    
584     void usage() {
585     printf("Usage: statgrab [OPTION]... [STAT]...\n"
586 ats 1.9 "Display system statistics.\n"
587     "\n"
588     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
589     "statistics starting with that prefix.\n"
590 tdb 1.1 "\n");
591     printf(" -l Linux sysctl-style output (default)\n"
592     " -b BSD sysctl-style output\n"
593     " -m MRTG-compatible output\n"
594     " -u Plain output (only show values)\n"
595     " -n Display cumulative stats once (default)\n"
596     " -s Display stat differences repeatedly\n"
597     " -o Display stat differences once\n"
598     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
599 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
600     " absolute values\n"
601 tdb 1.1 "\n");
602 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
603 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
604 tdb 1.1 exit(1);
605     }
606    
607     int main(int argc, char **argv) {
608     opterr = 0;
609     while (1) {
610     int c = getopt(argc, argv, "lbmunsot:p");
611     if (c == -1)
612     break;
613     switch (c) {
614     case 'l':
615     display_mode = DISPLAY_LINUX;
616     break;
617     case 'b':
618     display_mode = DISPLAY_BSD;
619     break;
620     case 'm':
621     display_mode = DISPLAY_MRTG;
622     break;
623     case 'u':
624     display_mode = DISPLAY_PLAIN;
625     break;
626     case 'n':
627     repeat_mode = REPEAT_NONE;
628     break;
629     case 's':
630     repeat_mode = REPEAT_FOREVER;
631     break;
632     case 'o':
633     repeat_mode = REPEAT_ONCE;
634     break;
635     case 't':
636     repeat_time = atoi(optarg);
637     break;
638     case 'p':
639     use_cpu_percent = 1;
640     break;
641     default:
642     usage();
643     }
644     }
645    
646     if (display_mode == DISPLAY_MRTG) {
647     if ((argc - optind) != 2)
648     die("mrtg mode: must specify exactly two stats");
649 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
650 tdb 1.1 die("mrtg mode: cannot repeat display");
651     }
652 ats 1.5
653     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
654     die("CPU percentage usage display requires stat differences");
655 tdb 1.1
656 ats 1.8 if (repeat_mode == REPEAT_NONE)
657     use_diffs = 0;
658     else
659     use_diffs = 1;
660    
661     select_interesting(argc - optind, &argv[optind]);
662    
663 ats 1.12 /* We don't care if statgrab_init fails, because we can just display
664     the statistics that can be read as non-root. */
665     statgrab_init();
666 ats 1.15 if (statgrab_drop_privileges() != 0)
667     die("Failed to drop setuid/setgid privileges");
668 ats 1.12
669 tdb 1.1 switch (repeat_mode) {
670     case REPEAT_NONE:
671 ats 1.8 get_stats();
672 tdb 1.1 print_stats(argc, argv);
673     break;
674     case REPEAT_ONCE:
675 ats 1.8 get_stats();
676 tdb 1.1 sleep(repeat_time);
677 ats 1.8 get_stats();
678 tdb 1.1 print_stats(argc, argv);
679     break;
680     case REPEAT_FOREVER:
681     while (1) {
682 ats 1.8 get_stats();
683 tdb 1.1 print_stats(argc, argv);
684     printf("\n");
685     sleep(repeat_time);
686     }
687     }
688    
689     if (display_mode == DISPLAY_MRTG) {
690     printf("\n");
691     printf("statgrab\n");
692     }
693    
694     return 0;
695     }
696