ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.9
Committed: Sun Aug 31 13:23:19 2003 UTC (21 years, 2 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_6, LIBSTATGRAB_0_5_1
Changes since 1.8: +41 -7 lines
Log Message:
If invoked as "statgrab foo.", print all stats starting with "foo.".
Print multiple values with the same name correctly.

File Contents

# Content
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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #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 int use_diffs = 0;
70
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 /* Compare two stats by name for qsort and bsearch. */
144 int stats_compare(const void *a, const void *b) {
145 return strcmp(((stat *)a)->name, ((stat *)b)->name);
146 }
147
148 /* 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 void populate_const() {
157 static int zero = 0;
158
159 /* Constants, for use with MRTG mode. */
160 add_stat(INT, &zero, "const", "0", NULL);
161 }
162
163 void populate_cpu() {
164 if (use_cpu_percent) {
165 cpu_percent_t *cpu_p = cpu_percent_usage();
166
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 add_stat(TIME_T, &cpu_p->time_taken,
181 "cpu", "time_taken", NULL);
182 }
183 } else {
184 cpu_states_t *cpu_s;
185
186 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 }
207
208 void populate_mem() {
209 mem_stat_t *mem = get_memory_stats();
210
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 }
218
219 void populate_load() {
220 load_stat_t *load = get_load_stats();
221
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 }
228
229 void populate_user() {
230 user_stat_t *user = get_user_stats();
231
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 }
237
238 void populate_swap() {
239 swap_stat_t *swap = get_swap_stats();
240
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 }
247
248 void populate_general() {
249 general_stat_t *gen = get_general_stats();
250
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 }
263
264 void populate_fs() {
265 int n, i;
266 disk_stat_t *disk = get_disk_stats(&n);
267
268 if (disk != NULL) {
269 for (i = 0; i < n; i++) {
270 /* FIXME it'd be nicer if libstatgrab did this */
271 const char *name = disk[i].device_name,
272 *p = strrchr(name, '/');
273 if (p != NULL)
274 name = p + 1;
275 if (*name == '\0')
276 name = "root";
277
278 add_stat(STRING, &disk[i].device_name,
279 "fs", name, "device_name", NULL);
280 add_stat(STRING, &disk[i].fs_type,
281 "fs", name, "fs_type", NULL);
282 add_stat(STRING, &disk[i].mnt_point,
283 "fs", name, "mnt_point", NULL);
284 add_stat(LONG_LONG, &disk[i].size,
285 "fs", name, "size", NULL);
286 add_stat(LONG_LONG, &disk[i].used,
287 "fs", name, "used", NULL);
288 add_stat(LONG_LONG, &disk[i].avail,
289 "fs", name, "avail", NULL);
290 add_stat(LONG_LONG, &disk[i].total_inodes,
291 "fs", name, "total_inodes", NULL);
292 add_stat(LONG_LONG, &disk[i].used_inodes,
293 "fs", name, "used_inodes", NULL);
294 add_stat(LONG_LONG, &disk[i].free_inodes,
295 "fs", name, "free_inodes", NULL);
296 }
297 }
298 }
299
300 void populate_disk() {
301 int n, i;
302 diskio_stat_t *diskio;
303
304 diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
305 if (diskio != NULL) {
306 for (i = 0; i < n; i++) {
307 const char *name = diskio[i].disk_name;
308
309 add_stat(STRING, &diskio[i].disk_name,
310 "disk", name, "disk_name", NULL);
311 add_stat(LONG_LONG, &diskio[i].read_bytes,
312 "disk", name, "read_bytes", NULL);
313 add_stat(LONG_LONG, &diskio[i].write_bytes,
314 "disk", name, "write_bytes", NULL);
315 add_stat(TIME_T, &diskio[i].systime,
316 "disk", name, "systime", NULL);
317 }
318 }
319 }
320
321 void populate_proc() {
322 process_stat_t *proc = get_process_stats();
323
324 if (proc != NULL) {
325 add_stat(INT, &proc->total, "proc", "total", NULL);
326 add_stat(INT, &proc->running, "proc", "running", NULL);
327 add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
328 add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
329 add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
330 }
331 }
332
333 void populate_net() {
334 int n, i;
335 network_stat_t *net;
336
337 net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
338 if (net != NULL) {
339 for (i = 0; i < n; i++) {
340 const char *name = net[i].interface_name;
341
342 add_stat(STRING, &net[i].interface_name,
343 "net", name, "interface_name", NULL);
344 add_stat(LONG_LONG, &net[i].tx,
345 "net", name, "tx", NULL);
346 add_stat(LONG_LONG, &net[i].rx,
347 "net", name, "rx", NULL);
348 add_stat(TIME_T, &net[i].systime,
349 "net", name, "systime", NULL);
350 }
351 }
352 }
353
354 void populate_page() {
355 page_stat_t *page;
356
357 page = use_diffs ? get_page_stats_diff() : get_page_stats();
358 if (page != NULL) {
359 add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
360 add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
361 add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
362 }
363 }
364
365 typedef struct {
366 const char *name;
367 void (*populate)();
368 int interesting;
369 } toplevel;
370 toplevel toplevels[] = {
371 {"const.", populate_const, 0},
372 {"cpu.", populate_cpu, 0},
373 {"mem.", populate_mem, 0},
374 {"load.", populate_load, 0},
375 {"user.", populate_user, 0},
376 {"swap.", populate_swap, 0},
377 {"general.", populate_general, 0},
378 {"fs.", populate_fs, 0},
379 {"disk.", populate_disk, 0},
380 {"proc.", populate_proc, 0},
381 {"net.", populate_net, 0},
382 {"page.", populate_page, 0},
383 {NULL, NULL, 0}
384 };
385
386 /* Set the "interesting" flag on the sections that we actually need to
387 fetch. */
388 void select_interesting(int argc, char **argv) {
389 toplevel *t;
390
391 if (argc == 0) {
392 for (t = &toplevels[0]; t->name != NULL; t++)
393 t->interesting = 1;
394 } else {
395 int i;
396
397 for (i = 0; i < argc; i++) {
398 for (t = &toplevels[0]; t->name != NULL; t++) {
399 if (strncmp(argv[i], t->name,
400 strlen(t->name)) == 0) {
401 t->interesting = 1;
402 break;
403 }
404 }
405 }
406 }
407 }
408
409 /* Clear and rebuild the stats array. */
410 void get_stats() {
411 toplevel *t;
412
413 clear_stats();
414
415 for (t = &toplevels[0]; t->name != NULL; t++) {
416 if (t->interesting)
417 t->populate();
418 }
419
420 qsort(stats, num_stats, sizeof *stats, stats_compare);
421 }
422
423 /* Print the value of a stat. */
424 void print_stat_value(const stat *s) {
425 void *v = s->stat;
426
427 switch (s->type) {
428 case LONG_LONG:
429 printf("%lld", *(long long *)v);
430 break;
431 case TIME_T:
432 /* FIXME option for formatted time? */
433 printf("%ld", *(time_t *)v);
434 break;
435 case FLOAT:
436 printf("%f", *(float *)v);
437 break;
438 case DOUBLE:
439 printf("%f", *(double *)v);
440 break;
441 case STRING:
442 /* FIXME escaping? */
443 printf("%s", *(char **)v);
444 break;
445 case INT:
446 printf("%d", *(int *)v);
447 break;
448 }
449 }
450
451 /* Print the name and value of a stat. */
452 void print_stat(const stat *s) {
453 switch (display_mode) {
454 case DISPLAY_LINUX:
455 printf("%s = ", s->name);
456 break;
457 case DISPLAY_BSD:
458 printf("%s: ", s->name);
459 break;
460 case DISPLAY_MRTG:
461 case DISPLAY_PLAIN:
462 break;
463 }
464 print_stat_value(s);
465 printf("\n");
466 }
467
468 /* Print stats as specified on the provided command line. */
469 void print_stats(int argc, char **argv) {
470 int i;
471
472 if (argc == optind) {
473 /* Print all stats. */
474 for (i = 0; i < num_stats; i++)
475 print_stat(&stats[i]);
476 } else {
477 /* Print selected stats. */
478 for (i = optind; i < argc; i++) {
479 char *name = argv[i];
480 stat key;
481 const stat *s, *end;
482 int (*compare)(const void *, const void *);
483
484 key.name = name;
485 if (name[strlen(name) - 1] == '.')
486 compare = stats_compare_prefix;
487 else
488 compare = stats_compare;
489
490 s = (const stat *)bsearch(&key, stats, num_stats,
491 sizeof *stats, compare);
492 if (s == NULL) {
493 printf("Unknown stat %s\n", name);
494 continue;
495 }
496
497 /* Find the range of stats the user wanted. */
498 for (; s >= stats; s--) {
499 if (compare(&key, s) != 0)
500 break;
501 }
502 s++;
503 for (end = s; end < &stats[num_stats]; end++) {
504 if (compare(&key, end) != 0)
505 break;
506 }
507
508 /* And print them. */
509 for (; s < end; s++) {
510 print_stat(s);
511 }
512 }
513 }
514 }
515
516 void usage() {
517 printf("Usage: statgrab [OPTION]... [STAT]...\n"
518 "Display system statistics.\n"
519 "\n"
520 "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
521 "statistics starting with that prefix.\n"
522 "\n");
523 printf(" -l Linux sysctl-style output (default)\n"
524 " -b BSD sysctl-style output\n"
525 " -m MRTG-compatible output\n"
526 " -u Plain output (only show values)\n"
527 " -n Display cumulative stats once (default)\n"
528 " -s Display stat differences repeatedly\n"
529 " -o Display stat differences once\n"
530 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
531 " -p Display CPU usage differences as percentages rather than\n"
532 " absolute values\n"
533 "\n");
534 printf("Version %s - report bugs to <%s>.\n",
535 PACKAGE_VERSION, PACKAGE_BUGREPORT);
536 exit(1);
537 }
538
539 int main(int argc, char **argv) {
540 opterr = 0;
541 while (1) {
542 int c = getopt(argc, argv, "lbmunsot:p");
543 if (c == -1)
544 break;
545 switch (c) {
546 case 'l':
547 display_mode = DISPLAY_LINUX;
548 break;
549 case 'b':
550 display_mode = DISPLAY_BSD;
551 break;
552 case 'm':
553 display_mode = DISPLAY_MRTG;
554 break;
555 case 'u':
556 display_mode = DISPLAY_PLAIN;
557 break;
558 case 'n':
559 repeat_mode = REPEAT_NONE;
560 break;
561 case 's':
562 repeat_mode = REPEAT_FOREVER;
563 break;
564 case 'o':
565 repeat_mode = REPEAT_ONCE;
566 break;
567 case 't':
568 repeat_time = atoi(optarg);
569 break;
570 case 'p':
571 use_cpu_percent = 1;
572 break;
573 default:
574 usage();
575 }
576 }
577
578 if (display_mode == DISPLAY_MRTG) {
579 if ((argc - optind) != 2)
580 die("mrtg mode: must specify exactly two stats");
581 if (repeat_mode == REPEAT_FOREVER)
582 die("mrtg mode: cannot repeat display");
583 }
584
585 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
586 die("CPU percentage usage display requires stat differences");
587
588 if (repeat_mode == REPEAT_NONE)
589 use_diffs = 0;
590 else
591 use_diffs = 1;
592
593 select_interesting(argc - optind, &argv[optind]);
594
595 switch (repeat_mode) {
596 case REPEAT_NONE:
597 get_stats();
598 print_stats(argc, argv);
599 break;
600 case REPEAT_ONCE:
601 get_stats();
602 sleep(repeat_time);
603 get_stats();
604 print_stats(argc, argv);
605 break;
606 case REPEAT_FOREVER:
607 while (1) {
608 get_stats();
609 print_stats(argc, argv);
610 printf("\n");
611 sleep(repeat_time);
612 }
613 }
614
615 if (display_mode == DISPLAY_MRTG) {
616 printf("\n");
617 printf("statgrab\n");
618 }
619
620 return 0;
621 }
622