ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
(Generate patch)

Comparing projects/libstatgrab/src/statgrab/statgrab.c (file contents):
Revision 1.2 by tdb, Wed Aug 27 14:00:12 2003 UTC vs.
Revision 1.23 by ats, Sun Apr 4 21:59:16 2004 UTC

# Line 1 | Line 1
1   /*
2   * i-scream central monitoring system
3   * http://www.i-scream.org
4 < * Copyright (C) 2000-2003 i-scream
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
# Line 16 | Line 16
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>
# Line 31 | Line 37 | typedef enum {
37          FLOAT,
38          DOUBLE,
39          STRING,
40 <        INT
40 >        INT,
41 >        BOOL,
42 >        DUPLEX
43   } stat_type;
44  
45   typedef enum {
# Line 62 | Line 70 | 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) {
# Line 75 | Line 84 | void clear_stats() {
84  
85          for (i = 0; i < num_stats; i++)
86                  free(stats[i].name);
78        free(stats);
79        stats = NULL;
87          num_stats = 0;
81        alloc_stats = 0;
88   }
89  
90   /* Add a stat. The varargs make up the name, joined with dots; the name is
# Line 138 | Line 144 | void add_stat(stat_type type, void *stat, ...) {
144          ++num_stats;
145   }
146  
147 < /* Compare two stats by name, for sorting purposes. */
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 < /* Clear and rebuild the stats array. */
153 < void get_stats(int use_diffs) {
154 <        cpu_states_t *cpu_s;
155 <        cpu_percent_t *cpu_p;
150 <        mem_stat_t *mem;
151 <        load_stat_t *load;
152 <        user_stat_t *user;
153 <        swap_stat_t *swap;
154 <        general_stat_t *gen;
155 <        disk_stat_t *disk;
156 <        diskio_stat_t *diskio;
157 <        process_stat_t *proc;
158 <        network_stat_t *net;
159 <        page_stat_t *page;
160 <        static int zero = 0;
161 <        int n, i;
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 <        clear_stats();
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 <        /* FIXME when only fetching some stats, it'd be more efficient to only
169 <           do the libstatgrab calls needed, rather than fetching everything. */
170 <
167 > void populate_cpu() {
168          if (use_cpu_percent) {
169 <                cpu_p = cpu_percent_usage();
169 >                cpu_percent_t *cpu_p = cpu_percent_usage();
170 >
171                  if (cpu_p != NULL) {
172                          add_stat(FLOAT, &cpu_p->user,
173                                   "cpu", "user", NULL);
# Line 183 | Line 181 | void get_stats(int use_diffs) {
181                                   "cpu", "swap", NULL);
182                          add_stat(FLOAT, &cpu_p->nice,
183                                   "cpu", "nice", NULL);
184 <                        add_stat(TIME_T, &cpu_s->systime,
184 >                        add_stat(TIME_T, &cpu_p->time_taken,
185                                   "cpu", "time_taken", NULL);
186                  }
187          } else {
188 +                cpu_states_t *cpu_s;
189 +
190                  cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
191                  if (cpu_s != NULL) {
192                          add_stat(LONG_LONG, &cpu_s->user,
# Line 207 | Line 207 | void get_stats(int use_diffs) {
207                                   "cpu", "systime", NULL);
208                  }
209          }
210 + }
211  
212 <        mem = get_memory_stats();
212 > void populate_mem() {
213 >        mem_stat_t *mem = get_memory_stats();
214 >
215          if (mem != NULL) {
216                  add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
217                  add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
218                  add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
219                  add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
220          }
221 + }
222  
223 <        load = get_load_stats();
223 > void populate_load() {
224 >        load_stat_t *load = get_load_stats();
225 >
226          if (load != NULL) {
227                  add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
228                  add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
229                  add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
230          }
231 + }
232  
233 <        user = get_user_stats();
233 > void populate_user() {
234 >        user_stat_t *user = get_user_stats();
235 >
236          if (user != NULL) {
237                  add_stat(INT, &user->num_entries, "user", "num", NULL);
238                  add_stat(STRING, &user->name_list, "user", "names", NULL);
239          }
240 + }
241  
242 <        swap = get_swap_stats();
242 > void populate_swap() {
243 >        swap_stat_t *swap = get_swap_stats();
244 >
245          if (swap != NULL) {
246                  add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
247                  add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
248                  add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
249          }
250 + }
251  
252 <        gen = get_general_stats();
252 > void populate_general() {
253 >        general_stat_t *gen = get_general_stats();
254 >
255          if (gen != NULL) {
256                  add_stat(STRING, &gen->os_name,
257                           "general", "os_name", NULL);
# Line 248 | Line 263 | void get_stats(int use_diffs) {
263                  add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
264                  add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
265          }
266 + }
267  
268 <        disk = get_disk_stats(&n);
268 > void populate_fs() {
269 >        int n, i;
270 >        disk_stat_t *disk = get_disk_stats(&n);
271 >
272          if (disk != NULL) {
273                  for (i = 0; i < n; i++) {
274                          /* FIXME it'd be nicer if libstatgrab did this */
275 <                        const char *name = disk[i].device_name,
276 <                                   *p = strrchr(name, '/');
277 <                        if (p != NULL)
278 <                                name = p + 1;
279 <                        if (*name == '\0')
280 <                                name = "root";
281 <        
275 >                        char *buf, *name, *p;
276 >                        const char *device = disk[i].device_name;
277 >
278 >                        if (strcmp(device, "/") == 0)
279 >                                device = "root";
280 >
281 >                        buf = strdup(device);
282 >                        if (buf == NULL)
283 >                                die("out of memory");
284 >
285 >                        name = buf;
286 >                        if (strlen(name) == 2 && name[1] == ':')
287 >                                name[1] = '\0';
288 >                        if (strncmp(name, "/dev/", 5) == 0)
289 >                                name += 5;
290 >                        while ((p = strchr(name, '/')) != NULL)
291 >                                *p = '_';
292 >
293                          add_stat(STRING, &disk[i].device_name,
294                                   "fs", name, "device_name", NULL);
295                          add_stat(STRING, &disk[i].fs_type,
# Line 278 | Line 308 | void get_stats(int use_diffs) {
308                                   "fs", name, "used_inodes", NULL);
309                          add_stat(LONG_LONG, &disk[i].free_inodes,
310                                   "fs", name, "free_inodes", NULL);
311 +
312 +                        free(buf);
313                  }
314          }
315 + }
316  
317 + void populate_disk() {
318 +        int n, i;
319 +        diskio_stat_t *diskio;
320 +
321          diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
322          if (diskio != NULL) {
323                  for (i = 0; i < n; i++) {
# Line 296 | Line 333 | void get_stats(int use_diffs) {
333                                   "disk", name, "systime", NULL);
334                  }
335          }
336 + }
337  
338 <        proc = get_process_stats();
338 > void populate_proc() {
339 >        process_stat_t *proc = get_process_stats();
340 >
341          if (proc != NULL) {
342                  add_stat(INT, &proc->total, "proc", "total", NULL);
343                  add_stat(INT, &proc->running, "proc", "running", NULL);
# Line 305 | Line 345 | void get_stats(int use_diffs) {
345                  add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
346                  add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
347          }
348 + }
349  
350 + void populate_net() {
351 +        int n, i;
352 +        network_stat_t *net;
353 +        network_iface_stat_t *iface;
354 +
355          net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
356          if (net != NULL) {
357                  for (i = 0; i < n; i++) {
# Line 317 | Line 363 | void get_stats(int use_diffs) {
363                                   "net", name, "tx", NULL);
364                          add_stat(LONG_LONG, &net[i].rx,
365                                   "net", name, "rx", NULL);
366 +                        add_stat(LONG_LONG, &net[i].ipackets,
367 +                                 "net", name, "ipackets", NULL);
368 +                        add_stat(LONG_LONG, &net[i].opackets,
369 +                                 "net", name, "opackets", NULL);
370 +                        add_stat(LONG_LONG, &net[i].ierrors,
371 +                                 "net", name, "ierrors", NULL);
372 +                        add_stat(LONG_LONG, &net[i].oerrors,
373 +                                 "net", name, "oerrors", NULL);
374 +                        add_stat(LONG_LONG, &net[i].collisions,
375 +                                 "net", name, "collisions", NULL);
376                          add_stat(TIME_T, &net[i].systime,
377                                   "net", name, "systime", NULL);
378                  }
379          }
380  
381 +        iface = get_network_iface_stats(&n);
382 +        if (iface != NULL) {
383 +                for (i = 0; i < n; i++) {
384 +                        const char *name = iface[i].interface_name;
385 +
386 +                        add_stat(INT, &iface[i].speed,
387 +                                 "net", name, "speed", NULL);
388 +                        add_stat(BOOL, &iface[i].up,
389 +                                 "net", name, "up", NULL);
390 +                        add_stat(DUPLEX, &iface[i].dup,
391 +                                 "net", name, "duplex", NULL);
392 +                }
393 +        }
394 + }
395 +
396 + void populate_page() {
397 +        page_stat_t *page;
398 +
399          page = use_diffs ? get_page_stats_diff() : get_page_stats();
400          if (page != NULL) {
401                  add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
402                  add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
403 <                add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
403 >                add_stat(TIME_T, &page->systime, "page", "systime", NULL);
404          }
405 + }
406  
407 <        qsort(stats, num_stats, sizeof *stats, stats_compare);
407 > typedef struct {
408 >        const char *name;
409 >        void (*populate)();
410 >        int interesting;
411 > } toplevel;
412 > toplevel toplevels[] = {
413 >        {"const.", populate_const, 0},
414 >        {"cpu.", populate_cpu, 0},
415 >        {"mem.", populate_mem, 0},
416 >        {"load.", populate_load, 0},
417 >        {"user.", populate_user, 0},
418 >        {"swap.", populate_swap, 0},
419 >        {"general.", populate_general, 0},
420 >        {"fs.", populate_fs, 0},
421 >        {"disk.", populate_disk, 0},
422 >        {"proc.", populate_proc, 0},
423 >        {"net.", populate_net, 0},
424 >        {"page.", populate_page, 0},
425 >        {NULL, NULL, 0}
426 > };
427 >
428 > /* Set the "interesting" flag on the sections that we actually need to
429 >   fetch. */
430 > void select_interesting(int argc, char **argv) {
431 >        toplevel *t;
432 >
433 >        if (argc == 0) {
434 >                for (t = &toplevels[0]; t->name != NULL; t++)
435 >                        t->interesting = 1;
436 >        } else {
437 >                int i;
438 >
439 >                for (i = 0; i < argc; i++) {
440 >                        for (t = &toplevels[0]; t->name != NULL; t++) {
441 >                                if (strncmp(argv[i], t->name,
442 >                                            strlen(t->name)) == 0) {
443 >                                        t->interesting = 1;
444 >                                        break;
445 >                                }
446 >                        }
447 >                }
448 >        }
449   }
450  
451 + /* Clear and rebuild the stats array. */
452 + void get_stats() {
453 +        toplevel *t;
454 +
455 +        clear_stats();
456 +
457 +        for (t = &toplevels[0]; t->name != NULL; t++) {
458 +                if (t->interesting)
459 +                        t->populate();
460 +        }
461 +
462 +        if (stats != NULL)
463 +                qsort(stats, num_stats, sizeof *stats, stats_compare);
464 + }
465 +
466   /* Print the value of a stat. */
467   void print_stat_value(const stat *s) {
468          void *v = s->stat;
469 +        long l;
470  
471          switch (s->type) {
472          case LONG_LONG:
# Line 342 | Line 474 | void print_stat_value(const stat *s) {
474                  break;
475          case TIME_T:
476                  /* FIXME option for formatted time? */
477 <                printf("%ld", *(time_t *)v);
477 >                l = *(time_t *)v;
478 >                printf("%ld", l);
479                  break;
480          case FLOAT:
481                  printf("%f", *(float *)v);
# Line 357 | Line 490 | void print_stat_value(const stat *s) {
490          case INT:
491                  printf("%d", *(int *)v);
492                  break;
493 +        case BOOL:
494 +                printf("%s", *(int *)v ? "true" : "false");
495 +                break;
496 +        case DUPLEX:
497 +                switch (*(statgrab_duplex *) v) {
498 +                case FULL_DUPLEX:
499 +                        printf("full");
500 +                        break;
501 +                case HALF_DUPLEX:
502 +                        printf("half");
503 +                        break;
504 +                default:
505 +                        printf("unknown");
506 +                        break;
507 +                }
508 +                break;
509          }
510   }
511  
# Line 388 | Line 537 | void print_stats(int argc, char **argv) {
537          } else {
538                  /* Print selected stats. */
539                  for (i = optind; i < argc; i++) {
540 +                        char *name = argv[i];
541                          stat key;
542 <                        const stat *s;
542 >                        const stat *s, *end;
543 >                        int (*compare)(const void *, const void *);
544  
545 <                        key.name = argv[i];
546 <                        s = (const stat *)bsearch(&key, stats, num_stats,
547 <                                                  sizeof *stats,
548 <                                                  stats_compare);
549 <                        if (s != NULL) {
545 >                        key.name = name;
546 >                        if (name[strlen(name) - 1] == '.')
547 >                                compare = stats_compare_prefix;
548 >                        else
549 >                                compare = stats_compare;
550 >
551 >                        if (stats == NULL) {
552 >                                s = NULL;
553 >                        } else {
554 >                                s = (const stat *)bsearch(&key, stats,
555 >                                                          num_stats,
556 >                                                          sizeof *stats,
557 >                                                          compare);
558 >                        }
559 >
560 >                        if (s == NULL) {
561 >                                printf("Unknown stat %s\n", name);
562 >                                continue;
563 >                        }
564 >
565 >                        /* Find the range of stats the user wanted. */
566 >                        for (; s >= stats; s--) {
567 >                                if (compare(&key, s) != 0)
568 >                                        break;
569 >                        }
570 >                        s++;
571 >                        for (end = s; end < &stats[num_stats]; end++) {
572 >                                if (compare(&key, end) != 0)
573 >                                        break;
574 >                        }
575 >
576 >                        /* And print them. */
577 >                        for (; s < end; s++) {
578                                  print_stat(s);
579                          }
580                  }
# Line 404 | Line 583 | void print_stats(int argc, char **argv) {
583  
584   void usage() {
585          printf("Usage: statgrab [OPTION]... [STAT]...\n"
586 <               "Display system statistics (all statistics by default).\n"
586 >               "Display system statistics.\n"
587 >               "\n"
588 >               "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
589 >               "statistics starting with that prefix.\n"
590                 "\n");
591          printf("  -l         Linux sysctl-style output (default)\n"
592                 "  -b         BSD sysctl-style output\n"
# Line 414 | Line 596 | void usage() {
596                 "  -s         Display stat differences repeatedly\n"
597                 "  -o         Display stat differences once\n"
598                 "  -t DELAY   When repeating, wait DELAY seconds between updates (default 1)\n"
599 <               "  -p         Display CPU usage as percentages rather than absolute values\n"
599 >               "  -p         Display CPU usage differences as percentages rather than\n"
600 >               "             absolute values\n"
601                 "\n");
602          printf("Version %s - report bugs to <%s>.\n",
603 <                PACKAGE_VERSION, PACKAGE_BUGREPORT);
603 >               PACKAGE_VERSION, PACKAGE_BUGREPORT);
604          exit(1);
605   }
606  
# Line 463 | Line 646 | int main(int argc, char **argv) {
646          if (display_mode == DISPLAY_MRTG) {
647                  if ((argc - optind) != 2)
648                          die("mrtg mode: must specify exactly two stats");
649 <                if (repeat_mode != REPEAT_NONE)
649 >                if (repeat_mode == REPEAT_FOREVER)
650                          die("mrtg mode: cannot repeat display");
651          }
652  
653 +        if (use_cpu_percent && repeat_mode == REPEAT_NONE)
654 +                die("CPU percentage usage display requires stat differences");
655 +
656 +        if (repeat_mode == REPEAT_NONE)
657 +                use_diffs = 0;
658 +        else
659 +                use_diffs = 1;
660 +
661 +        select_interesting(argc - optind, &argv[optind]);
662 +
663 +        /* We don't care if statgrab_init fails, because we can just display
664 +           the statistics that can be read as non-root. */
665 +        statgrab_init();
666 +        if (statgrab_drop_privileges() != 0)
667 +                die("Failed to drop setuid/setgid privileges");
668 +
669          switch (repeat_mode) {
670          case REPEAT_NONE:
671 <                get_stats(0);
671 >                get_stats();
672                  print_stats(argc, argv);
673                  break;
674          case REPEAT_ONCE:
675 <                get_stats(1);
675 >                get_stats();
676                  sleep(repeat_time);
677 <                get_stats(1);
677 >                get_stats();
678                  print_stats(argc, argv);
679                  break;
680          case REPEAT_FOREVER:
681                  while (1) {
682 <                        get_stats(1);
682 >                        get_stats();
683                          print_stats(argc, argv);
684                          printf("\n");
685                          sleep(repeat_time);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines