ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.26
Committed: Wed Apr 7 15:50:26 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_10
Changes since 1.25: +60 -60 lines
Log Message:
More whitespace tidyup (spaces->tabs).

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.25 2004/04/06 14:53:00 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 n, i;
356 sg_network_io_stats *io;
357 sg_network_iface_stats *iface;
358
359 io = use_diffs ? sg_get_network_io_stats_diff(&n)
360 : sg_get_network_io_stats(&n);
361 if (io != NULL) {
362 for (i = 0; i < n; 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(&n);
387 if (iface != NULL) {
388 for (i = 0; i < n; i++) {
389 const char *name = iface[i].interface_name;
390
391 add_stat(INT, &iface[i].speed,
392 "net", name, "speed", NULL);
393 add_stat(BOOL, &iface[i].up,
394 "net", name, "up", NULL);
395 add_stat(DUPLEX, &iface[i].dup,
396 "net", name, "duplex", NULL);
397 }
398 }
399 }
400
401 void populate_page() {
402 sg_page_stats *page;
403
404 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
405 if (page != NULL) {
406 add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
407 add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
408 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
409 }
410 }
411
412 typedef struct {
413 const char *name;
414 void (*populate)();
415 int interesting;
416 } toplevel;
417 toplevel toplevels[] = {
418 {"const.", populate_const, 0},
419 {"cpu.", populate_cpu, 0},
420 {"mem.", populate_mem, 0},
421 {"load.", populate_load, 0},
422 {"user.", populate_user, 0},
423 {"swap.", populate_swap, 0},
424 {"general.", populate_general, 0},
425 {"fs.", populate_fs, 0},
426 {"disk.", populate_disk, 0},
427 {"proc.", populate_proc, 0},
428 {"net.", populate_net, 0},
429 {"page.", populate_page, 0},
430 {NULL, NULL, 0}
431 };
432
433 /* Set the "interesting" flag on the sections that we actually need to
434 fetch. */
435 void select_interesting(int argc, char **argv) {
436 toplevel *t;
437
438 if (argc == 0) {
439 for (t = &toplevels[0]; t->name != NULL; t++)
440 t->interesting = 1;
441 } else {
442 int i;
443
444 for (i = 0; i < argc; i++) {
445 for (t = &toplevels[0]; t->name != NULL; t++) {
446 if (strncmp(argv[i], t->name,
447 strlen(t->name)) == 0) {
448 t->interesting = 1;
449 break;
450 }
451 }
452 }
453 }
454 }
455
456 /* Clear and rebuild the stats array. */
457 void get_stats() {
458 toplevel *t;
459
460 clear_stats();
461
462 for (t = &toplevels[0]; t->name != NULL; t++) {
463 if (t->interesting)
464 t->populate();
465 }
466
467 if (stats != NULL)
468 qsort(stats, num_stats, sizeof *stats, stats_compare);
469 }
470
471 /* Print the value of a stat. */
472 void print_stat_value(const stat *s) {
473 void *v = s->stat;
474 long l;
475
476 switch (s->type) {
477 case LONG_LONG:
478 printf("%lld", *(long long *)v);
479 break;
480 case TIME_T:
481 /* FIXME option for formatted time? */
482 l = *(time_t *)v;
483 printf("%ld", l);
484 break;
485 case FLOAT:
486 printf("%f", *(float *)v);
487 break;
488 case DOUBLE:
489 printf("%f", *(double *)v);
490 break;
491 case STRING:
492 /* FIXME escaping? */
493 printf("%s", *(char **)v);
494 break;
495 case INT:
496 printf("%d", *(int *)v);
497 break;
498 case BOOL:
499 printf("%s", *(int *)v ? "true" : "false");
500 break;
501 case DUPLEX:
502 switch (*(sg_iface_duplex *) v) {
503 case SG_IFACE_DUPLEX_FULL:
504 printf("full");
505 break;
506 case SG_IFACE_DUPLEX_HALF:
507 printf("half");
508 break;
509 default:
510 printf("unknown");
511 break;
512 }
513 break;
514 }
515 }
516
517 /* Print the name and value of a stat. */
518 void print_stat(const stat *s) {
519 switch (display_mode) {
520 case DISPLAY_LINUX:
521 printf("%s = ", s->name);
522 break;
523 case DISPLAY_BSD:
524 printf("%s: ", s->name);
525 break;
526 case DISPLAY_MRTG:
527 case DISPLAY_PLAIN:
528 break;
529 }
530 print_stat_value(s);
531 printf("\n");
532 }
533
534 /* Print stats as specified on the provided command line. */
535 void print_stats(int argc, char **argv) {
536 int i;
537
538 if (argc == optind) {
539 /* Print all stats. */
540 for (i = 0; i < num_stats; i++)
541 print_stat(&stats[i]);
542 } else {
543 /* Print selected stats. */
544 for (i = optind; i < argc; i++) {
545 char *name = argv[i];
546 stat key;
547 const stat *s, *end;
548 int (*compare)(const void *, const void *);
549
550 key.name = name;
551 if (name[strlen(name) - 1] == '.')
552 compare = stats_compare_prefix;
553 else
554 compare = stats_compare;
555
556 if (stats == NULL) {
557 s = NULL;
558 } else {
559 s = (const stat *)bsearch(&key, stats,
560 num_stats,
561 sizeof *stats,
562 compare);
563 }
564
565 if (s == NULL) {
566 printf("Unknown stat %s\n", name);
567 continue;
568 }
569
570 /* Find the range of stats the user wanted. */
571 for (; s >= stats; s--) {
572 if (compare(&key, s) != 0)
573 break;
574 }
575 s++;
576 for (end = s; end < &stats[num_stats]; end++) {
577 if (compare(&key, end) != 0)
578 break;
579 }
580
581 /* And print them. */
582 for (; s < end; s++) {
583 print_stat(s);
584 }
585 }
586 }
587 }
588
589 void usage() {
590 printf("Usage: statgrab [OPTION]... [STAT]...\n"
591 "Display system statistics.\n"
592 "\n"
593 "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
594 "statistics starting with that prefix.\n"
595 "\n");
596 printf(" -l Linux sysctl-style output (default)\n"
597 " -b BSD sysctl-style output\n"
598 " -m MRTG-compatible output\n"
599 " -u Plain output (only show values)\n"
600 " -n Display cumulative stats once (default)\n"
601 " -s Display stat differences repeatedly\n"
602 " -o Display stat differences once\n"
603 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
604 " -p Display CPU usage differences as percentages rather than\n"
605 " absolute values\n"
606 "\n");
607 printf("Version %s - report bugs to <%s>.\n",
608 PACKAGE_VERSION, PACKAGE_BUGREPORT);
609 exit(1);
610 }
611
612 int main(int argc, char **argv) {
613 opterr = 0;
614 while (1) {
615 int c = getopt(argc, argv, "lbmunsot:p");
616 if (c == -1)
617 break;
618 switch (c) {
619 case 'l':
620 display_mode = DISPLAY_LINUX;
621 break;
622 case 'b':
623 display_mode = DISPLAY_BSD;
624 break;
625 case 'm':
626 display_mode = DISPLAY_MRTG;
627 break;
628 case 'u':
629 display_mode = DISPLAY_PLAIN;
630 break;
631 case 'n':
632 repeat_mode = REPEAT_NONE;
633 break;
634 case 's':
635 repeat_mode = REPEAT_FOREVER;
636 break;
637 case 'o':
638 repeat_mode = REPEAT_ONCE;
639 break;
640 case 't':
641 repeat_time = atoi(optarg);
642 break;
643 case 'p':
644 use_cpu_percent = 1;
645 break;
646 default:
647 usage();
648 }
649 }
650
651 if (display_mode == DISPLAY_MRTG) {
652 if ((argc - optind) != 2)
653 die("mrtg mode: must specify exactly two stats");
654 if (repeat_mode == REPEAT_FOREVER)
655 die("mrtg mode: cannot repeat display");
656 }
657
658 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
659 die("CPU percentage usage display requires stat differences");
660
661 if (repeat_mode == REPEAT_NONE)
662 use_diffs = 0;
663 else
664 use_diffs = 1;
665
666 select_interesting(argc - optind, &argv[optind]);
667
668 /* We don't care if statgrab_init fails, because we can just display
669 the statistics that can be read as non-root. */
670 sg_init();
671 if (sg_drop_privileges() != 0)
672 die("Failed to drop setuid/setgid privileges");
673
674 switch (repeat_mode) {
675 case REPEAT_NONE:
676 get_stats();
677 print_stats(argc, argv);
678 break;
679 case REPEAT_ONCE:
680 get_stats();
681 sleep(repeat_time);
682 get_stats();
683 print_stats(argc, argv);
684 break;
685 case REPEAT_FOREVER:
686 while (1) {
687 get_stats();
688 print_stats(argc, argv);
689 printf("\n");
690 sleep(repeat_time);
691 }
692 }
693
694 if (display_mode == DISPLAY_MRTG) {
695 printf("\n");
696 printf("statgrab\n");
697 }
698
699 return 0;
700 }
701