ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.19
Committed: Fri Feb 13 15:13:37 2004 UTC (20 years, 10 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.18: +2 -2 lines
Log Message:
Track rename of duplex to statgrab_duplex.

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