ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.13
Committed: Mon Nov 10 23:35:43 2003 UTC (20 years, 11 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.12: +2 -0 lines
Log Message:
Strip colons from Cygwin drive names.

File Contents

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