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, 1 month 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

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 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 * $Id: statgrab.c,v 1.22 2004/03/06 22:34:36 ats Exp $
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #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 INT,
41 BOOL,
42 DUPLEX
43 } 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 int use_diffs = 0;
74
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 /* Compare two stats by name for qsort and bsearch. */
148 int stats_compare(const void *a, const void *b) {
149 return strcmp(((stat *)a)->name, ((stat *)b)->name);
150 }
151
152 /* 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 void populate_const() {
161 static int zero = 0;
162
163 /* Constants, for use with MRTG mode. */
164 add_stat(INT, &zero, "const", "0", NULL);
165 }
166
167 void populate_cpu() {
168 if (use_cpu_percent) {
169 cpu_percent_t *cpu_p = cpu_percent_usage();
170
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 add_stat(TIME_T, &cpu_p->time_taken,
185 "cpu", "time_taken", NULL);
186 }
187 } else {
188 cpu_states_t *cpu_s;
189
190 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 }
211
212 void populate_mem() {
213 mem_stat_t *mem = get_memory_stats();
214
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 }
222
223 void populate_load() {
224 load_stat_t *load = get_load_stats();
225
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 }
232
233 void populate_user() {
234 user_stat_t *user = get_user_stats();
235
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 }
241
242 void populate_swap() {
243 swap_stat_t *swap = get_swap_stats();
244
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 }
251
252 void populate_general() {
253 general_stat_t *gen = get_general_stats();
254
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 }
267
268 void populate_fs() {
269 int n, i;
270 disk_stat_t *disk = get_disk_stats(&n);
271
272 if (disk != NULL) {
273 for (i = 0; i < n; i++) {
274 /* FIXME it'd be nicer if libstatgrab did this */
275 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 if (strlen(name) == 2 && name[1] == ':')
287 name[1] = '\0';
288 if (strncmp(name, "/dev/", 5) == 0)
289 name += 5;
290 while ((p = strchr(name, '/')) != NULL)
291 *p = '_';
292
293 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
312 free(buf);
313 }
314 }
315 }
316
317 void populate_disk() {
318 int n, i;
319 diskio_stat_t *diskio;
320
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 }
337
338 void populate_proc() {
339 process_stat_t *proc = get_process_stats();
340
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 }
349
350 void populate_net() {
351 int n, i;
352 network_stat_t *net;
353 network_iface_stat_t *iface;
354
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 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 add_stat(TIME_T, &net[i].systime,
377 "net", name, "systime", NULL);
378 }
379 }
380
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 add_stat(BOOL, &iface[i].up,
389 "net", name, "up", NULL);
390 add_stat(DUPLEX, &iface[i].dup,
391 "net", name, "duplex", NULL);
392 }
393 }
394 }
395
396 void populate_page() {
397 page_stat_t *page;
398
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 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
404 }
405 }
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
462 if (stats != NULL)
463 qsort(stats, num_stats, sizeof *stats, stats_compare);
464 }
465
466 /* Print the value of a stat. */
467 void print_stat_value(const stat *s) {
468 void *v = s->stat;
469 long l;
470
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 l = *(time_t *)v;
478 printf("%ld", l);
479 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 break;
493 case BOOL:
494 printf("%s", *(int *)v ? "true" : "false");
495 break;
496 case DUPLEX:
497 switch (*(statgrab_duplex *) v) {
498 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 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 char *name = argv[i];
541 stat key;
542 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
551 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 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 print_stat(s);
579 }
580 }
581 }
582 }
583
584 void usage() {
585 printf("Usage: statgrab [OPTION]... [STAT]...\n"
586 "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 "\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 " -p Display CPU usage differences as percentages rather than\n"
600 " absolute values\n"
601 "\n");
602 printf("Version %s - report bugs to <%s>.\n",
603 PACKAGE_VERSION, PACKAGE_BUGREPORT);
604 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 if (repeat_mode == REPEAT_FOREVER)
650 die("mrtg mode: cannot repeat display");
651 }
652
653 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
654 die("CPU percentage usage display requires stat differences");
655
656 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 /* 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 if (statgrab_drop_privileges() != 0)
667 die("Failed to drop setuid/setgid privileges");
668
669 switch (repeat_mode) {
670 case REPEAT_NONE:
671 get_stats();
672 print_stats(argc, argv);
673 break;
674 case REPEAT_ONCE:
675 get_stats();
676 sleep(repeat_time);
677 get_stats();
678 print_stats(argc, argv);
679 break;
680 case REPEAT_FOREVER:
681 while (1) {
682 get_stats();
683 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