ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.29
Committed: Tue Aug 10 18:50:37 2004 UTC (19 years, 9 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.28: +18 -5 lines
Log Message:
Add -f option to scale floating-point values to integers for MRTG
output.

File Contents

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