ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.14
Committed: Mon Jan 5 16:39:15 2004 UTC (20 years, 10 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.13: +4 -0 lines
Log Message:
Drop setuidness on Solaris.

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