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.25 by tdb, Tue Apr 6 14:53:00 2004 UTC

# Line 1 | Line 1
1   /*
2 < * i-scream central monitoring system
2 > * i-scream libstatgrab
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 >                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);
# 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_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
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);
# Line 207 | Line 208 | void get_stats(int use_diffs) {
208                                   "cpu", "systime", NULL);
209                  }
210          }
211 + }
212  
213 <        mem = get_memory_stats();
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 <        load = get_load_stats();
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 <        user = get_user_stats();
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 <        swap = get_swap_stats();
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 <        gen = get_general_stats();
254 <        if (gen != NULL) {
255 <                add_stat(STRING, &gen->os_name,
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, &gen->os_release,
260 >                add_stat(STRING, &host->os_release,
261                           "general", "os_release", NULL);
262 <                add_stat(STRING, &gen->os_version,
262 >                add_stat(STRING, &host->os_version,
263                           "general", "os_version", NULL);
264 <                add_stat(STRING, &gen->platform, "general", "platform", NULL);
265 <                add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
266 <                add_stat(TIME_T, &gen->uptime, "general", "uptime", 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 <        disk = get_disk_stats(&n);
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 <                        const char *name = disk[i].device_name,
278 <                                   *p = strrchr(name, '/');
279 <                        if (p != NULL)
280 <                                name = p + 1;
281 <                        if (*name == '\0')
282 <                                name = "root";
283 <        
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,
# Line 278 | Line 310 | void get_stats(int use_diffs) {
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 <        diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
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;
# Line 296 | Line 336 | void get_stats(int use_diffs) {
336                                   "disk", name, "systime", NULL);
337                  }
338          }
339 + }
340  
341 <        proc = get_process_stats();
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);
# Line 305 | Line 349 | void get_stats(int use_diffs) {
349                  add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
350                  add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
351          }
352 + }
353  
354 <        net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
355 <        if (net != NULL) {
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 = net[i].interface_name;
363 >                        const char *name = io[i].interface_name;
364          
365 <                        add_stat(STRING, &net[i].interface_name,
365 >                        add_stat(STRING, &io[i].interface_name,
366                                   "net", name, "interface_name", NULL);
367 <                        add_stat(LONG_LONG, &net[i].tx,
367 >                        add_stat(LONG_LONG, &io[i].tx,
368                                   "net", name, "tx", NULL);
369 <                        add_stat(LONG_LONG, &net[i].rx,
369 >                        add_stat(LONG_LONG, &io[i].rx,
370                                   "net", name, "rx", NULL);
371 <                        add_stat(TIME_T, &net[i].systime,
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 <        page = use_diffs ? get_page_stats_diff() : get_page_stats();
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(LONG_LONG, &page->systime, "page", "systime", NULL);
408 >                add_stat(TIME_T, &page->systime, "page", "systime", NULL);
409          }
410 + }
411  
412 <        qsort(stats, num_stats, sizeof *stats, stats_compare);
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:
# Line 342 | Line 479 | void print_stat_value(const stat *s) {
479                  break;
480          case TIME_T:
481                  /* FIXME option for formatted time? */
482 <                printf("%ld", *(time_t *)v);
482 >                l = *(time_t *)v;
483 >                printf("%ld", l);
484                  break;
485          case FLOAT:
486                  printf("%f", *(float *)v);
# Line 357 | Line 495 | void print_stat_value(const stat *s) {
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  
# Line 388 | Line 542 | void print_stats(int argc, char **argv) {
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;
547 >                        const stat *s, *end;
548 >                        int (*compare)(const void *, const void *);
549  
550 <                        key.name = argv[i];
551 <                        s = (const stat *)bsearch(&key, stats, num_stats,
552 <                                                  sizeof *stats,
553 <                                                  stats_compare);
554 <                        if (s != NULL) {
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                  }
# Line 404 | Line 588 | void print_stats(int argc, char **argv) {
588  
589   void usage() {
590          printf("Usage: statgrab [OPTION]... [STAT]...\n"
591 <               "Display system statistics (all statistics by default).\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"
# Line 414 | Line 601 | void usage() {
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 as percentages rather than absolute values\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);
608 >               PACKAGE_VERSION, PACKAGE_BUGREPORT);
609          exit(1);
610   }
611  
# Line 463 | Line 651 | int main(int argc, char **argv) {
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_NONE)
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(0);
676 >                get_stats();
677                  print_stats(argc, argv);
678                  break;
679          case REPEAT_ONCE:
680 <                get_stats(1);
680 >                get_stats();
681                  sleep(repeat_time);
682 <                get_stats(1);
682 >                get_stats();
683                  print_stats(argc, argv);
684                  break;
685          case REPEAT_FOREVER:
686                  while (1) {
687 <                        get_stats(1);
687 >                        get_stats();
688                          print_stats(argc, argv);
689                          printf("\n");
690                          sleep(repeat_time);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines