--- projects/libstatgrab/src/statgrab/statgrab.c 2003/08/29 06:48:04 1.6 +++ projects/libstatgrab/src/statgrab/statgrab.c 2004/04/06 14:53:00 1.25 @@ -1,7 +1,7 @@ /* - * i-scream central monitoring system + * i-scream libstatgrab * http://www.i-scream.org - * Copyright (C) 2000-2003 i-scream + * Copyright (C) 2000-2004 i-scream * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id: statgrab.c,v 1.25 2004/04/06 14:53:00 tdb Exp $ */ #ifdef HAVE_CONFIG_H @@ -35,7 +37,9 @@ typedef enum { FLOAT, DOUBLE, STRING, - INT + INT, + BOOL, + DUPLEX } stat_type; typedef enum { @@ -66,6 +70,7 @@ display_mode_type display_mode = DISPLAY_LINUX; repeat_mode_type repeat_mode = REPEAT_NONE; int repeat_time = 1; int use_cpu_percent = 0; +int use_diffs = 0; /* Exit with an error message. */ void die(const char *s) { @@ -139,38 +144,30 @@ void add_stat(stat_type type, void *stat, ...) { ++num_stats; } -/* Compare two stats by name, for sorting purposes. */ +/* Compare two stats by name for qsort and bsearch. */ int stats_compare(const void *a, const void *b) { return strcmp(((stat *)a)->name, ((stat *)b)->name); } -/* Clear and rebuild the stats array. */ -void get_stats(int use_diffs) { - cpu_states_t *cpu_s; - cpu_percent_t *cpu_p; - mem_stat_t *mem; - load_stat_t *load; - user_stat_t *user; - swap_stat_t *swap; - general_stat_t *gen; - disk_stat_t *disk; - diskio_stat_t *diskio; - process_stat_t *proc; - network_stat_t *net; - page_stat_t *page; - static int zero = 0; - int n, i; +/* Compare up to the length of the key for bsearch. */ +int stats_compare_prefix(const void *key, const void *item) { + const char *kn = ((stat *)key)->name; + const char *in = ((stat *)item)->name; - clear_stats(); + return strncmp(kn, in, strlen(kn)); +} +void populate_const() { + static int zero = 0; + /* Constants, for use with MRTG mode. */ add_stat(INT, &zero, "const", "0", NULL); +} - /* FIXME when only fetching some stats, it'd be more efficient to only - do the libstatgrab calls needed, rather than fetching everything. */ - +void populate_cpu() { if (use_cpu_percent) { - cpu_p = cpu_percent_usage(); + sg_cpu_percents *cpu_p = sg_get_cpu_percents(); + if (cpu_p != NULL) { add_stat(FLOAT, &cpu_p->user, "cpu", "user", NULL); @@ -184,11 +181,14 @@ void get_stats(int use_diffs) { "cpu", "swap", NULL); add_stat(FLOAT, &cpu_p->nice, "cpu", "nice", NULL); - add_stat(TIME_T, &cpu_p->systime, + add_stat(TIME_T, &cpu_p->time_taken, "cpu", "time_taken", NULL); } } else { - cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals(); + sg_cpu_stats *cpu_s; + + cpu_s = use_diffs ? sg_get_cpu_stats_diff() + : sg_get_cpu_stats(); if (cpu_s != NULL) { add_stat(LONG_LONG, &cpu_s->user, "cpu", "user", NULL); @@ -208,59 +208,90 @@ void get_stats(int use_diffs) { "cpu", "systime", NULL); } } +} - mem = get_memory_stats(); +void populate_mem() { + sg_mem_stats *mem = sg_get_mem_stats(); + if (mem != NULL) { add_stat(LONG_LONG, &mem->total, "mem", "total", NULL); add_stat(LONG_LONG, &mem->free, "mem", "free", NULL); add_stat(LONG_LONG, &mem->used, "mem", "used", NULL); add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL); } +} - load = get_load_stats(); +void populate_load() { + sg_load_stats *load = sg_get_load_stats(); + if (load != NULL) { add_stat(DOUBLE, &load->min1, "load", "min1", NULL); add_stat(DOUBLE, &load->min5, "load", "min5", NULL); add_stat(DOUBLE, &load->min15, "load", "min15", NULL); } +} - user = get_user_stats(); +void populate_user() { + sg_user_stats *user = sg_get_user_stats(); + if (user != NULL) { add_stat(INT, &user->num_entries, "user", "num", NULL); add_stat(STRING, &user->name_list, "user", "names", NULL); } +} - swap = get_swap_stats(); +void populate_swap() { + sg_swap_stats *swap = sg_get_swap_stats(); + if (swap != NULL) { add_stat(LONG_LONG, &swap->total, "swap", "total", NULL); add_stat(LONG_LONG, &swap->used, "swap", "used", NULL); add_stat(LONG_LONG, &swap->free, "swap", "free", NULL); } +} - gen = get_general_stats(); - if (gen != NULL) { - add_stat(STRING, &gen->os_name, +void populate_general() { + /* FIXME this should be renamed to host. */ + sg_host_info *host = sg_get_host_info(); + + if (host != NULL) { + add_stat(STRING, &host->os_name, "general", "os_name", NULL); - add_stat(STRING, &gen->os_release, + add_stat(STRING, &host->os_release, "general", "os_release", NULL); - add_stat(STRING, &gen->os_version, + add_stat(STRING, &host->os_version, "general", "os_version", NULL); - add_stat(STRING, &gen->platform, "general", "platform", NULL); - add_stat(STRING, &gen->hostname, "general", "hostname", NULL); - add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL); + add_stat(STRING, &host->platform, "general", "platform", NULL); + add_stat(STRING, &host->hostname, "general", "hostname", NULL); + add_stat(TIME_T, &host->uptime, "general", "uptime", NULL); } +} - disk = get_disk_stats(&n); +void populate_fs() { + int n, i; + sg_fs_stats *disk = sg_get_fs_stats(&n); + if (disk != NULL) { for (i = 0; i < n; i++) { /* FIXME it'd be nicer if libstatgrab did this */ - const char *name = disk[i].device_name, - *p = strrchr(name, '/'); - if (p != NULL) - name = p + 1; - if (*name == '\0') - name = "root"; - + char *buf, *name, *p; + const char *device = disk[i].device_name; + + if (strcmp(device, "/") == 0) + device = "root"; + + buf = strdup(device); + if (buf == NULL) + die("out of memory"); + + name = buf; + if (strlen(name) == 2 && name[1] == ':') + name[1] = '\0'; + if (strncmp(name, "/dev/", 5) == 0) + name += 5; + while ((p = strchr(name, '/')) != NULL) + *p = '_'; + add_stat(STRING, &disk[i].device_name, "fs", name, "device_name", NULL); add_stat(STRING, &disk[i].fs_type, @@ -279,10 +310,18 @@ void get_stats(int use_diffs) { "fs", name, "used_inodes", NULL); add_stat(LONG_LONG, &disk[i].free_inodes, "fs", name, "free_inodes", NULL); + + free(buf); } } +} - diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n); +void populate_disk() { + int n, i; + sg_disk_io_stats *diskio; + + diskio = use_diffs ? sg_get_disk_io_stats_diff(&n) + : sg_get_disk_io_stats(&n); if (diskio != NULL) { for (i = 0; i < n; i++) { const char *name = diskio[i].disk_name; @@ -297,8 +336,12 @@ void get_stats(int use_diffs) { "disk", name, "systime", NULL); } } +} - proc = get_process_stats(); +void populate_proc() { + /* FIXME expose individual process info too */ + sg_process_count *proc = sg_get_process_count(); + if (proc != NULL) { add_stat(INT, &proc->total, "proc", "total", NULL); add_stat(INT, &proc->running, "proc", "running", NULL); @@ -306,36 +349,129 @@ void get_stats(int use_diffs) { add_stat(INT, &proc->stopped, "proc", "stopped", NULL); add_stat(INT, &proc->zombie, "proc", "zombie", NULL); } +} - net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n); - if (net != NULL) { +void populate_net() { + int n, i; + sg_network_io_stats *io; + sg_network_iface_stats *iface; + + io = use_diffs ? sg_get_network_io_stats_diff(&n) + : sg_get_network_io_stats(&n); + if (io != NULL) { for (i = 0; i < n; i++) { - const char *name = net[i].interface_name; + const char *name = io[i].interface_name; - add_stat(STRING, &net[i].interface_name, + add_stat(STRING, &io[i].interface_name, "net", name, "interface_name", NULL); - add_stat(LONG_LONG, &net[i].tx, + add_stat(LONG_LONG, &io[i].tx, "net", name, "tx", NULL); - add_stat(LONG_LONG, &net[i].rx, + add_stat(LONG_LONG, &io[i].rx, "net", name, "rx", NULL); - add_stat(TIME_T, &net[i].systime, + add_stat(LONG_LONG, &io[i].ipackets, + "net", name, "ipackets", NULL); + add_stat(LONG_LONG, &io[i].opackets, + "net", name, "opackets", NULL); + add_stat(LONG_LONG, &io[i].ierrors, + "net", name, "ierrors", NULL); + add_stat(LONG_LONG, &io[i].oerrors, + "net", name, "oerrors", NULL); + add_stat(LONG_LONG, &io[i].collisions, + "net", name, "collisions", NULL); + add_stat(TIME_T, &io[i].systime, "net", name, "systime", NULL); } } - page = use_diffs ? get_page_stats_diff() : get_page_stats(); + iface = sg_get_network_iface_stats(&n); + if (iface != NULL) { + for (i = 0; i < n; i++) { + const char *name = iface[i].interface_name; + + add_stat(INT, &iface[i].speed, + "net", name, "speed", NULL); + add_stat(BOOL, &iface[i].up, + "net", name, "up", NULL); + add_stat(DUPLEX, &iface[i].dup, + "net", name, "duplex", NULL); + } + } +} + +void populate_page() { + sg_page_stats *page; + + page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats(); if (page != NULL) { add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL); add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL); - add_stat(LONG_LONG, &page->systime, "page", "systime", NULL); + add_stat(TIME_T, &page->systime, "page", "systime", NULL); } +} - qsort(stats, num_stats, sizeof *stats, stats_compare); +typedef struct { + const char *name; + void (*populate)(); + int interesting; +} toplevel; +toplevel toplevels[] = { + {"const.", populate_const, 0}, + {"cpu.", populate_cpu, 0}, + {"mem.", populate_mem, 0}, + {"load.", populate_load, 0}, + {"user.", populate_user, 0}, + {"swap.", populate_swap, 0}, + {"general.", populate_general, 0}, + {"fs.", populate_fs, 0}, + {"disk.", populate_disk, 0}, + {"proc.", populate_proc, 0}, + {"net.", populate_net, 0}, + {"page.", populate_page, 0}, + {NULL, NULL, 0} +}; + +/* Set the "interesting" flag on the sections that we actually need to + fetch. */ +void select_interesting(int argc, char **argv) { + toplevel *t; + + if (argc == 0) { + for (t = &toplevels[0]; t->name != NULL; t++) + t->interesting = 1; + } else { + int i; + + for (i = 0; i < argc; i++) { + for (t = &toplevels[0]; t->name != NULL; t++) { + if (strncmp(argv[i], t->name, + strlen(t->name)) == 0) { + t->interesting = 1; + break; + } + } + } + } } +/* Clear and rebuild the stats array. */ +void get_stats() { + toplevel *t; + + clear_stats(); + + for (t = &toplevels[0]; t->name != NULL; t++) { + if (t->interesting) + t->populate(); + } + + if (stats != NULL) + qsort(stats, num_stats, sizeof *stats, stats_compare); +} + /* Print the value of a stat. */ void print_stat_value(const stat *s) { void *v = s->stat; + long l; switch (s->type) { case LONG_LONG: @@ -343,7 +479,8 @@ void print_stat_value(const stat *s) { break; case TIME_T: /* FIXME option for formatted time? */ - printf("%ld", *(time_t *)v); + l = *(time_t *)v; + printf("%ld", l); break; case FLOAT: printf("%f", *(float *)v); @@ -358,6 +495,22 @@ void print_stat_value(const stat *s) { case INT: printf("%d", *(int *)v); break; + case BOOL: + printf("%s", *(int *)v ? "true" : "false"); + break; + case DUPLEX: + switch (*(sg_iface_duplex *) v) { + case SG_IFACE_DUPLEX_FULL: + printf("full"); + break; + case SG_IFACE_DUPLEX_HALF: + printf("half"); + break; + default: + printf("unknown"); + break; + } + break; } } @@ -389,14 +542,44 @@ void print_stats(int argc, char **argv) { } else { /* Print selected stats. */ for (i = optind; i < argc; i++) { + char *name = argv[i]; stat key; - const stat *s; + const stat *s, *end; + int (*compare)(const void *, const void *); - key.name = argv[i]; - s = (const stat *)bsearch(&key, stats, num_stats, - sizeof *stats, - stats_compare); - if (s != NULL) { + key.name = name; + if (name[strlen(name) - 1] == '.') + compare = stats_compare_prefix; + else + compare = stats_compare; + + if (stats == NULL) { + s = NULL; + } else { + s = (const stat *)bsearch(&key, stats, + num_stats, + sizeof *stats, + compare); + } + + if (s == NULL) { + printf("Unknown stat %s\n", name); + continue; + } + + /* Find the range of stats the user wanted. */ + for (; s >= stats; s--) { + if (compare(&key, s) != 0) + break; + } + s++; + for (end = s; end < &stats[num_stats]; end++) { + if (compare(&key, end) != 0) + break; + } + + /* And print them. */ + for (; s < end; s++) { print_stat(s); } } @@ -405,7 +588,10 @@ void print_stats(int argc, char **argv) { void usage() { printf("Usage: statgrab [OPTION]... [STAT]...\n" - "Display system statistics (all statistics by default).\n" + "Display system statistics.\n" + "\n" + "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n" + "statistics starting with that prefix.\n" "\n"); printf(" -l Linux sysctl-style output (default)\n" " -b BSD sysctl-style output\n" @@ -472,20 +658,33 @@ int main(int argc, char **argv) { if (use_cpu_percent && repeat_mode == REPEAT_NONE) die("CPU percentage usage display requires stat differences"); + if (repeat_mode == REPEAT_NONE) + use_diffs = 0; + else + use_diffs = 1; + + select_interesting(argc - optind, &argv[optind]); + + /* We don't care if statgrab_init fails, because we can just display + the statistics that can be read as non-root. */ + sg_init(); + if (sg_drop_privileges() != 0) + die("Failed to drop setuid/setgid privileges"); + switch (repeat_mode) { case REPEAT_NONE: - get_stats(0); + get_stats(); print_stats(argc, argv); break; case REPEAT_ONCE: - get_stats(1); + get_stats(); sleep(repeat_time); - get_stats(1); + get_stats(); print_stats(argc, argv); break; case REPEAT_FOREVER: while (1) { - get_stats(1); + get_stats(); print_stats(argc, argv); printf("\n"); sleep(repeat_time);