ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.27
Committed: Fri Jul 16 11:17:16 2004 UTC (20 years, 5 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.26: +24 -7 lines
Log Message:
Make sure that network interfaces get an interface_name stat, even if they
don't have IO stats (like for loopback interfaces on Solaris).

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.26 2004/04/07 15:50:26 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
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 sg_cpu_percents *cpu_p = sg_get_cpu_percents();
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 sg_cpu_stats *cpu_s;
189
190 cpu_s = use_diffs ? sg_get_cpu_stats_diff()
191 : sg_get_cpu_stats();
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
213 void populate_mem() {
214 sg_mem_stats *mem = sg_get_mem_stats();
215
216 if (mem != NULL) {
217 add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
218 add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
219 add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
220 add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
221 }
222 }
223
224 void populate_load() {
225 sg_load_stats *load = sg_get_load_stats();
226
227 if (load != NULL) {
228 add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
229 add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
230 add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
231 }
232 }
233
234 void populate_user() {
235 sg_user_stats *user = sg_get_user_stats();
236
237 if (user != NULL) {
238 add_stat(INT, &user->num_entries, "user", "num", NULL);
239 add_stat(STRING, &user->name_list, "user", "names", NULL);
240 }
241 }
242
243 void populate_swap() {
244 sg_swap_stats *swap = sg_get_swap_stats();
245
246 if (swap != NULL) {
247 add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
248 add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
249 add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
250 }
251 }
252
253 void populate_general() {
254 /* FIXME this should be renamed to host. */
255 sg_host_info *host = sg_get_host_info();
256
257 if (host != NULL) {
258 add_stat(STRING, &host->os_name,
259 "general", "os_name", NULL);
260 add_stat(STRING, &host->os_release,
261 "general", "os_release", NULL);
262 add_stat(STRING, &host->os_version,
263 "general", "os_version", NULL);
264 add_stat(STRING, &host->platform, "general", "platform", NULL);
265 add_stat(STRING, &host->hostname, "general", "hostname", NULL);
266 add_stat(TIME_T, &host->uptime, "general", "uptime", NULL);
267 }
268 }
269
270 void populate_fs() {
271 int n, i;
272 sg_fs_stats *disk = sg_get_fs_stats(&n);
273
274 if (disk != NULL) {
275 for (i = 0; i < n; i++) {
276 /* FIXME it'd be nicer if libstatgrab did this */
277 char *buf, *name, *p;
278 const char *device = disk[i].device_name;
279
280 if (strcmp(device, "/") == 0)
281 device = "root";
282
283 buf = strdup(device);
284 if (buf == NULL)
285 die("out of memory");
286
287 name = buf;
288 if (strlen(name) == 2 && name[1] == ':')
289 name[1] = '\0';
290 if (strncmp(name, "/dev/", 5) == 0)
291 name += 5;
292 while ((p = strchr(name, '/')) != NULL)
293 *p = '_';
294
295 add_stat(STRING, &disk[i].device_name,
296 "fs", name, "device_name", NULL);
297 add_stat(STRING, &disk[i].fs_type,
298 "fs", name, "fs_type", NULL);
299 add_stat(STRING, &disk[i].mnt_point,
300 "fs", name, "mnt_point", NULL);
301 add_stat(LONG_LONG, &disk[i].size,
302 "fs", name, "size", NULL);
303 add_stat(LONG_LONG, &disk[i].used,
304 "fs", name, "used", NULL);
305 add_stat(LONG_LONG, &disk[i].avail,
306 "fs", name, "avail", NULL);
307 add_stat(LONG_LONG, &disk[i].total_inodes,
308 "fs", name, "total_inodes", NULL);
309 add_stat(LONG_LONG, &disk[i].used_inodes,
310 "fs", name, "used_inodes", NULL);
311 add_stat(LONG_LONG, &disk[i].free_inodes,
312 "fs", name, "free_inodes", NULL);
313
314 free(buf);
315 }
316 }
317 }
318
319 void populate_disk() {
320 int n, i;
321 sg_disk_io_stats *diskio;
322
323 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
324 : sg_get_disk_io_stats(&n);
325 if (diskio != NULL) {
326 for (i = 0; i < n; i++) {
327 const char *name = diskio[i].disk_name;
328
329 add_stat(STRING, &diskio[i].disk_name,
330 "disk", name, "disk_name", NULL);
331 add_stat(LONG_LONG, &diskio[i].read_bytes,
332 "disk", name, "read_bytes", NULL);
333 add_stat(LONG_LONG, &diskio[i].write_bytes,
334 "disk", name, "write_bytes", NULL);
335 add_stat(TIME_T, &diskio[i].systime,
336 "disk", name, "systime", NULL);
337 }
338 }
339 }
340
341 void populate_proc() {
342 /* FIXME expose individual process info too */
343 sg_process_count *proc = sg_get_process_count();
344
345 if (proc != NULL) {
346 add_stat(INT, &proc->total, "proc", "total", NULL);
347 add_stat(INT, &proc->running, "proc", "running", NULL);
348 add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
349 add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
350 add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
351 }
352 }
353
354 void populate_net() {
355 int num_io, num_iface, i;
356 sg_network_io_stats *io;
357 sg_network_iface_stats *iface;
358
359 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
360 : sg_get_network_io_stats(&num_io);
361 if (io != NULL) {
362 for (i = 0; i < num_io; i++) {
363 const char *name = io[i].interface_name;
364
365 add_stat(STRING, &io[i].interface_name,
366 "net", name, "interface_name", NULL);
367 add_stat(LONG_LONG, &io[i].tx,
368 "net", name, "tx", NULL);
369 add_stat(LONG_LONG, &io[i].rx,
370 "net", name, "rx", NULL);
371 add_stat(LONG_LONG, &io[i].ipackets,
372 "net", name, "ipackets", NULL);
373 add_stat(LONG_LONG, &io[i].opackets,
374 "net", name, "opackets", NULL);
375 add_stat(LONG_LONG, &io[i].ierrors,
376 "net", name, "ierrors", NULL);
377 add_stat(LONG_LONG, &io[i].oerrors,
378 "net", name, "oerrors", NULL);
379 add_stat(LONG_LONG, &io[i].collisions,
380 "net", name, "collisions", NULL);
381 add_stat(TIME_T, &io[i].systime,
382 "net", name, "systime", NULL);
383 }
384 }
385
386 iface = sg_get_network_iface_stats(&num_iface);
387 if (iface != NULL) {
388 for (i = 0; i < num_iface; i++) {
389 const char *name = iface[i].interface_name;
390 int had_io = 0, j;
391
392 /* If there wasn't a corresponding io stat,
393 add interface_name from here. */
394 if (io != NULL) {
395 for (j = 0; j < num_io; j++) {
396 if (strcmp(io[j].interface_name,
397 name) == 0) {
398 had_io = 1;
399 break;
400 }
401 }
402 }
403 if (!had_io) {
404 add_stat(STRING, &iface[i].interface_name,
405 "net", name, "interface_name", NULL);
406 }
407
408 add_stat(INT, &iface[i].speed,
409 "net", name, "speed", NULL);
410 add_stat(BOOL, &iface[i].up,
411 "net", name, "up", NULL);
412 add_stat(DUPLEX, &iface[i].dup,
413 "net", name, "duplex", NULL);
414 }
415 }
416 }
417
418 void populate_page() {
419 sg_page_stats *page;
420
421 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
422 if (page != NULL) {
423 add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
424 add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
425 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
426 }
427 }
428
429 typedef struct {
430 const char *name;
431 void (*populate)();
432 int interesting;
433 } toplevel;
434 toplevel toplevels[] = {
435 {"const.", populate_const, 0},
436 {"cpu.", populate_cpu, 0},
437 {"mem.", populate_mem, 0},
438 {"load.", populate_load, 0},
439 {"user.", populate_user, 0},
440 {"swap.", populate_swap, 0},
441 {"general.", populate_general, 0},
442 {"fs.", populate_fs, 0},
443 {"disk.", populate_disk, 0},
444 {"proc.", populate_proc, 0},
445 {"net.", populate_net, 0},
446 {"page.", populate_page, 0},
447 {NULL, NULL, 0}
448 };
449
450 /* Set the "interesting" flag on the sections that we actually need to
451 fetch. */
452 void select_interesting(int argc, char **argv) {
453 toplevel *t;
454
455 if (argc == 0) {
456 for (t = &toplevels[0]; t->name != NULL; t++)
457 t->interesting = 1;
458 } else {
459 int i;
460
461 for (i = 0; i < argc; i++) {
462 for (t = &toplevels[0]; t->name != NULL; t++) {
463 if (strncmp(argv[i], t->name,
464 strlen(t->name)) == 0) {
465 t->interesting = 1;
466 break;
467 }
468 }
469 }
470 }
471 }
472
473 /* Clear and rebuild the stats array. */
474 void get_stats() {
475 toplevel *t;
476
477 clear_stats();
478
479 for (t = &toplevels[0]; t->name != NULL; t++) {
480 if (t->interesting)
481 t->populate();
482 }
483
484 if (stats != NULL)
485 qsort(stats, num_stats, sizeof *stats, stats_compare);
486 }
487
488 /* Print the value of a stat. */
489 void print_stat_value(const stat *s) {
490 void *v = s->stat;
491 long l;
492
493 switch (s->type) {
494 case LONG_LONG:
495 printf("%lld", *(long long *)v);
496 break;
497 case TIME_T:
498 /* FIXME option for formatted time? */
499 l = *(time_t *)v;
500 printf("%ld", l);
501 break;
502 case FLOAT:
503 printf("%f", *(float *)v);
504 break;
505 case DOUBLE:
506 printf("%f", *(double *)v);
507 break;
508 case STRING:
509 /* FIXME escaping? */
510 printf("%s", *(char **)v);
511 break;
512 case INT:
513 printf("%d", *(int *)v);
514 break;
515 case BOOL:
516 printf("%s", *(int *)v ? "true" : "false");
517 break;
518 case DUPLEX:
519 switch (*(sg_iface_duplex *) v) {
520 case SG_IFACE_DUPLEX_FULL:
521 printf("full");
522 break;
523 case SG_IFACE_DUPLEX_HALF:
524 printf("half");
525 break;
526 default:
527 printf("unknown");
528 break;
529 }
530 break;
531 }
532 }
533
534 /* Print the name and value of a stat. */
535 void print_stat(const stat *s) {
536 switch (display_mode) {
537 case DISPLAY_LINUX:
538 printf("%s = ", s->name);
539 break;
540 case DISPLAY_BSD:
541 printf("%s: ", s->name);
542 break;
543 case DISPLAY_MRTG:
544 case DISPLAY_PLAIN:
545 break;
546 }
547 print_stat_value(s);
548 printf("\n");
549 }
550
551 /* Print stats as specified on the provided command line. */
552 void print_stats(int argc, char **argv) {
553 int i;
554
555 if (argc == optind) {
556 /* Print all stats. */
557 for (i = 0; i < num_stats; i++)
558 print_stat(&stats[i]);
559 } else {
560 /* Print selected stats. */
561 for (i = optind; i < argc; i++) {
562 char *name = argv[i];
563 stat key;
564 const stat *s, *end;
565 int (*compare)(const void *, const void *);
566
567 key.name = name;
568 if (name[strlen(name) - 1] == '.')
569 compare = stats_compare_prefix;
570 else
571 compare = stats_compare;
572
573 if (stats == NULL) {
574 s = NULL;
575 } else {
576 s = (const stat *)bsearch(&key, stats,
577 num_stats,
578 sizeof *stats,
579 compare);
580 }
581
582 if (s == NULL) {
583 printf("Unknown stat %s\n", name);
584 continue;
585 }
586
587 /* Find the range of stats the user wanted. */
588 for (; s >= stats; s--) {
589 if (compare(&key, s) != 0)
590 break;
591 }
592 s++;
593 for (end = s; end < &stats[num_stats]; end++) {
594 if (compare(&key, end) != 0)
595 break;
596 }
597
598 /* And print them. */
599 for (; s < end; s++) {
600 print_stat(s);
601 }
602 }
603 }
604 }
605
606 void usage() {
607 printf("Usage: statgrab [OPTION]... [STAT]...\n"
608 "Display system statistics.\n"
609 "\n"
610 "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
611 "statistics starting with that prefix.\n"
612 "\n");
613 printf(" -l Linux sysctl-style output (default)\n"
614 " -b BSD sysctl-style output\n"
615 " -m MRTG-compatible output\n"
616 " -u Plain output (only show values)\n"
617 " -n Display cumulative stats once (default)\n"
618 " -s Display stat differences repeatedly\n"
619 " -o Display stat differences once\n"
620 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
621 " -p Display CPU usage differences as percentages rather than\n"
622 " absolute values\n"
623 "\n");
624 printf("Version %s - report bugs to <%s>.\n",
625 PACKAGE_VERSION, PACKAGE_BUGREPORT);
626 exit(1);
627 }
628
629 int main(int argc, char **argv) {
630 opterr = 0;
631 while (1) {
632 int c = getopt(argc, argv, "lbmunsot:p");
633 if (c == -1)
634 break;
635 switch (c) {
636 case 'l':
637 display_mode = DISPLAY_LINUX;
638 break;
639 case 'b':
640 display_mode = DISPLAY_BSD;
641 break;
642 case 'm':
643 display_mode = DISPLAY_MRTG;
644 break;
645 case 'u':
646 display_mode = DISPLAY_PLAIN;
647 break;
648 case 'n':
649 repeat_mode = REPEAT_NONE;
650 break;
651 case 's':
652 repeat_mode = REPEAT_FOREVER;
653 break;
654 case 'o':
655 repeat_mode = REPEAT_ONCE;
656 break;
657 case 't':
658 repeat_time = atoi(optarg);
659 break;
660 case 'p':
661 use_cpu_percent = 1;
662 break;
663 default:
664 usage();
665 }
666 }
667
668 if (display_mode == DISPLAY_MRTG) {
669 if ((argc - optind) != 2)
670 die("mrtg mode: must specify exactly two stats");
671 if (repeat_mode == REPEAT_FOREVER)
672 die("mrtg mode: cannot repeat display");
673 }
674
675 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
676 die("CPU percentage usage display requires stat differences");
677
678 if (repeat_mode == REPEAT_NONE)
679 use_diffs = 0;
680 else
681 use_diffs = 1;
682
683 select_interesting(argc - optind, &argv[optind]);
684
685 /* We don't care if statgrab_init fails, because we can just display
686 the statistics that can be read as non-root. */
687 sg_init();
688 if (sg_drop_privileges() != 0)
689 die("Failed to drop setuid/setgid privileges");
690
691 switch (repeat_mode) {
692 case REPEAT_NONE:
693 get_stats();
694 print_stats(argc, argv);
695 break;
696 case REPEAT_ONCE:
697 get_stats();
698 sleep(repeat_time);
699 get_stats();
700 print_stats(argc, argv);
701 break;
702 case REPEAT_FOREVER:
703 while (1) {
704 get_stats();
705 print_stats(argc, argv);
706 printf("\n");
707 sleep(repeat_time);
708 }
709 }
710
711 if (display_mode == DISPLAY_MRTG) {
712 printf("\n");
713 printf("statgrab\n");
714 }
715
716 return 0;
717 }
718