ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.17
Committed: Mon Jan 19 16:49:23 2004 UTC (20 years, 4 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_8_2, LIBSTATGRAB_0_8_1
Changes since 1.16: +2 -0 lines
Log Message:
A whole bunch of minor cosmetic changes.

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