ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.6
Committed: Fri Aug 29 06:48:04 2003 UTC (21 years, 4 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.5: +1 -1 lines
Log Message:
Fill in time_taken correctly.

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    
70     /* Exit with an error message. */
71     void die(const char *s) {
72     fprintf(stderr, "fatal: %s\n", s);
73     exit(1);
74     }
75    
76     /* Remove all the recorded stats. */
77     void clear_stats() {
78     int i;
79    
80     for (i = 0; i < num_stats; i++)
81     free(stats[i].name);
82     num_stats = 0;
83     }
84    
85     /* Add a stat. The varargs make up the name, joined with dots; the name is
86     terminated with a NULL. */
87     void add_stat(stat_type type, void *stat, ...) {
88     va_list ap;
89     int len = 0;
90     char *name, *p;
91    
92     /* Figure out how long the name will be, including dots and trailing
93     \0. */
94     va_start(ap, stat);
95     while (1) {
96     const char *part = va_arg(ap, const char *);
97     if (part == NULL)
98     break;
99     len += 1 + strlen(part);
100     }
101     va_end(ap);
102    
103     /* Paste the name together. */
104     name = malloc(len);
105     if (name == NULL)
106     die("out of memory");
107     p = name;
108     va_start(ap, stat);
109     while (1) {
110     const char *part = va_arg(ap, const char *);
111     int partlen;
112     if (part == NULL)
113     break;
114     partlen = strlen(part);
115     memcpy(p, part, partlen);
116     p += partlen;
117     *p++ = '.';
118     }
119     va_end(ap);
120     *--p = '\0';
121    
122     /* Replace spaces with underscores. */
123     for (p = name; *p != '\0'; p++) {
124     if (*p == ' ')
125     *p = '_';
126     }
127    
128     /* Stretch the stats array if necessary. */
129     if (num_stats >= alloc_stats) {
130     alloc_stats += INCREMENT_STATS;
131     stats = realloc(stats, alloc_stats * sizeof *stats);
132     if (stats == NULL)
133     die("out of memory");
134     }
135    
136     stats[num_stats].name = name;
137     stats[num_stats].type = type;
138     stats[num_stats].stat = stat;
139     ++num_stats;
140     }
141    
142     /* Compare two stats by name, for sorting purposes. */
143     int stats_compare(const void *a, const void *b) {
144     return strcmp(((stat *)a)->name, ((stat *)b)->name);
145     }
146    
147     /* Clear and rebuild the stats array. */
148     void get_stats(int use_diffs) {
149     cpu_states_t *cpu_s;
150     cpu_percent_t *cpu_p;
151     mem_stat_t *mem;
152     load_stat_t *load;
153     user_stat_t *user;
154     swap_stat_t *swap;
155     general_stat_t *gen;
156     disk_stat_t *disk;
157     diskio_stat_t *diskio;
158     process_stat_t *proc;
159     network_stat_t *net;
160     page_stat_t *page;
161     static int zero = 0;
162     int n, i;
163    
164     clear_stats();
165    
166     /* Constants, for use with MRTG mode. */
167     add_stat(INT, &zero, "const", "0", NULL);
168    
169     /* FIXME when only fetching some stats, it'd be more efficient to only
170     do the libstatgrab calls needed, rather than fetching everything. */
171    
172     if (use_cpu_percent) {
173     cpu_p = cpu_percent_usage();
174     if (cpu_p != NULL) {
175     add_stat(FLOAT, &cpu_p->user,
176     "cpu", "user", NULL);
177     add_stat(FLOAT, &cpu_p->kernel,
178     "cpu", "kernel", NULL);
179     add_stat(FLOAT, &cpu_p->idle,
180     "cpu", "idle", NULL);
181     add_stat(FLOAT, &cpu_p->iowait,
182     "cpu", "iowait", NULL);
183     add_stat(FLOAT, &cpu_p->swap,
184     "cpu", "swap", NULL);
185     add_stat(FLOAT, &cpu_p->nice,
186     "cpu", "nice", NULL);
187 ats 1.6 add_stat(TIME_T, &cpu_p->systime,
188 tdb 1.1 "cpu", "time_taken", NULL);
189     }
190     } else {
191     cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
192     if (cpu_s != NULL) {
193     add_stat(LONG_LONG, &cpu_s->user,
194     "cpu", "user", NULL);
195     add_stat(LONG_LONG, &cpu_s->kernel,
196     "cpu", "kernel", NULL);
197     add_stat(LONG_LONG, &cpu_s->idle,
198     "cpu", "idle", NULL);
199     add_stat(LONG_LONG, &cpu_s->iowait,
200     "cpu", "iowait", NULL);
201     add_stat(LONG_LONG, &cpu_s->swap,
202     "cpu", "swap", NULL);
203     add_stat(LONG_LONG, &cpu_s->nice,
204     "cpu", "nice", NULL);
205     add_stat(LONG_LONG, &cpu_s->total,
206     "cpu", "total", NULL);
207     add_stat(TIME_T, &cpu_s->systime,
208     "cpu", "systime", NULL);
209     }
210     }
211    
212     mem = get_memory_stats();
213     if (mem != NULL) {
214     add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
215     add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
216     add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
217     add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
218     }
219    
220     load = get_load_stats();
221     if (load != NULL) {
222     add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
223     add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
224     add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
225     }
226    
227     user = get_user_stats();
228     if (user != NULL) {
229     add_stat(INT, &user->num_entries, "user", "num", NULL);
230     add_stat(STRING, &user->name_list, "user", "names", NULL);
231     }
232    
233     swap = get_swap_stats();
234     if (swap != NULL) {
235     add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
236     add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
237     add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
238     }
239    
240     gen = get_general_stats();
241     if (gen != NULL) {
242     add_stat(STRING, &gen->os_name,
243     "general", "os_name", NULL);
244     add_stat(STRING, &gen->os_release,
245     "general", "os_release", NULL);
246     add_stat(STRING, &gen->os_version,
247     "general", "os_version", NULL);
248     add_stat(STRING, &gen->platform, "general", "platform", NULL);
249     add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
250     add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
251     }
252    
253     disk = get_disk_stats(&n);
254     if (disk != NULL) {
255     for (i = 0; i < n; i++) {
256     /* FIXME it'd be nicer if libstatgrab did this */
257     const char *name = disk[i].device_name,
258     *p = strrchr(name, '/');
259     if (p != NULL)
260     name = p + 1;
261     if (*name == '\0')
262     name = "root";
263    
264     add_stat(STRING, &disk[i].device_name,
265     "fs", name, "device_name", NULL);
266     add_stat(STRING, &disk[i].fs_type,
267     "fs", name, "fs_type", NULL);
268     add_stat(STRING, &disk[i].mnt_point,
269     "fs", name, "mnt_point", NULL);
270     add_stat(LONG_LONG, &disk[i].size,
271     "fs", name, "size", NULL);
272     add_stat(LONG_LONG, &disk[i].used,
273     "fs", name, "used", NULL);
274     add_stat(LONG_LONG, &disk[i].avail,
275     "fs", name, "avail", NULL);
276     add_stat(LONG_LONG, &disk[i].total_inodes,
277     "fs", name, "total_inodes", NULL);
278     add_stat(LONG_LONG, &disk[i].used_inodes,
279     "fs", name, "used_inodes", NULL);
280     add_stat(LONG_LONG, &disk[i].free_inodes,
281     "fs", name, "free_inodes", NULL);
282     }
283     }
284    
285     diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
286     if (diskio != NULL) {
287     for (i = 0; i < n; i++) {
288     const char *name = diskio[i].disk_name;
289    
290     add_stat(STRING, &diskio[i].disk_name,
291     "disk", name, "disk_name", NULL);
292     add_stat(LONG_LONG, &diskio[i].read_bytes,
293     "disk", name, "read_bytes", NULL);
294     add_stat(LONG_LONG, &diskio[i].write_bytes,
295     "disk", name, "write_bytes", NULL);
296     add_stat(TIME_T, &diskio[i].systime,
297     "disk", name, "systime", NULL);
298     }
299     }
300    
301     proc = get_process_stats();
302     if (proc != NULL) {
303     add_stat(INT, &proc->total, "proc", "total", NULL);
304     add_stat(INT, &proc->running, "proc", "running", NULL);
305     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
306     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
307     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
308     }
309    
310     net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
311     if (net != NULL) {
312     for (i = 0; i < n; i++) {
313     const char *name = net[i].interface_name;
314    
315     add_stat(STRING, &net[i].interface_name,
316     "net", name, "interface_name", NULL);
317     add_stat(LONG_LONG, &net[i].tx,
318     "net", name, "tx", NULL);
319     add_stat(LONG_LONG, &net[i].rx,
320     "net", name, "rx", NULL);
321     add_stat(TIME_T, &net[i].systime,
322     "net", name, "systime", NULL);
323     }
324     }
325    
326     page = use_diffs ? get_page_stats_diff() : get_page_stats();
327     if (page != NULL) {
328     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
329     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
330     add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
331     }
332    
333     qsort(stats, num_stats, sizeof *stats, stats_compare);
334     }
335    
336     /* Print the value of a stat. */
337     void print_stat_value(const stat *s) {
338     void *v = s->stat;
339    
340     switch (s->type) {
341     case LONG_LONG:
342     printf("%lld", *(long long *)v);
343     break;
344     case TIME_T:
345     /* FIXME option for formatted time? */
346     printf("%ld", *(time_t *)v);
347     break;
348     case FLOAT:
349     printf("%f", *(float *)v);
350     break;
351     case DOUBLE:
352     printf("%f", *(double *)v);
353     break;
354     case STRING:
355     /* FIXME escaping? */
356     printf("%s", *(char **)v);
357     break;
358     case INT:
359     printf("%d", *(int *)v);
360     break;
361     }
362     }
363    
364     /* Print the name and value of a stat. */
365     void print_stat(const stat *s) {
366     switch (display_mode) {
367     case DISPLAY_LINUX:
368     printf("%s = ", s->name);
369     break;
370     case DISPLAY_BSD:
371     printf("%s: ", s->name);
372     break;
373     case DISPLAY_MRTG:
374     case DISPLAY_PLAIN:
375     break;
376     }
377     print_stat_value(s);
378     printf("\n");
379     }
380    
381     /* Print stats as specified on the provided command line. */
382     void print_stats(int argc, char **argv) {
383     int i;
384    
385     if (argc == optind) {
386     /* Print all stats. */
387     for (i = 0; i < num_stats; i++)
388     print_stat(&stats[i]);
389     } else {
390     /* Print selected stats. */
391     for (i = optind; i < argc; i++) {
392     stat key;
393     const stat *s;
394    
395     key.name = argv[i];
396     s = (const stat *)bsearch(&key, stats, num_stats,
397     sizeof *stats,
398     stats_compare);
399     if (s != NULL) {
400     print_stat(s);
401     }
402     }
403     }
404     }
405    
406     void usage() {
407     printf("Usage: statgrab [OPTION]... [STAT]...\n"
408     "Display system statistics (all statistics by default).\n"
409     "\n");
410     printf(" -l Linux sysctl-style output (default)\n"
411     " -b BSD sysctl-style output\n"
412     " -m MRTG-compatible output\n"
413     " -u Plain output (only show values)\n"
414     " -n Display cumulative stats once (default)\n"
415     " -s Display stat differences repeatedly\n"
416     " -o Display stat differences once\n"
417     " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
418 ats 1.5 " -p Display CPU usage differences as percentages rather than\n"
419     " absolute values\n"
420 tdb 1.1 "\n");
421 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
422 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
423 tdb 1.1 exit(1);
424     }
425    
426     int main(int argc, char **argv) {
427     opterr = 0;
428     while (1) {
429     int c = getopt(argc, argv, "lbmunsot:p");
430     if (c == -1)
431     break;
432     switch (c) {
433     case 'l':
434     display_mode = DISPLAY_LINUX;
435     break;
436     case 'b':
437     display_mode = DISPLAY_BSD;
438     break;
439     case 'm':
440     display_mode = DISPLAY_MRTG;
441     break;
442     case 'u':
443     display_mode = DISPLAY_PLAIN;
444     break;
445     case 'n':
446     repeat_mode = REPEAT_NONE;
447     break;
448     case 's':
449     repeat_mode = REPEAT_FOREVER;
450     break;
451     case 'o':
452     repeat_mode = REPEAT_ONCE;
453     break;
454     case 't':
455     repeat_time = atoi(optarg);
456     break;
457     case 'p':
458     use_cpu_percent = 1;
459     break;
460     default:
461     usage();
462     }
463     }
464    
465     if (display_mode == DISPLAY_MRTG) {
466     if ((argc - optind) != 2)
467     die("mrtg mode: must specify exactly two stats");
468 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
469 tdb 1.1 die("mrtg mode: cannot repeat display");
470     }
471 ats 1.5
472     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
473     die("CPU percentage usage display requires stat differences");
474 tdb 1.1
475     switch (repeat_mode) {
476     case REPEAT_NONE:
477     get_stats(0);
478     print_stats(argc, argv);
479     break;
480     case REPEAT_ONCE:
481     get_stats(1);
482     sleep(repeat_time);
483     get_stats(1);
484     print_stats(argc, argv);
485     break;
486     case REPEAT_FOREVER:
487     while (1) {
488     get_stats(1);
489     print_stats(argc, argv);
490     printf("\n");
491     sleep(repeat_time);
492     }
493     }
494    
495     if (display_mode == DISPLAY_MRTG) {
496     printf("\n");
497     printf("statgrab\n");
498     }
499    
500     return 0;
501     }
502