ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.12
Committed: Mon Oct 20 22:18:21 2003 UTC (21 years, 2 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_7
Changes since 1.11: +8 -0 lines
Log Message:
Use statgrab_init and drop privs as soon as possible.

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