ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.19
Committed: Fri Feb 13 15:13:37 2004 UTC (20 years, 8 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.18: +2 -2 lines
Log Message:
Track rename of duplex to statgrab_duplex.

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