Initial import

This commit is contained in:
Thomas Graf
2013-07-05 15:11:46 +02:00
parent 924d1e1fb4
commit 3be703f67d
52 changed files with 10285 additions and 13 deletions

2
src/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
bmon

42
src/Makefile.am Normal file
View File

@ -0,0 +1,42 @@
# -*- Makefile -*-
bin_PROGRAMS = bmon
bmon_CFLAGS = \
-I${top_srcdir}/include \
-I${top_builddir}/include \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-D_GNU_SOURCE \
$(CURSES_CFLAGS) \
$(CONFUSE_CFLAGS) \
$(LIBNL_CFLAGS) \
$(LIBNL_ROUTE_CFLAGS)
bmon_LDADD = \
$(CURSES_LIB) \
$(CONFUSE_LIBS) \
$(LIBNL_LIBS) \
$(LIBNL_ROUTE_LIBS)
bmon_SOURCES = \
utils.c \
unit.c \
conf.c \
input.c \
output.c \
group.c \
element.c \
attr.c \
element_cfg.c \
history.c \
graph.c \
bmon.c \
module.c \
in_netlink.c \
in_null.c \
in_dummy.c \
in_proc.c \
out_null.c \
out_format.c \
out_ascii.c \
out_curses.c

635
src/attr.c Normal file
View File

@ -0,0 +1,635 @@
/*
* attr.c Attributes
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/attr.h>
#include <bmon/history.h>
#include <bmon/element.h>
#include <bmon/unit.h>
#include <bmon/input.h>
#include <bmon/utils.h>
#if 0
#define MAX_POLICY 255
static char * allowed_attrs[MAX_POLICY];
static char * denied_attrs[MAX_POLICY];
static int allow_all_attrs;
#endif
static LIST_HEAD(attr_def_list);
static int attr_id_gen = 1;
struct attr_def *attr_def_lookup(const char *name)
{
struct attr_def *def;
list_for_each_entry(def, &attr_def_list, ad_list)
if (!strcmp(name, def->ad_name))
return def;
return NULL;
}
struct attr_def *attr_def_lookup_id(int id)
{
struct attr_def *def;
list_for_each_entry(def, &attr_def_list, ad_list)
if (def->ad_id == id)
return def;
return NULL;
}
#if 0
int foreach_attr_type(int (*cb)(struct attr_type *, void *), void *arg)
{
int i, err = 0;
for (i = 0; i < NAME_HASHSIZ; i++) {
struct attr_type *t;
for (t = attr_ht_name[i]; t; t = t->at_next_name) {
err = cb(t, arg);
if (err < 0)
break;
}
}
return err;
}
#endif
int attr_def_add(const char *name, const char *desc, struct unit *unit,
int type, int flags)
{
struct attr_def *def;
if ((def = attr_def_lookup(name)))
return def->ad_id;
def = xcalloc(1, sizeof(*def));
def->ad_id = attr_id_gen++;
def->ad_name = strdup(name);
def->ad_description = strdup(desc ? : "");
def->ad_type = type;
def->ad_unit = unit;
def->ad_flags = flags;
list_add_tail(&def->ad_list, &attr_def_list);
DBG("New attribute %s desc=\"%s\" unit=%s type=%d",
def->ad_name, def->ad_description, def->ad_unit->u_name, type);
return def->ad_id;
}
void attr_def_free(struct attr_def *def)
{
if (!def)
return;
xfree(def->ad_name);
xfree(def->ad_description);
xfree(def);
}
int attr_map_load(struct attr_map *map, size_t size)
{
int i, nfailed = 0;
for (i = 0; i < size; i++) {
struct attr_map *m = &map[i];
struct unit *u;
if (!(u = unit_lookup(m->unit))) {
nfailed++;
continue;
}
m->attrid = attr_def_add(m->name, m->description, u,
m->type, m->flags);
}
return nfailed;
}
static inline unsigned int attr_hash(int id)
{
return id % ATTR_HASH_SIZE;
}
struct attr *attr_lookup(const struct element *e, int id)
{
unsigned int hash = attr_hash(id);
struct attr *attr;
list_for_each_entry(attr, &e->e_attrhash[hash], a_list)
if (attr->a_def->ad_id == id)
return attr;
return NULL;
}
static int collect_history(struct element *e, struct attr_def *def)
{
int n;
if (def->ad_flags & ATTR_FORCE_HISTORY)
return 1;
for (n = 0; n <= GT_MAX; n++)
if (e->e_key_attr[n] == def)
return 1;
return 0;
#if 0
if (!allowed_attrs[0] && !denied_attrs[0]) {
if (!strcmp(ad->ad_name, "bytes") ||
!strcmp(ad->ad_name, "packets"))
return 1;
else
return 0;
}
if (!allowed_attrs[0]) {
for (n = 0; n < MAX_POLICY && denied_attrs[n]; n++)
if (!strcasecmp(denied_attrs[n], ad->ad_name))
return 0;
return 1;
}
for (n = 0; n < MAX_POLICY && denied_attrs[n]; n++)
if (!strcasecmp(denied_attrs[n], ad->ad_name))
return 0;
for (n=0; n < MAX_POLICY && allowed_attrs[n]; n++)
if (!strcasecmp(allowed_attrs[n], ad->ad_name))
return 1;
#endif
}
#if 0
void attr_parse_policy(const char *policy)
{
static int set = 0;
int i, a = 0, d = 0, f = 0;
char *p, *s;
if (set)
return;
set = 1;
if (!strcasecmp(policy, "all")) {
allow_all_attrs = 1;
return ;
}
s = strdup(policy);
for (i = 0, p = s; ; i++) {
if (s[i] == ',' || s[i] == '\0') {
f = s[i] == '\0' ? 1 : 0;
s[i] = '\0';
if ('!' == *p) {
if (d > (MAX_POLICY - 1))
break;
denied_attrs[d++] = strdup(++p);
} else {
if(a > (MAX_POLICY - 1))
break;
allowed_attrs[a++] = strdup(p);
}
if (f)
break;
p = &s[i+1];
}
}
xfree(s);
}
#endif
void attr_start_collecting_history(struct attr *attr)
{
if (attr->a_flags & ATTR_DOING_HISTORY)
return;
DBG("Starting to collect history for attribute %s",
attr->a_def->ad_name);
history_attach(attr);
attr->a_flags |= ATTR_DOING_HISTORY;
}
static int attrcmp(struct element *e, struct attr *a, struct attr *b)
{
/* major key attribute is always first */
if (e->e_key_attr[GT_MAJOR] == b->a_def)
return 1;
/* minor key attribte is always second */
if (e->e_key_attr[GT_MINOR] == b->a_def)
return (e->e_key_attr[GT_MAJOR] == a->a_def) ? -1 : 1;
/* otherwise sort by alphabet */
return strcasecmp(a->a_def->ad_description, b->a_def->ad_description);
}
void attr_update(struct element *e, int id, uint64_t rx, uint64_t tx, int flags)
{
struct attr *attr, *n;
int update_ts = 0;
if (!(attr = attr_lookup(e, id))) {
unsigned int hash = attr_hash(id);
struct attr_def *def;
if (!(def = attr_def_lookup_id(id)))
return;
DBG("Tracking new attribute %d (\"%s\") of element %s",
def->ad_id, def->ad_name, e->e_name);
attr = xcalloc(1, sizeof(*attr));
attr->a_def = def;
attr->a_flags = def->ad_flags;
init_list_head(&attr->a_history_list);
if (collect_history(e, def))
attr_start_collecting_history(attr);
list_add_tail(&attr->a_list, &e->e_attrhash[hash]);
e->e_nattrs++;
list_for_each_entry(n, &e->e_attr_sorted, a_sort_list) {
if (attrcmp(e, attr, n) < 0) {
list_add_tail(&attr->a_sort_list,
&n->a_sort_list);
goto inserted;
}
}
list_add_tail(&attr->a_sort_list, &e->e_attr_sorted);
}
inserted:
if (flags & UPDATE_FLAG_RX) {
attr->a_rx_rate.r_current = rx;
attr->a_flags |= ATTR_RX_ENABLED;
update_ts = 1;
}
if (flags & UPDATE_FLAG_TX) {
attr->a_tx_rate.r_current = tx;
attr->a_flags |= ATTR_TX_ENABLED;
update_ts = 1;
}
if (update_ts)
update_timestamp(&attr->a_last_update);
DBG("Updated attribute %d (\"%s\") of element %s", id, attr->a_def->ad_name, e->e_name);
}
void attr_free(struct attr *a)
{
struct history *h, *n;
list_for_each_entry_safe(h, n, &a->a_history_list, h_list)
history_free(h);
list_del(&a->a_list);
xfree(a);
}
void attr_rate2float(struct attr *a, double *rx, char **rxu, int *rxprec,
double *tx, char **txu, int *txprec)
{
struct unit *u = a->a_def->ad_unit;
*rx = unit_value2str(a->a_rx_rate.r_rate, u, rxu, rxprec);
*tx = unit_value2str(a->a_tx_rate.r_rate, u, txu, txprec);
}
struct attr *attr_select_first(void)
{
struct element *e;
if (!(e = element_current()))
return NULL;
if (list_empty(&e->e_attr_sorted))
e->e_current_attr = NULL;
else
e->e_current_attr = list_first_entry(&e->e_attr_sorted,
struct attr, a_sort_list);
return e->e_current_attr;
}
struct attr *attr_select_last(void)
{
struct element *e;
if (!(e = element_current()))
return NULL;
if (list_empty(&e->e_attr_sorted))
e->e_current_attr = NULL;
else
e->e_current_attr = list_entry(&e->e_attr_sorted,
struct attr, a_sort_list);
return e->e_current_attr;
}
struct attr *attr_select_next(void)
{
struct element *e;
struct attr *a;
if (!(e = element_current()))
return NULL;
if (!(a = e->e_current_attr))
return attr_select_first();
if (a->a_sort_list.next != &e->e_attr_sorted)
e->e_current_attr = list_entry(a->a_sort_list.next,
struct attr, a_sort_list);
else
return attr_select_first();
return e->e_current_attr;
}
struct attr *attr_select_prev(void)
{
struct element *e;
struct attr *a;
if (!(e = element_current()))
return NULL;
if (!(a = e->e_current_attr))
return attr_select_last();
if (a->a_sort_list.prev != &e->e_attr_sorted)
e->e_current_attr = list_entry(a->a_sort_list.prev,
struct attr, a_sort_list);
else
return attr_select_last();
return e->e_current_attr;
}
struct attr *attr_current(void)
{
struct element *e;
if (!(e = element_current()))
return NULL;
if (!e->e_current_attr)
return attr_select_first();
return e->e_current_attr;
}
#if 0
int __first_attr(struct item *item, int graph)
{
int i;
struct attr *a;
for (i = 0; i < ATTR_HASH_MAX; i++) {
for (a = item->i_attrs[i]; a; a = a->a_next) {
if (a->a_flags & ATTR_FLAG_HISTORY) {
item->i_attr_sel[graph] = a;
return 0;
}
}
}
return EMPTY_LIST;
}
int __next_attr(struct item *item, int graph)
{
int hash;
struct attr *attr, *next;
if (item->i_attr_sel[graph] == NULL)
return __first_attr(item, graph);
attr = item->i_attr_sel[graph];
hash = attr_hash(attr->a_def->ad_id);
next = attr->a_next;
if (next == NULL)
hash++;
for (; hash < ATTR_HASH_MAX; hash++) {
if (next) {
attr = next;
next = NULL;
} else
attr = item->i_attrs[hash];
for (; attr; attr = attr->a_next) {
if (!(attr->a_flags & ATTR_FLAG_HISTORY))
continue;
item->i_attr_sel[graph] = attr;
return 0;
}
}
return __first_attr(item, graph);
}
struct attr *attr_current(struct item *item, int graph)
{
if (item->i_attr_sel[graph] == NULL)
__first_attr(item, graph);
return item->i_attr_sel[graph];
}
int attr_first(void)
{
struct item *item = item_current();
if (item == NULL)
return EMPTY_LIST;
return __first_attr(item, item->i_graph_sel);
}
int attr_next(void)
{
struct item *item = item_current();
if (item == NULL)
return EMPTY_LIST;
return __next_attr(item, item->i_graph_sel);
}
#endif
static float __calc_usage(double rate, uint64_t max)
{
if (!max)
return FLT_MAX;
if (!rate)
return 0.0f;
return 100.0f / ((double) max / (rate * cfg_rate_interval));
}
void attr_calc_usage(struct attr *a, float *rx, float *tx,
uint64_t rxmax, uint64_t txmax)
{
if (a->a_def->ad_type == ATTR_TYPE_PERCENT) {
*rx = a->a_rx_rate.r_total;
*tx = a->a_tx_rate.r_total;
} else {
*rx = __calc_usage(a->a_rx_rate.r_rate, rxmax);
*tx = __calc_usage(a->a_tx_rate.r_rate, txmax);
}
}
static void calc_counter_rate(struct attr *a, struct rate *rate,
timestamp_t *ts)
{
uint64_t delta, prev_total;
float diff, old_rate;
if (rate->r_current < rate->r_prev) {
/* Overflow detected */
if (a->a_flags & ATTR_IGNORE_OVERFLOWS)
delta = rate->r_current;
else {
if (a->a_flags & ATTR_TRUE_64BIT)
delta = 0xFFFFFFFFFFFFFFFFULL - rate->r_prev;
else
delta = 0xFFFFFFFFULL - rate->r_prev;
delta += rate->r_current + 1;
}
} else
delta = rate->r_current - rate->r_prev;
prev_total = rate->r_total;
rate->r_total += delta;
rate->r_prev = rate->r_current;
if (!prev_total) {
/*
* No previous records exists, reset time to now to
* avoid doing unnecessary calculation, this behaviour
* continues as long as the counter stays 0.
*/
goto out;
}
diff = timestamp_diff(&rate->r_last_calc, ts);
if (diff < (cfg_rate_interval - cfg_rate_variance))
return;
old_rate = rate->r_rate;
if (rate->r_total < prev_total) {
/* overflow */
delta = 0xFFFFFFFFFFFFFFFFULL - prev_total;
delta += rate->r_total + 1;
} else
delta = rate->r_total - prev_total;
rate->r_rate = delta / diff;
if (old_rate)
rate->r_rate = ((rate->r_rate * 3.0f) + old_rate) / 4.0f;
out:
copy_timestamp(&rate->r_last_calc, ts);
}
static void calc_rate_total(struct attr *a, struct rate *rate, timestamp_t *ts)
{
rate->r_prev = rate->r_rate = rate->r_total = rate->r_current;
copy_timestamp(&rate->r_last_calc, ts);
}
void attr_notify_update(struct attr *a, timestamp_t *ts)
{
switch (a->a_def->ad_type) {
case ATTR_TYPE_RATE:
case ATTR_TYPE_PERCENT:
calc_rate_total(a, &a->a_rx_rate, ts);
calc_rate_total(a, &a->a_tx_rate, ts);
break;
case ATTR_TYPE_COUNTER:
calc_counter_rate(a, &a->a_rx_rate, ts);
calc_counter_rate(a, &a->a_tx_rate, ts);
break;
default:
DBG("Attribute update of unknown type");
break;
}
if (a->a_flags & ATTR_DOING_HISTORY) {
struct history *h;
DBG("Updating history of attribute %d (\"%s\")", a->a_def->ad_id, a->a_def->ad_name);
list_for_each_entry(h, &a->a_history_list, h_list)
history_update(a, h, ts);
}
}
static void __exit attr_exit(void)
{
struct attr_def *ad, *n;
list_for_each_entry_safe(ad, n, &attr_def_list, ad_list)
attr_def_free(ad);
}

397
src/bmon.c Normal file
View File

@ -0,0 +1,397 @@
/*
* src/bmon.c Bandwidth Monitor
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/attr.h>
#include <bmon/utils.h>
#include <bmon/input.h>
#include <bmon/output.h>
#include <bmon/module.h>
#include <bmon/group.h>
int start_time;
int do_quit = 0;
int is_daemon = 0;
struct reader_timing rtiming;
static char *usage_text =
"Usage: bmon [OPTION]...\n" \
"\n" \
"Options:\n" \
"Startup:\n" \
" -i, --input=MODPARM Input module(s)\n" \
" -o, --output=MODPARM Ouptut module(s)\n" \
" -f, --configfile=PATH Alternative path to configuration file\n" \
" -h, --help Show this help text\n" \
" -V, --version Show version\n" \
"\n" \
"Input:\n" \
" -p, --policy=POLICY Element display policy (see below)\n" \
" -a, --show-all Show all elements (even disabled elements)\n" \
" -r, --read-interval=FLOAT Read interval in seconds (float)\n" \
" -R, --rate-internval=FLOAT Rate interval in seconds (float)\n" \
" -s, --sleep-interval=FLOAT Sleep time in seconds (float)\n" \
" -L, --lifetime=LIFETIME Lifetime of an element in seconds (float)\n" \
"\n" \
"Output:\n" \
" -U, --use-si Use SI units\n" \
"\n" \
"Module configuration:\n" \
" modparm := MODULE:optlist,MODULE:optlist,...\n" \
" optlist := option;option;...\n" \
" option := TYPE[=VALUE]\n" \
"\n" \
" Examples:\n" \
" -o curses:ngraph=2\n" \
" -o list # Shows a list of available modules\n" \
" -o curses:help # Shows a help text for html module\n" \
"\n" \
"Interface selection:\n" \
" policy := [!]simple_regexp,[!]simple_regexp,...\n" \
"\n" \
" Example: -p 'eth*,lo*,!eth1'\n" \
"\n" \
"Please see the bmon(1) man pages for full documentation.\n";
static void do_shutdown(void)
{
static int done;
if (!done) {
done = 1;
module_shutdown();
}
}
RETSIGTYPE sig_int(int unused)
{
if (do_quit)
exit(-1);
do_quit = 1;
}
void sig_exit(void)
{
do_shutdown();
}
void quit(const char *fmt, ...)
{
static int done;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (!done) {
done = 1;
do_shutdown();
}
exit(1);
}
static inline void print_version(void)
{
printf("bmon %s\n", PACKAGE_VERSION);
printf("Copyright (C) 2001-2013 by Thomas Graf <tgraf@suug.ch>\n");
printf("Copyright (C) 2013 Red Hat, Inc.\n");
printf("bmon comes with ABSOLUTELY NO WARRANTY. This is free " \
"software, and you\nare welcome to redistribute it under " \
"certain conditions. See the source\ncode for details.\n");
}
static void parse_args_pre(int argc, char *argv[])
{
DBG("Parsing arguments pre state");
for (;;)
{
char *gostr = "+:hvVf:";
struct option long_opts[] = {
{"help", 0, 0, 'h'},
{"version", 0, 0, 'v'},
{"configfile", 1, 0, 'f'},
{0, 0, 0, 0},
};
int c = getopt_long(argc, argv, gostr, long_opts, NULL);
if (c == -1)
break;
switch (c)
{
case 'f':
set_configfile(optarg);
break;
case 'h':
print_version();
printf("\n%s", usage_text);
exit(1);
case 'V':
case 'v':
print_version();
exit(0);
}
}
}
static int parse_args_post(int argc, char *argv[])
{
DBG("Parsing arguments post state");
optind = 1;
for (;;)
{
char *gostr = "i:o:p:r:R:s:aU" \
"L:hvVf:";
struct option long_opts[] = {
{"input", 1, 0, 'i'},
{"output", 1, 0, 'o'},
{"policy", 1, 0, 'p'},
{"read-interval", 1, 0, 'r'},
{"rate-interval", 1, 0, 'R'},
{"sleep-interval", 1, 0, 's'},
{"show-all", 0, 0, 'a'},
{"use-si", 0, 0, 'U'},
{"lifetime", 1, 0, 'L'},
{0, 0, 0, 0},
};
int c = getopt_long(argc, argv, gostr, long_opts, NULL);
if (c == -1)
break;
switch (c)
{
case 'i':
if (input_set(optarg))
return 1;
break;
case 'o':
if (output_set(optarg))
return 1;
break;
case 'p':
cfg_setstr(cfg, "policy", optarg);
break;
case 'r':
cfg_setfloat(cfg, "read_interval", strtod(optarg, NULL));
break;
case 'R':
cfg_setfloat(cfg, "rate_interval", strtod(optarg, NULL));
break;
case 's':
cfg_setint(cfg, "sleep_time", strtoul(optarg, NULL, 0));
break;
case 'a':
cfg_setint(cfg, "show_all", 1);
break;
case 'U':
cfg_setbool(cfg, "use_si", cfg_true);
break;
case 'L':
cfg_setint(cfg, "lifetime", strtoul(optarg, NULL, 0));
break;
case 'f':
/* Already handled in pre getopt loop */
break;
default:
quit("Aborting...\n");
break;
}
}
return 0;
}
#if 0
static void calc_variance(timestamp_t *c, timestamp_t *ri)
{
float v = (ts_to_float(c) / ts_to_float(ri)) * 100.0f;
rtiming.rt_variance.v_error = v;
rtiming.rt_variance.v_total += v;
if (v > rtiming.rt_variance.v_max)
rtiming.rt_variance.v_max = v;
if (v < rtiming.rt_variance.v_min)
rtiming.rt_variance.v_min = v;
}
#endif
int main(int argc, char *argv[])
{
unsigned long sleep_time;
double read_interval;
start_time = time(0);
memset(&rtiming, 0, sizeof(rtiming));
rtiming.rt_variance.v_min = FLT_MAX;
/*
* Early initialization before reading config
*/
conf_init_pre();
parse_args_pre(argc, argv);
/*
* Reading the configuration file */
configfile_read();
/*
* Late initialization after reading config
*/
if (parse_args_post(argc, argv))
return 1;
conf_init_post();
module_init();
read_interval = cfg_read_interval;
sleep_time = cfg_getint(cfg, "sleep_time");
if (((double) sleep_time / 1000000.0f) > read_interval)
sleep_time = (unsigned long) (read_interval * 1000000.0f);
DBG("Entering mainloop...");
do {
/*
* E := Elapsed time
* NR := Next Read
* LR := Last Read
* RI := Read Interval
* ST := Sleep Time
* C := Correction
*/
timestamp_t e, ri, tmp;
unsigned long st;
float_to_timestamp(&ri, read_interval);
/*
* NR := NOW
*/
update_timestamp(&rtiming.rt_next_read);
for (;;) {
output_pre();
/*
* E := NOW
*/
update_timestamp(&e);
/*
* IF NR <= E THEN
*/
if (timestamp_le(&rtiming.rt_next_read, &e)) {
timestamp_t c;
/*
* C := (NR - E)
*/
timestamp_sub(&c, &rtiming.rt_next_read, &e);
//calc_variance(&c, &ri);
/*
* LR := E
*/
copy_timestamp(&rtiming.rt_last_read, &e);
/*
* NR := E + RI + C
*/
timestamp_add(&rtiming.rt_next_read, &e, &ri);
timestamp_add(&rtiming.rt_next_read,
&rtiming.rt_next_read, &c);
reset_update_flags();
input_read();
free_unused_elements();
output_draw();
output_post();
}
if (do_quit)
exit(0);
/*
* ST := Configured ST
*/
st = sleep_time;
/*
* IF (NR - E) < ST THEN
*/
timestamp_sub(&tmp, &rtiming.rt_next_read, &e);
if (tmp.tv_sec < 0)
continue;
if (tmp.tv_sec == 0 && tmp.tv_usec < st) {
if (tmp.tv_usec < 0)
continue;
/*
* ST := (NR - E)
*/
st = tmp.tv_usec;
}
/*
* SLEEP(ST)
*/
usleep(st);
}
} while (0);
return 0; /* buddha says i'll never be reached */
}
static void __init bmon_init(void)
{
atexit(&sig_exit);
//signal(SIGINT, &sig_int);
}

596
src/conf.c Normal file
View File

@ -0,0 +1,596 @@
/*
* conf.c Config Crap
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/unit.h>
#include <bmon/attr.h>
#include <bmon/element.h>
#include <bmon/element_cfg.h>
#include <bmon/history.h>
#include <bmon/utils.h>
cfg_t *cfg;
static cfg_opt_t element_opts[] = {
CFG_STR("description", NULL, CFGF_NONE),
CFG_BOOL("show", cfg_true, CFGF_NONE),
CFG_INT("rxmax", 0, CFGF_NONE),
CFG_INT("txmax", 0, CFGF_NONE),
CFG_INT("max", 0, CFGF_NONE),
CFG_END()
};
static cfg_opt_t history_opts[] = {
CFG_FLOAT("interval", 1.0f, CFGF_NONE),
CFG_INT("size", 60, CFGF_NONE),
CFG_STR("type", "64bit", CFGF_NONE),
CFG_END()
};
static cfg_opt_t attr_opts[] = {
CFG_STR("description", "", CFGF_NONE),
CFG_STR("unit", "", CFGF_NONE),
CFG_STR("type", "counter", CFGF_NONE),
CFG_BOOL("history", cfg_false, CFGF_NONE),
CFG_END()
};
static cfg_opt_t variant_opts[] = {
CFG_FLOAT_LIST("div", "{}", CFGF_NONE),
CFG_STR_LIST("txt", "", CFGF_NONE),
CFG_END()
};
static cfg_opt_t unit_opts[] = {
CFG_SEC("variant", variant_opts, CFGF_MULTI | CFGF_TITLE),
CFG_END()
};
static cfg_opt_t global_opts[] = {
CFG_FLOAT("read_interval", 1.0f, CFGF_NONE),
CFG_FLOAT("rate_interval", 1.0f, CFGF_NONE),
CFG_FLOAT("lifetime", 30.0f, CFGF_NONE),
CFG_FLOAT("history_variance", 0.1f, CFGF_NONE),
CFG_FLOAT("variance", 0.1f, CFGF_NONE),
CFG_BOOL("show_all", cfg_false, CFGF_NONE),
CFG_INT("unit_exp", -1, CFGF_NONE),
CFG_INT("sleep_time", 20000UL, CFGF_NONE),
CFG_BOOL("use_si", 0, CFGF_NONE),
CFG_STR("uid", NULL, CFGF_NONE),
CFG_STR("gid", NULL, CFGF_NONE),
CFG_STR("policy", "", CFGF_NONE),
CFG_SEC("unit", unit_opts, CFGF_MULTI | CFGF_TITLE),
CFG_SEC("attr", attr_opts, CFGF_MULTI | CFGF_TITLE),
CFG_SEC("history", history_opts, CFGF_MULTI | CFGF_TITLE),
CFG_SEC("element", element_opts, CFGF_MULTI | CFGF_TITLE),
};
float cfg_read_interval;
float cfg_rate_interval;
float cfg_rate_variance;
float cfg_history_variance;
int cfg_show_all;
int cfg_unit_exp = DYNAMIC_EXP;
static char * configfile = NULL;
#if defined HAVE_CURSES
#if defined HAVE_USE_DEFAULT_COLORS
struct layout cfg_layout[] =
{
{-1, -1, 0}, /* dummy, not used */
{-1, -1, 0}, /* default */
{-1, -1, A_REVERSE}, /* statusbar */
{-1, -1, 0}, /* header */
{-1, -1, 0}, /* list */
{-1, -1, A_REVERSE}, /* selected */
};
#else
struct layout cfg_layout[] =
{
{0, 0, 0}, /* dummy, not used */
{COLOR_BLACK, COLOR_WHITE, 0}, /* default */
{COLOR_BLACK, COLOR_WHITE, A_REVERSE}, /* statusbar */
{COLOR_BLACK, COLOR_WHITE, 0}, /* header */
{COLOR_BLACK, COLOR_WHITE, 0}, /* list */
{COLOR_BLACK, COLOR_WHITE, A_REVERSE}, /* selected */
};
#endif
#endif
tv_t * parse_tv(char *data)
{
char *value;
tv_t *tv = xcalloc(1, sizeof(tv_t));
init_list_head(&tv->tv_list);
value = strchr(data, '=');
if (value) {
*value = '\0';
++value;
tv->tv_value = strdup(value);
}
tv->tv_type = strdup(data);
return tv;
}
module_conf_t * parse_module(char *data)
{
char *name = data, *opts = data, *next;
module_conf_t *m;
if (!*name)
quit("No module name given");
m = xcalloc(1, sizeof(module_conf_t));
init_list_head(&m->m_attrs);
opts = strchr(data, ':');
if (opts) {
*opts = '\0';
opts++;
do {
tv_t *tv;
next = strchr(opts, ';');
if (next) {
*next = '\0';
++next;
}
tv = parse_tv(opts);
list_add_tail(&tv->tv_list, &m->m_attrs);
opts = next;
} while(next);
}
m->m_name = strdup(name);
return m;
}
int parse_module_param(const char *data, struct list_head *list)
{
char *buf = strdup(data);
char *next;
char *current = buf;
module_conf_t *m;
int n = 0;
do {
next = strchr(current, ',');
if (next) {
*next = '\0';
++next;
}
m = parse_module(current);
if (m) {
list_add_tail(&m->m_list, list);
n++;
}
current = next;
} while (next);
free(buf);
return n;
}
static void configfile_read_history(void)
{
int i, nhistory;
nhistory = cfg_size(cfg, "history");
for (i = 0; i < nhistory; i++) {
struct history_def *def;
cfg_t *history;
const char *name, *type;
float interval;
int size;
if (!(history = cfg_getnsec(cfg, "history", i)))
BUG();
if (!(name = cfg_title(history)))
BUG();
interval = cfg_getfloat(history, "interval");
size = cfg_getint(history, "size");
type = cfg_getstr(history, "type");
if (interval == 0.0f)
interval = cfg_getfloat(cfg, "read_interval");
def = history_def_alloc(name);
def->hd_interval = interval;
def->hd_size = size;
if (!strcasecmp(type, "8bit"))
def->hd_type = HISTORY_TYPE_8;
else if (!strcasecmp(type, "16bit"))
def->hd_type = HISTORY_TYPE_16;
else if (!strcasecmp(type, "32bit"))
def->hd_type = HISTORY_TYPE_32;
else if (!strcasecmp(type, "64bit"))
def->hd_type = HISTORY_TYPE_64;
else
quit("Invalid type \'%s\', must be \"(8|16|32|64)bit\""
" in history definition #%d\n", type, i+1);
}
}
static void configfile_read_element_cfg(void)
{
int i, nelement;
nelement = cfg_size(cfg, "element");
for (i = 0; i < nelement; i++) {
struct element_cfg *ec;
cfg_t *element;
const char *name, *description;
long max;
if (!(element = cfg_getnsec(cfg, "element", i)))
BUG();
if (!(name = cfg_title(element)))
BUG();
ec = element_cfg_alloc(name);
if ((description = cfg_getstr(element, "description")))
ec->ec_description = strdup(description);
if ((max = cfg_getint(element, "max")))
ec->ec_rxmax = ec->ec_txmax = max;
if ((max = cfg_getint(element, "rxmax")))
ec->ec_rxmax = max;
if ((max = cfg_getint(element, "txmax")))
ec->ec_txmax = max;
if (cfg_getbool(element, "show"))
ec->ec_flags |= ELEMENT_CFG_SHOW;
else
ec->ec_flags |= ELEMENT_CFG_HIDE;
}
}
static void add_div(struct unit *unit, int type, cfg_t *variant)
{
int ndiv, n, ntxt;
if (!(ndiv = cfg_size(variant, "div")))
return;
ntxt = cfg_size(variant, "txt");
if (ntxt != ndiv)
quit("Number of elements for div and txt not equal\n");
if (!list_empty(&unit->u_div[type])) {
struct fraction *f, *n;
list_for_each_entry_safe(f, n, &unit->u_div[type], f_list)
fraction_free(f);
}
for (n = 0; n < ndiv; n++) {
char *txt;
float div;
div = cfg_getnfloat(variant, "div", n);
txt = cfg_getnstr(variant, "txt", n);
unit_add_div(unit, type, txt, div);
}
}
static void configfile_read_units(void)
{
int i, nunits;
struct unit *u;
nunits = cfg_size(cfg, "unit");
for (i = 0; i < nunits; i++) {
int nvariants, n;
cfg_t *unit;
const char *name;
if (!(unit = cfg_getnsec(cfg, "unit", i)))
BUG();
if (!(name = cfg_title(unit)))
BUG();
if (!(nvariants = cfg_size(unit, "variant")))
continue;
if (!(u = unit_add(name)))
continue;
for (n = 0; n < nvariants; n++) {
cfg_t *variant;
const char *vtitle;
if (!(variant = cfg_getnsec(unit, "variant", n)))
BUG();
if (!(vtitle = cfg_title(variant)))
BUG();
if (!strcasecmp(vtitle, "default"))
add_div(u, UNIT_DEFAULT, variant);
else if (!strcasecmp(vtitle, "si"))
add_div(u, UNIT_SI, variant);
else
quit("Unknown unit variant \'%s\'\n", vtitle);
}
}
}
static void configfile_read_attrs(void)
{
int i, nattrs, t;
nattrs = cfg_size(cfg, "attr");
for (i = 0; i < nattrs; i++) {
struct unit *u;
cfg_t *attr;
const char *name, *description, *unit, *type;
int flags = 0;
if (!(attr = cfg_getnsec(cfg, "attr", i)))
BUG();
if (!(name = cfg_title(attr)))
BUG();
description = cfg_getstr(attr, "description");
unit = cfg_getstr(attr, "unit");
type = cfg_getstr(attr, "type");
if (!unit)
quit("Attribute '%s' is missing unit specification\n",
name);
if (!type)
quit("Attribute '%s' is missing type specification\n",
name);
if (!(u = unit_lookup(unit)))
quit("Unknown unit \'%s\' attribute '%s'\n",
unit, name);
if (!strcasecmp(type, "counter"))
t = ATTR_TYPE_COUNTER;
else if (!strcasecmp(type, "rate"))
t = ATTR_TYPE_RATE;
else if (!strcasecmp(type, "percent"))
t = ATTR_TYPE_PERCENT;
else
quit("Unknown type \'%s\' in attribute '%s'\n",
type, name);
if (cfg_getbool(attr, "history"))
flags |= ATTR_FORCE_HISTORY;
if (cfg_getbool(attr, "ignore_overflows"))
flags |= ATTR_IGNORE_OVERFLOWS;
attr_def_add(name, description, u, t, flags);
}
}
static void conf_read(const char *path, int must)
{
int err;
DBG("Reading configfile %s...", path);
if (access(path, R_OK) != 0) {
if (must)
quit("Error: Unable to read configfile \"%s\": %s\n",
path, strerror(errno));
else
return;
}
err = cfg_parse(cfg, path);
if (err == CFG_FILE_ERROR) {
quit("Error while reading configfile \"%s\": %s\n",
path, strerror(errno));
} else if (err == CFG_PARSE_ERROR) {
quit("Error while reading configfile \"%s\": parse error\n",
path);
}
configfile_read_units();
configfile_read_history();
configfile_read_attrs();
configfile_read_element_cfg();
}
static const char default_config[] = \
"unit byte {" \
" variant default {" \
" div = { 1, 1024, 1048576, 1073741824, 1099511627776}" \
" txt = { \"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\" }" \
" }" \
" variant si {" \
" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
" txt = { \"B\", \"KB\", \"MB\", \"GB\", \"TB\" }" \
" }" \
" }" \
"unit bit {" \
" variant default {" \
" div = { 1, 1024, 1048576, 1073741824, 1099511627776}" \
" txt = { \"b\", \"Kib\", \"Mib\", \"Gib\", \"TiB\" }" \
" }" \
" variant si {" \
" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
" txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
" }" \
"}" \
"unit number {" \
" variant default {" \
" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
" txt = { \"\", \"K\", \"M\", \"G\", \"T\" }" \
" }" \
"}" \
"unit percent {" \
" variant default {" \
" div = { 1. }" \
" txt = { \"%\" }" \
" }" \
"}" \
"history second {" \
" interval = 1.0" \
" size = 60" \
"}" \
"history minute {" \
" interval = 60.0" \
" size = 60" \
"}" \
"history hour {" \
" interval = 3600.0" \
" size = 60" \
"}" \
"history day {" \
" interval = 86400.0" \
" size = 60" \
"}";
static void conf_read_default(void)
{
int err;
DBG("Reading default config");
err = cfg_parse_buf(cfg, default_config);
if (err)
quit("Error while parsing default config\n");
configfile_read_units();
configfile_read_history();
configfile_read_attrs();
configfile_read_element_cfg();
}
void configfile_read(void)
{
if (configfile)
conf_read(configfile, 1);
else {
conf_read(SYSCONFDIR "/bmonrc", 0);
if (getenv("HOME")) {
char path[FILENAME_MAX+1];
snprintf(path, sizeof(path), "%s/.bmonrc",
getenv("HOME"));
conf_read(path, 0);
}
}
}
void conf_init_pre(void)
{
conf_read_default();
}
void conf_init_post(void)
{
cfg_read_interval = cfg_getfloat(cfg, "read_interval");
cfg_rate_interval = cfg_getfloat(cfg, "rate_interval");
cfg_rate_variance = cfg_getfloat(cfg, "variance") * cfg_rate_interval;
cfg_history_variance = cfg_getfloat(cfg, "history_variance");
cfg_show_all = cfg_getbool(cfg, "show_all");
cfg_unit_exp = cfg_getint(cfg, "unit_exp");
element_parse_policy(cfg_getstr(cfg, "policy"));
}
void set_configfile(const char *file)
{
static int set = 0;
if (!set) {
configfile = strdup(file);
set = 1;
}
}
void set_unit_exp(const char *name)
{
if (tolower(*name) == 'b')
cfg_setint(cfg, "unit_exp", 0);
else if (tolower(*name) == 'k')
cfg_setint(cfg, "unit_exp", 1);
else if (tolower(*name) == 'm')
cfg_setint(cfg, "unit_exp", 2);
else if (tolower(*name) == 'g')
cfg_setint(cfg, "unit_exp", 3);
else if (tolower(*name) == 't')
cfg_setint(cfg, "unit_exp", 4);
else if (tolower(*name) == 'd')
cfg_setint(cfg, "unit_exp", DYNAMIC_EXP);
else
quit("Unknown unit exponent '%s'\n", name);
}
unsigned int get_lifecycles(void)
{
return (unsigned int)
(cfg_getfloat(cfg, "lifetime") / cfg_getfloat(cfg, "read_interval"));
}
static void __exit conf_shutdown(void)
{
cfg_free(cfg);
}
static void __init __conf_init(void)
{
DBG("init");
cfg = cfg_init(global_opts, CFGF_NOCASE);
/* FIXME: Add validation functions */
//cfg_set_validate_func(cfg, "bookmark", &cb_validate_bookmark);
}

537
src/element.c Normal file
View File

@ -0,0 +1,537 @@
/*
* src/element.c Elements
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/element.h>
#include <bmon/element_cfg.h>
#include <bmon/group.h>
#include <bmon/input.h>
#include <bmon/utils.h>
static LIST_HEAD(allowed);
static LIST_HEAD(denied);
static int match_mask(const struct policy *p, const char *str)
{
int i, n;
char c;
if (!p || !str)
return 0;
for (i = 0, n = 0; p->p_rule[i] != '\0'; i++) {
if (p->p_rule[i] == '*') {
c = tolower(p->p_rule[i + 1]);
if (c == '\0')
return 1;
while (tolower(str[n]) != c)
if (str[n++] == '\0')
return 0;
} else if (tolower(p->p_rule[i]) != tolower(str[n++]))
return 0;
}
return str[n] == '\0' ? 1 : 0;
}
int element_allowed(const char *name, struct element_cfg *cfg)
{
struct policy *p;
if (cfg) {
if (cfg->ec_flags & ELEMENT_CFG_HIDE)
return 0;
else if (cfg->ec_flags & ELEMENT_CFG_SHOW)
return 1;
}
list_for_each_entry(p, &denied, p_list)
if (match_mask(p, name))
return 0;
if (!list_empty(&allowed)) {
list_for_each_entry(p, &allowed, p_list)
if (match_mask(p, name))
return 1;
return 0;
}
return 1;
}
void element_parse_policy(const char *policy)
{
char *start, *copy, *save = NULL, *tok;
struct policy *p;
if (!policy)
return;
copy = strdup(policy);
start = copy;
while ((tok = strtok_r(start, ",", &save)) != NULL) {
start = NULL;
p = xcalloc(1, sizeof(*p));
if (*tok == '!') {
p->p_rule = strdup(++tok);
list_add_tail(&p->p_list, &denied);
} else {
p->p_rule = strdup(tok);
list_add_tail(&p->p_list, &allowed);
}
}
xfree(copy);
}
struct element *__lookup_element(struct element_group *group, const char *name,
uint32_t id, struct element *parent)
{
struct list_head *list;
struct element *e;
if (parent)
list = &parent->e_childs;
else
list = &group->g_elements;
list_for_each_entry(e, list, e_list)
if (!strcmp(name, e->e_name) && e->e_id == id)
return e;
return NULL;
}
struct element *element_lookup(struct element_group *group, const char *name,
uint32_t id, struct element *parent, int flags)
{
struct element_cfg *cfg;
struct element *e;
int i;
if (!group)
BUG();
if ((e = __lookup_element(group, name, id, parent)))
return e;
if (!(flags & ELEMENT_CREAT))
return NULL;
cfg = element_cfg_lookup(name);
if (!element_allowed(name, cfg))
return NULL;
DBG("Creating element %d \"%s\"", id, name);
e = xcalloc(1, sizeof(*e));
init_list_head(&e->e_list);
init_list_head(&e->e_childs);
init_list_head(&e->e_info_list);
init_list_head(&e->e_attr_sorted);
for (i = 0; i < ATTR_HASH_SIZE; i++)
init_list_head(&e->e_attrhash[i]);
e->e_name = strdup(name);
e->e_id = id;
e->e_parent = parent;
e->e_group = group;
e->e_lifecycles = get_lifecycles();
e->e_flags = ELEMENT_FLAG_CREATED;
e->e_cfg = cfg;
if (e->e_cfg) {
if (e->e_cfg->ec_description)
element_update_info(e, "Description",
e->e_cfg->ec_description);
element_set_rxmax(e, e->e_cfg->ec_rxmax);
element_set_txmax(e, e->e_cfg->ec_txmax);
}
if (parent) {
DBG("Attached to parent %d \"%s\"", parent->e_id, parent->e_name);
list_add_tail(&e->e_list, &parent->e_childs);
} else {
DBG("Attached to group %s", group->g_name);
list_add_tail(&e->e_list, &group->g_elements);
}
group->g_nelements++;
return e;
}
void element_free(struct element *e)
{
struct info *info, *ninfo;
struct element *c, *cnext;
struct attr *a, *an;
int i;
if (e->e_group->g_current == e) {
element_select_prev();
if (e->e_group->g_current == e)
e->e_group->g_current = NULL;
}
list_for_each_entry_safe(c, cnext, &e->e_childs, e_list)
element_free(c);
list_for_each_entry_safe(info, ninfo, &e->e_info_list, i_list) {
xfree(info->i_name);
xfree(info->i_value);
list_del(&info->i_list);
xfree(info);
}
for (i = 0; i < ATTR_HASH_SIZE; i++)
list_for_each_entry_safe(a, an, &e->e_attrhash[i], a_list)
attr_free(a);
if (e->e_group) {
list_del(&e->e_list);
e->e_group->g_nelements--;
}
xfree(e->e_name);
xfree(e);
}
#if 0
if (item->i_group->g_selected == item) {
if (item_select_prev() == END_OF_LIST) {
if (group_select_prev() == END_OF_LIST) {
if (node_select_prev() != END_OF_LIST)
item_select_last();
} else
item_select_last();
}
}
#endif
#if 0
void item_delete(struct item *item)
{
int m;
struct item *child;
for (m = 0; m < ATTR_HASH_MAX; m++) {
struct attr *a, *next;
for (a = item->i_attrs[m]; a; a = next) {
next = a->a_next;
xfree(a);
}
}
if (item->i_group->g_selected == item) {
if (item_select_prev() == END_OF_LIST) {
if (group_select_prev() == END_OF_LIST) {
if (node_select_prev() != END_OF_LIST)
item_select_last();
} else
item_select_last();
}
}
unlink_item(item);
item->i_group->g_nitems--;
for (child = item->i_childs; child; child = child->i_next)
item_delete(child);
if (item->i_path)
xfree(item->i_path);
xfree(item);
}
#endif
void element_reset_update_flag(struct element_group *g,
struct element *e, void *arg)
{
DBG("Reseting update flag of %s", e->e_name);
e->e_flags &= ~ELEMENT_FLAG_UPDATED;
}
/**
* Needs to be called after updating all attributes of an element
*/
void element_notify_update(struct element *e, timestamp_t *ts)
{
struct attr *a;
int i;
e->e_flags |= ELEMENT_FLAG_UPDATED;
if (ts == NULL)
ts = &rtiming.rt_last_read;
for (i = 0; i < ATTR_HASH_SIZE; i++)
list_for_each_entry(a, &e->e_attrhash[i], a_list)
attr_notify_update(a, ts);
if (e->e_usage_attr && e->e_cfg &&
(a = attr_lookup(e, e->e_usage_attr->ad_id))) {
attr_calc_usage(a, &e->e_rx_usage, &e->e_tx_usage,
e->e_cfg->ec_rxmax, e->e_cfg->ec_txmax);
} else {
e->e_rx_usage = FLT_MAX;
e->e_tx_usage = FLT_MAX;
}
}
void element_lifesign(struct element *e, int n)
{
e->e_lifecycles = n * get_lifecycles();
}
void element_check_if_dead(struct element_group *g,
struct element *e, void *arg)
{
if (--(e->e_lifecycles) <= 0) {
element_free(e);
DBG("Deleting dead element %s", e->e_name);
}
}
void element_foreach_attr(struct element *e,
void (*cb)(struct element *e,
struct attr *, void *),
void *arg)
{
struct attr *a;
list_for_each_entry(a, &e->e_attr_sorted, a_sort_list)
cb(e, a, arg);
}
int element_set_key_attr(struct element *e, const char *major,
const char * minor)
{
if (!(e->e_key_attr[GT_MAJOR] = attr_def_lookup(major)))
return -ENOENT;
if (!(e->e_key_attr[GT_MINOR] = attr_def_lookup(minor)))
return -ENOENT;
return 0;
}
int element_set_usage_attr(struct element *e, const char *usage)
{
if (!(e->e_usage_attr = attr_def_lookup(usage)))
return -ENOENT;
return 0;
}
struct element *element_current(void)
{
struct element_group *g;
if (!(g = group_current()))
return NULL;
if (!g->g_current)
element_select_first();
return g->g_current;
}
struct element *element_select_first(void)
{
struct element_group *g;
if (!(g = group_current()))
return NULL;
if (list_empty(&g->g_elements))
g->g_current = NULL;
else
g->g_current = list_first_entry(&g->g_elements,
struct element, e_list);
return g->g_current;
}
struct element *element_select_last(void)
{
struct element_group *g;
if (!(g = group_current()))
return NULL;
if (list_empty(&g->g_elements))
g->g_current = NULL;
else {
struct element *e;
e = list_entry(g->g_elements.prev, struct element, e_list);
while (!list_empty(&e->e_childs))
e = list_entry(e->e_childs.prev, struct element,
e_list);
g->g_current = e;
}
return g->g_current;
}
struct element *element_select_next(void)
{
struct element_group *g;
struct element *e;
if (!(g = group_current()))
return NULL;
if (!(e = g->g_current))
return element_select_first();
if (!list_empty(&e->e_childs))
e = list_first_entry(&e->e_childs, struct element, e_list);
else {
/*
* move upwards until we have no parent or there is a next
* entry in the list
*/
while (e->e_parent && e->e_list.next == &e->e_parent->e_childs)
e = e->e_parent;
if (!e->e_parent && e->e_list.next == &g->g_elements) {
group_select_next();
return element_select_first();
} else
e = list_entry(e->e_list.next, struct element, e_list);
}
g->g_current = e;
return e;
}
struct element *element_select_prev(void)
{
struct element_group *g;
struct element *e;
if (!(g = group_current()))
return NULL;
if (!(e = g->g_current))
return element_select_last();
if (!e->e_parent && e->e_list.prev == &g->g_elements) {
group_select_prev();
return element_select_last();
}
if (e->e_parent && e->e_list.prev == &e->e_parent->e_childs)
e = e->e_parent;
else {
e = list_entry(e->e_list.prev, struct element, e_list);
while (!list_empty(&e->e_childs))
e = list_entry(e->e_childs.prev, struct element,
e_list);
}
g->g_current = e;
return e;
}
static struct info *element_info_lookup(struct element *e, const char *name)
{
struct info *i;
list_for_each_entry(i, &e->e_info_list, i_list)
if (!strcmp(i->i_name, name))
return i;
return NULL;
}
void element_update_info(struct element *e, const char *name, const char *value)
{
struct info *i;
if ((i = element_info_lookup(e, name))) {
xfree(i->i_value);
i->i_value = strdup(value);
return;
}
DBG("Created element info %s (\"%s\")", name, value);
i = xcalloc(1, sizeof(*i));
i->i_name = strdup(name);
i->i_value = strdup(value);
e->e_ninfo++;
list_add_tail(&i->i_list, &e->e_info_list);
}
void element_set_txmax(struct element *e, uint64_t max)
{
char buf[32];
if (!e->e_cfg)
e->e_cfg = element_cfg_create(e->e_name);
if (e->e_cfg->ec_txmax != max)
e->e_cfg->ec_txmax = max;
unit_bit2str(e->e_cfg->ec_txmax * 8, buf, sizeof(buf));
element_update_info(e, "TxMax", buf);
}
void element_set_rxmax(struct element *e, uint64_t max)
{
char buf[32];
if (!e->e_cfg)
e->e_cfg = element_cfg_create(e->e_name);
if (e->e_cfg->ec_rxmax != max)
e->e_cfg->ec_rxmax = max;
unit_bit2str(e->e_cfg->ec_rxmax * 8, buf, sizeof(buf));
element_update_info(e, "RxMax", buf);
}

96
src/element_cfg.c Normal file
View File

@ -0,0 +1,96 @@
/*
* element_cfg.c Element Configuration
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/element.h>
#include <bmon/element_cfg.h>
#include <bmon/utils.h>
static LIST_HEAD(cfg_list);
struct element_cfg *element_cfg_alloc(const char *name)
{
struct element_cfg *ec;
if ((ec = element_cfg_lookup(name))) {
ec->ec_refcnt++;
return ec;
}
ec = xcalloc(1, sizeof(*ec));
ec->ec_name = strdup(name);
ec->ec_refcnt = 1;
list_add_tail(&ec->ec_list, &cfg_list);
return ec;
}
struct element_cfg *element_cfg_create(const char *name)
{
struct element_cfg *cfg;
if (!(cfg = element_cfg_lookup(name)))
cfg = element_cfg_alloc(name);
return cfg;
}
static void __element_cfg_free(struct element_cfg *ec)
{
list_del(&ec->ec_list);
xfree(ec->ec_name);
xfree(ec->ec_description);
xfree(ec);
}
void element_cfg_free(struct element_cfg *ec)
{
if (!ec || --ec->ec_refcnt)
return;
__element_cfg_free(ec);
}
struct element_cfg *element_cfg_lookup(const char *name)
{
struct element_cfg *ec;
list_for_each_entry(ec, &cfg_list, ec_list)
if (!strcmp(name, ec->ec_name))
return ec;
return NULL;
}
static void __exit __element_cfg_exit(void)
{
struct element_cfg *ec, *n;
list_for_each_entry_safe(ec, n, &cfg_list, ec_list)
__element_cfg_free(ec);
}

235
src/graph.c Normal file
View File

@ -0,0 +1,235 @@
/*
* graph.c Graph creation utility
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/graph.h>
#include <bmon/input.h>
#include <bmon/conf.h>
#include <bmon/history.h>
#include <bmon/conf.h>
#include <bmon/unit.h>
#include <bmon/utils.h>
size_t graph_row_size(struct graph_cfg *cfg)
{
/* +1 for trailing \0 */
return cfg->gc_width + 1;
}
static inline size_t table_size(struct graph_cfg *cfg)
{
return cfg->gc_height * graph_row_size(cfg);
}
static inline char *at_row(struct graph_cfg *cfg, char *col, int nrow)
{
return col + (nrow * graph_row_size(cfg));
}
static inline char *at_col(char *row, int ncol)
{
return row + ncol;
}
static inline char *tbl_pos(struct graph_cfg *cfg, char *tbl, int nrow, int ncol)
{
return at_col(at_row(cfg, tbl, nrow), ncol);
}
static void write_column(struct graph_cfg *cfg, struct graph_table *tbl, int ncol,
uint64_t value, double *scale, double half_step)
{
char *col = at_col(tbl->gt_table, ncol);
int i;
#if 0
if (value == UNK_DATA) {
for (i = 0; i < height; i++)
*(at_row(g, col, i)) = unk_char;
#endif
if (value) {
*(at_row(cfg, col, 0)) = ':';
for (i = 0; i < cfg->gc_height; i++)
if (value >= (scale[i] - half_step))
*(at_row(cfg, col, i)) = cfg->gc_foreground;
}
}
static void fill_table(struct graph *g, struct graph_table *tbl,
struct history *h, struct history_store *data)
{
struct graph_cfg *cfg = &g->g_cfg;
uint64_t max = 0, v;
int i, n, t;
float half_step, step;
if (!tbl->gt_table) {
tbl->gt_table = xcalloc(table_size(cfg), sizeof(char));
tbl->gt_scale = xcalloc(cfg->gc_height, sizeof(double));
}
memset(tbl->gt_table, cfg->gc_background, table_size(cfg));
/* end each line with a \0 */
for (i = 0; i < cfg->gc_height; i++)
*tbl_pos(cfg, tbl->gt_table, i, cfg->gc_width) = '\0';
/* leave table blank if there is no data */
if (!h || !data->hs_data)
return;
if (cfg->gc_width > h->h_definition->hd_size)
BUG();
/* find the largest peak */
for (n = h->h_index, i = 0; i < cfg->gc_width; i++) {
if (--n < 0)
n = h->h_definition->hd_size - 1;
v = history_data(h, data, n);
if (v != HISTORY_UNKNOWN && max < v)
max = v;
}
step = (double) max / (double) cfg->gc_height;
half_step = step / 2.0f;
for (i = 0; i < cfg->gc_height; i++)
tbl->gt_scale[i] = (double) (i + 1) * step;
for (n = h->h_index, i = 0; i < cfg->gc_width; i++) {
char * col = at_col(tbl->gt_table, i);
if (--n < 0)
n = h->h_definition->hd_size - 1;
v = history_data(h, data, n);
if (v == HISTORY_UNKNOWN) {
for (t = 0; t < cfg->gc_height; t++)
*(at_row(cfg, col, t)) = cfg->gc_unknown;
} else if (v > 0) {
*(at_row(cfg, col, 0)) = cfg->gc_noise;
for (t = 0; t < cfg->gc_height; t++)
if (v >= (tbl->gt_scale[t] - half_step))
*(at_row(cfg, col, t)) = cfg->gc_foreground;
}
}
n = (cfg->gc_height / 3) * 2;
if (n >= cfg->gc_height)
n = (cfg->gc_height - 1);
v = unit_divisor(tbl->gt_scale[n], cfg->gc_unit,
&tbl->gt_y_unit, NULL);
for (i = 0; i < cfg->gc_height; i++)
tbl->gt_scale[i] /= (double) v;
}
struct graph *graph_alloc(struct history *h, struct graph_cfg *cfg)
{
struct graph *g;
if (!cfg->gc_height)
BUG();
g = xcalloc(1, sizeof(*g));
memcpy(&g->g_cfg, cfg, sizeof(*cfg));
if (h != NULL &&
(cfg->gc_width > h->h_definition->hd_size || !cfg->gc_width))
g->g_cfg.gc_width = h->h_definition->hd_size;
if (!g->g_cfg.gc_width)
BUG();
return g;
}
void graph_refill(struct graph *g, struct history *h)
{
fill_table(g, &g->g_rx, h, h ? &h->h_rx : NULL);
fill_table(g, &g->g_tx, h, h ? &h->h_tx : NULL);
}
void graph_free(struct graph *g)
{
if (!g)
return;
xfree(g->g_rx.gt_table);
xfree(g->g_rx.gt_scale);
xfree(g->g_tx.gt_table);
xfree(g->g_tx.gt_scale);
xfree(g);
}
#if 0
void new_graph(void)
{
if (ngraphs >= (MAX_GRAPHS - 1))
return;
set_ngraphs_hard(ngraphs + 1);
}
void del_graph(void)
{
if (ngraphs <= 1)
return;
set_ngraphs_hard(ngraphs - 1);
}
int next_graph(void)
{
struct item *it = item_current();
if (it == NULL)
return EMPTY_LIST;
if (it->i_graph_sel >= (ngraphs - 1))
it->i_graph_sel = 0;
else
it->i_graph_sel++;
return 0;
}
int prev_graph(void)
{
struct item *it = item_current();
if (it == NULL)
return EMPTY_LIST;
if (it->i_graph_sel <= 0)
it->i_graph_sel = ngraphs - 1;
else
it->i_graph_sel--;
return 0;
}
#endif

278
src/group.c Normal file
View File

@ -0,0 +1,278 @@
/*
* group.c Group Management
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/element.h>
#include <bmon/group.h>
#include <bmon/utils.h>
static LIST_HEAD(titles_list);
static LIST_HEAD(group_list);
static unsigned int ngroups;
static struct element_group *current_group;
static void __group_foreach_element(struct element_group *g,
struct list_head *list,
void (*cb)(struct element_group *,
struct element *, void *),
void *arg)
{
struct element *e, *n;
list_for_each_entry_safe(e, n, list, e_list) {
cb(g, e, arg);
if (!list_empty(&e->e_childs))
__group_foreach_element(g, &e->e_childs, cb, arg);
}
}
void group_foreach_element(struct element_group *g,
void (*cb)(struct element_group *,
struct element *, void *),
void *arg)
{
__group_foreach_element(g, &g->g_elements, cb, arg);
}
void group_foreach_recursive(void (*cb)(struct element_group *,
struct element *, void *),
void *arg)
{
struct element_group *g, *n;
list_for_each_entry_safe(g, n, &group_list, g_list)
__group_foreach_element(g, &g->g_elements, cb, arg);
}
void group_foreach(void (*cb)(struct element_group *, void *), void *arg)
{
struct element_group *g, *n;
list_for_each_entry_safe(g, n, &group_list, g_list)
cb(g, arg);
}
struct element_group *group_select_first(void)
{
if (list_empty(&group_list))
current_group = NULL;
else
current_group = list_first_entry(&group_list,
struct element_group, g_list);
return current_group;
}
struct element_group *group_select_last(void)
{
if (list_empty(&group_list))
current_group = NULL;
else
current_group = list_entry(group_list.prev,
struct element_group, g_list);
return current_group;
}
struct element_group *group_select_next(void)
{
if (!current_group)
return group_select_first();
if (current_group->g_list.next != &group_list)
current_group = list_entry(current_group->g_list.next,
struct element_group,
g_list);
else
return group_select_first();
return current_group;
}
struct element_group *group_select_prev(void)
{
if (!current_group)
return group_select_last();
if (current_group->g_list.prev != &group_list)
current_group = list_entry(current_group->g_list.prev,
struct element_group,
g_list);
else
return group_select_last();
return current_group;
}
struct element_group *group_current(void)
{
if (current_group == NULL)
current_group = group_select_first();
return current_group;
}
struct element_group *group_lookup(const char *name, int flags)
{
struct element_group *g;
struct group_hdr *hdr;
list_for_each_entry(g, &group_list, g_list)
if (!strcmp(name, g->g_name))
return g;
if (!(flags & GROUP_CREATE))
return NULL;
if (!(hdr = group_lookup_hdr(name))) {
fprintf(stderr, "Cannot find title for group \"%s\"\n", name);
return NULL;
}
g = xcalloc(1, sizeof(*g));
init_list_head(&g->g_elements);
g->g_name = hdr->gh_name;
g->g_hdr = hdr;
list_add_tail(&g->g_list, &group_list);
ngroups++;
return g;
}
static void group_free(struct element_group *g)
{
struct element *e, *n;
struct element_group *next;
if (current_group == g) {
next = group_select_next();
if (!next || next == g)
current_group = NULL;
}
list_for_each_entry_safe(e, n, &g->g_elements, e_list)
element_free(e);
xfree(g);
}
void reset_update_flags(void)
{
group_foreach_recursive(&element_reset_update_flag, NULL);
}
void free_unused_elements(void)
{
group_foreach_recursive(&element_check_if_dead, NULL);
}
struct group_hdr *group_lookup_hdr(const char *name)
{
struct group_hdr *hdr;
list_for_each_entry(hdr, &titles_list, gh_list)
if (!strcmp(hdr->gh_name, name))
return hdr;
return NULL;
}
int group_new_hdr(const char *name, const char *title,
const char *col1, const char *col2,
const char *col3, const char *col4)
{
struct group_hdr *hdr;
if (group_lookup_hdr(name))
return -EEXIST;
hdr = xcalloc(1, sizeof(*hdr));
init_list_head(&hdr->gh_list);
hdr->gh_name = strdup(name);
hdr->gh_title = strdup(title);
hdr->gh_column[0] = strdup(col1);
hdr->gh_column[1] = strdup(col2);
hdr->gh_column[2] = strdup(col3);
hdr->gh_column[3] = strdup(col4);
list_add_tail(&hdr->gh_list, &titles_list);
DBG("New group title %s \"%s\"", name, title);
return 0;
}
int group_new_derived_hdr(const char *name, const char *title,
const char *template)
{
struct group_hdr *t;
if (group_lookup_hdr(name))
return -EEXIST;
if (!(t = group_lookup_hdr(template)))
return -ENOENT;
return group_new_hdr(name, title, t->gh_column[0], t->gh_column[1],
t->gh_column[2], t->gh_column[3]);
}
static void group_hdr_free(struct group_hdr *hdr)
{
xfree(hdr->gh_name);
xfree(hdr->gh_title);
xfree(hdr->gh_column[0]);
xfree(hdr->gh_column[1]);
xfree(hdr->gh_column[2]);
xfree(hdr->gh_column[3]);
xfree(hdr);
}
static void __init group_init(void)
{
DBG("init");
group_new_hdr("intf", "Interfaces",
"RX bps", "pps", "TX bps", "pps");
}
static void __exit group_exit(void)
{
struct element_group *g, *next;
struct group_hdr *hdr, *gnext;
list_for_each_entry_safe(g, next, &group_list, g_list)
group_free(g);
list_for_each_entry_safe(hdr, gnext, &titles_list, gh_list)
group_hdr_free(hdr);
}

318
src/history.c Normal file
View File

@ -0,0 +1,318 @@
/*
* history.c History Management
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/history.h>
#include <bmon/utils.h>
static LIST_HEAD(def_list);
static struct history_def *current_history;
struct history_def *history_def_lookup(const char *name)
{
struct history_def *def;
list_for_each_entry(def, &def_list, hd_list)
if (!strcmp(def->hd_name, name))
return def;
return NULL;
}
struct history_def *history_def_alloc(const char *name)
{
struct history_def *def;
if ((def = history_def_lookup(name)))
return def;
def = xcalloc(1, sizeof(*def));
def->hd_name = strdup(name);
list_add_tail(&def->hd_list, &def_list);
DBG("New history definition %s", name);
return def;
}
static void history_def_free(struct history_def *def)
{
if (!def)
return;
xfree(def->hd_name);
xfree(def);
}
static void *history_alloc_data(struct history_def *def)
{
return xcalloc(def->hd_size, def->hd_type);
}
static void history_store_data(struct history *h, struct history_store *hs,
uint64_t total, float diff)
{
uint64_t delta;
if (!hs->hs_data) {
if (total == HISTORY_UNKNOWN)
return;
hs->hs_data = history_alloc_data(h->h_definition);
}
if (total == HISTORY_UNKNOWN)
delta = HISTORY_UNKNOWN;
else {
delta = (total - hs->hs_prev_total);
if (delta > 0)
delta /= diff;
hs->hs_prev_total = total;
}
switch (h->h_definition->hd_type) {
case HISTORY_TYPE_8:
((uint8_t *) hs->hs_data)[h->h_index] = (uint8_t) delta;
break;
case HISTORY_TYPE_16:
((uint16_t *) hs->hs_data)[h->h_index] = (uint16_t) delta;
break;
case HISTORY_TYPE_32:
((uint32_t *) hs->hs_data)[h->h_index] = (uint32_t) delta;
break;
case HISTORY_TYPE_64:
((uint64_t *) hs->hs_data)[h->h_index] = (uint64_t) delta;
break;
default:
BUG();
}
}
static inline void inc_history_index(struct history *h)
{
if (h->h_index < (h->h_definition->hd_size - 1))
h->h_index++;
else
h->h_index = 0;
}
uint64_t history_data(struct history *h, struct history_store *hs, int index)
{
switch (h->h_definition->hd_type) {
case HISTORY_TYPE_8: {
uint8_t v = ((uint8_t *) hs->hs_data)[index];
return (v == (uint8_t) -1) ? HISTORY_UNKNOWN : v;
}
case HISTORY_TYPE_16: {
uint16_t v = ((uint16_t *) hs->hs_data)[index];
return (v == (uint16_t) -1) ? HISTORY_UNKNOWN : v;
}
case HISTORY_TYPE_32: {
uint32_t v = ((uint32_t *) hs->hs_data)[index];
return (v == (uint32_t) -1) ? HISTORY_UNKNOWN : v;
}
case HISTORY_TYPE_64: {
uint64_t v = ((uint64_t *) hs->hs_data)[index];
return (v == (uint64_t) -1) ? HISTORY_UNKNOWN : v;
}
default:
BUG();
}
}
void history_update(struct attr *a, struct history *h, timestamp_t *ts)
{
struct history_def *def = h->h_definition;
float timediff;
if (h->h_last_update.tv_sec)
timediff = timestamp_diff(&h->h_last_update, ts);
else {
timediff = 0.0f; /* initial history update */
/* Need a delta when working with counters */
if (a->a_def->ad_type == ATTR_TYPE_COUNTER)
goto update_prev_total;
}
/*
* A read interval greater than the desired history interval
* can't possibly result in anything useful. Discard it and
* mark history data as invalid. The user has to adjust the
* read interval.
*/
if (cfg_read_interval > def->hd_interval)
goto discard;
/*
* If the history interval matches the read interval it makes
* sense to update upon every read. The reader timing already
* took care of being as close as possible to the desired
* interval.
*/
if (cfg_read_interval == def->hd_interval)
goto update;
if (timediff > h->h_max_interval)
goto discard;
if (timediff < h->h_min_interval)
return;
update:
history_store_data(h, &h->h_rx, a->a_rx_rate.r_total, timediff);
history_store_data(h, &h->h_tx, a->a_tx_rate.r_total, timediff);
inc_history_index(h);
goto update_ts;
discard:
while(timediff >= (def->hd_interval / 2)) {
history_store_data(h, &h->h_rx, HISTORY_UNKNOWN, 0.0f);
history_store_data(h, &h->h_tx, HISTORY_UNKNOWN, 0.0f);
inc_history_index(h);
timediff -= def->hd_interval;
}
update_prev_total:
h->h_rx.hs_prev_total = a->a_rx_rate.r_total;
h->h_tx.hs_prev_total = a->a_tx_rate.r_total;
update_ts:
copy_timestamp(&h->h_last_update, ts);
}
struct history *history_alloc(struct history_def *def)
{
struct history *h;
h = xcalloc(1, sizeof(*h));
init_list_head(&h->h_list);
h->h_definition = def;
h->h_min_interval = (def->hd_interval - (cfg_read_interval / 2.0f));
h->h_max_interval = (def->hd_interval / cfg_history_variance);
return h;
}
void history_free(struct history *h)
{
if (!h)
return;
xfree(h->h_rx.hs_data);
xfree(h->h_tx.hs_data);
list_del(&h->h_list);
xfree(h);
}
void history_attach(struct attr *attr)
{
struct history_def *def;
struct history *h;
list_for_each_entry(def, &def_list, hd_list) {
h = history_alloc(def);
list_add_tail(&h->h_list, &attr->a_history_list);
}
}
struct history_def *history_select_first(void)
{
if (list_empty(&def_list))
current_history = NULL;
else
current_history = list_first_entry(&def_list,
struct history_def, hd_list);
return current_history;
}
struct history_def *history_select_last(void)
{
if (list_empty(&def_list))
current_history = NULL;
else
current_history = list_entry(def_list.prev,
struct history_def, hd_list);
return current_history;
}
struct history_def *history_select_next(void)
{
if (current_history && current_history->hd_list.next != &def_list) {
current_history = list_entry(current_history->hd_list.next,
struct history_def, hd_list);
return current_history;
}
return history_select_first();
}
struct history_def *history_select_prev(void)
{
if (current_history && current_history->hd_list.prev != &def_list) {
current_history = list_entry(current_history->hd_list.prev,
struct history_def, hd_list);
return current_history;
}
return history_select_last();
}
struct history_def *history_current(void)
{
if (!current_history)
current_history = history_select_first();
return current_history;
}
static void __exit history_exit(void)
{
struct history_def *def, *n;
list_for_each_entry_safe(def, n, &def_list, hd_list)
history_def_free(def);
}

243
src/in_dummy.c Normal file
View File

@ -0,0 +1,243 @@
/*
* in_dummy.c Dummy Input Method
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/input.h>
#include <bmon/group.h>
#include <bmon/element.h>
#include <bmon/attr.h>
#include <bmon/utils.h>
#define MAXDEVS 32
static uint64_t c_rx_b_inc = 1000000000;
static uint64_t c_tx_b_inc = 80000000;
static uint64_t c_rx_p_inc = 1000;
static uint64_t c_tx_p_inc = 800;
static int c_numdev = 5;
static int c_randomize = 0;
static int c_mtu = 1540;
static int c_maxpps = 100000;
static int c_numgroups = 2;
static uint64_t *cnts;
enum {
DUMMY_BYTES,
DUMMY_PACKETS,
NUM_DUMMY_VALUE,
};
static struct attr_map link_attrs[NUM_DUMMY_VALUE] = {
{
.name = "bytes",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Bytes",
},
{
.name = "packets",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Packtes",
}
};
/* cnts[ngroups][ndevs][2][2] */
static inline int cnt_size(void)
{
return c_numgroups * c_numdev * 2 * 2;
}
static inline uint64_t *cnt(int group, int dev, int unit,
int direction)
{
return cnts + (group * c_numdev * 2 * 2) +
(dev * 2 * 2) +
(unit * 2) +
direction;
}
static void dummy_read(void)
{
int gidx, n;
for (gidx = 0; gidx < c_numgroups; gidx++) {
char gname[32];
struct element_group *group;
snprintf(gname, sizeof(gname), "group%02d", gidx);
group = group_lookup(gname, GROUP_CREATE);
for (n = 0; n < c_numdev; n++) {
char ifname[IFNAMSIZ];
struct element *e;
int i;
snprintf(ifname, sizeof(ifname), "dummy%d", n);
if (!(e = element_lookup(group, ifname, 0, NULL, ELEMENT_CREAT)))
return;
if (e->e_flags & ELEMENT_FLAG_CREATED) {
if (element_set_key_attr(e, "bytes", "packets") ||
element_set_usage_attr(e, "bytes"))
BUG();
e->e_flags &= ~ELEMENT_FLAG_CREATED;
}
if (e->e_flags & ELEMENT_FLAG_UPDATED)
continue;
if (c_randomize) {
uint64_t rx = rand() % c_maxpps;
uint64_t tx = rand() % c_maxpps;
*cnt(gidx, n, 0, 0) += rx;
*cnt(gidx, n, 0, 1) += tx;
*cnt(gidx, n, 1, 0) += rx * (rand() % c_mtu);
*cnt(gidx, n, 1, 1) += tx * (rand() % c_mtu);
} else {
*cnt(gidx, n, 0, 0) += c_rx_p_inc;
*cnt(gidx, n, 0, 1) += c_tx_p_inc;
*cnt(gidx, n, 1, 0) += c_rx_b_inc;
*cnt(gidx, n, 1, 1) += c_tx_b_inc;
}
for (i = 0; i < ARRAY_SIZE(link_attrs); i++) {
struct attr_map *m = &link_attrs[i];
attr_update(e, m->attrid,
*cnt(gidx, n, i, 0),
*cnt(gidx, n, i, 1),
UPDATE_FLAG_RX | UPDATE_FLAG_TX |
UPDATE_FLAG_64BIT);
}
element_notify_update(e, NULL);
element_lifesign(e, 1);
}
}
}
static void print_help(void)
{
printf(
"dummy - Statistic generator module (dummy)\n" \
"\n" \
" Basic statistic generator for testing purposes. Can produce a\n" \
" constant or random statistic flow with configurable parameters.\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" rxb=NUM RX bytes increment amount (default: 10^9)\n" \
" txb=NUM TX bytes increment amount (default: 8*10^7)\n" \
" rxp=NUM RX packets increment amount (default: 1K)\n" \
" txp=NUM TX packets increment amount (default: 800)\n" \
" num=NUM Number of devices (default: 5)\n" \
" numgroups=NUM Number of groups (default: 2)\n" \
" randomize Randomize counters (default: off)\n" \
" seed=NUM Seed for randomizer (default: time(0))\n" \
" mtu=NUM Maximal Transmission Unit (default: 1540)\n" \
" maxpps=NUM Upper limit for packets per second (default: 100K)\n" \
"\n" \
" Randomizer:\n" \
" RX-packets := Rand() %% maxpps\n" \
" TX-packets := Rand() %% maxpps\n" \
" RX-bytes := RX-packets * (Rand() %% mtu)\n" \
" TX-bytes := TX-packets * (Rand() %% mtu)\n");
}
static void dummy_parse_opt(const char *value, const char *type)
{
if (!strcasecmp(type, "rxb") && value)
c_rx_b_inc = strtol(value, NULL, 0);
else if (!strcasecmp(type, "txb") && value)
c_tx_b_inc = strtol(value, NULL, 0);
else if (!strcasecmp(type, "rxp") && value)
c_rx_p_inc = strtol(value, NULL, 0);
else if (!strcasecmp(type, "txp") && value)
c_tx_p_inc = strtol(value, NULL, 0);
else if (!strcasecmp(type, "num") && value)
c_numdev = strtol(value, NULL, 0);
else if (!strcasecmp(type, "randomize")) {
c_randomize = 1;
srand(time(0));
} else if (!strcasecmp(type, "seed") && value)
srand(strtol(value, NULL, 0));
else if (!strcasecmp(type, "mtu") && value)
c_mtu = strtol(value, NULL, 0);
else if (!strcasecmp(type, "maxpps") && value)
c_maxpps = strtol(value, NULL, 0);
else if (!strcasecmp(type, "numgroups") && value)
c_numgroups = strtol(value, NULL, 0);
else if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static int dummy_do_init(void)
{
if (attr_map_load(link_attrs, ARRAY_SIZE(link_attrs)))
BUG();
return 0;
}
static int dummy_probe(void)
{
int i;
if (c_numdev >= MAXDEVS) {
fprintf(stderr, "numdev must be in range 0..%d\n", MAXDEVS);
return 0;
}
cnts = xcalloc(cnt_size(), sizeof(uint64_t));
for (i = 0; i < c_numgroups; i++) {
char groupname[32];
snprintf(groupname, sizeof(groupname), "group%02d", i);
group_new_derived_hdr(groupname, groupname, DEFAULT_GROUP);
}
return 1;
}
static struct bmon_module dummy_ops = {
.m_name = "dummy",
.m_do = dummy_read,
.m_parse_opt = dummy_parse_opt,
.m_probe = dummy_probe,
.m_init = dummy_do_init,
};
static void __init dummy_init(void)
{
input_register(&dummy_ops);
}

868
src/in_netlink.c Normal file
View File

@ -0,0 +1,868 @@
/*
* in_netlink.c Netlink input
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/input.h>
#include <bmon/element.h>
#include <bmon/attr.h>
#include <bmon/conf.h>
#include <bmon/input.h>
#include <bmon/utils.h>
static int c_notc = 0;
static struct element_group *grp;
static struct bmon_module netlink_ops;
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
#include <netlink/route/link.h>
#include <netlink/route/tc.h>
#include <netlink/route/qdisc.h>
#include <netlink/route/class.h>
#include <netlink/route/classifier.h>
#include <netlink/route/qdisc/htb.h>
static struct attr_map link_attrs[] = {
{
.name = "bytes",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Bytes",
.rxid = RTNL_LINK_RX_BYTES,
.txid = RTNL_LINK_TX_BYTES,
},
{
.name = "packets",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Packtes",
.rxid = RTNL_LINK_RX_PACKETS,
.txid = RTNL_LINK_TX_PACKETS,
},
{
.name = "errors",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Errors",
.rxid = RTNL_LINK_RX_ERRORS,
.txid = RTNL_LINK_TX_ERRORS,
},
{
.name = "drop",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Dropped",
.rxid = RTNL_LINK_RX_DROPPED,
.txid = RTNL_LINK_TX_DROPPED,
},
{
.name = "compressed",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Compressed",
.rxid = RTNL_LINK_RX_COMPRESSED,
.txid = RTNL_LINK_TX_COMPRESSED,
},
{
.name = "fifoerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "FIFO Error",
.rxid = RTNL_LINK_RX_FIFO_ERR,
.txid = RTNL_LINK_TX_FIFO_ERR,
},
{
.name = "lenerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Length Error",
.rxid = RTNL_LINK_RX_LEN_ERR,
.txid = -1,
},
{
.name = "overerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Over Error",
.rxid = RTNL_LINK_RX_OVER_ERR,
.txid = -1,
},
{
.name = "crcerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "CRC Error",
.rxid = RTNL_LINK_RX_CRC_ERR,
.txid = -1,
},
{
.name = "frameerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Frame Error",
.rxid = RTNL_LINK_RX_FRAME_ERR,
.txid = -1,
},
{
.name = "misserr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Missed Error",
.rxid = RTNL_LINK_RX_MISSED_ERR,
.txid = -1,
},
{
.name = "aborterr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Abort Error",
.rxid = -1,
.txid = RTNL_LINK_TX_ABORT_ERR,
},
{
.name = "carrerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Carrier Error",
.rxid = -1,
.txid = RTNL_LINK_TX_CARRIER_ERR,
},
{
.name = "hbeaterr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Heartbeat Error",
.rxid = -1,
.txid = RTNL_LINK_TX_HBEAT_ERR,
},
{
.name = "winerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Window Error",
.rxid = -1,
.txid = RTNL_LINK_TX_WIN_ERR,
},
{
.name = "coll",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Collisions",
.rxid = -1,
.txid = RTNL_LINK_COLLISIONS,
},
{
.name = "mcast",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Multicast",
.rxid = -1,
.txid = RTNL_LINK_MULTICAST,
},
{
.name = "ip6pkts",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6Pkts",
.rxid = RTNL_LINK_IP6_INPKTS,
.txid = RTNL_LINK_IP6_OUTPKTS,
},
{
.name = "ip6discards",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6Discards",
.rxid = RTNL_LINK_IP6_INDISCARDS,
.txid = RTNL_LINK_IP6_OUTDISCARDS,
},
{
.name = "ip6octets",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Ip6Octets",
.rxid = RTNL_LINK_IP6_INOCTETS,
.txid = RTNL_LINK_IP6_OUTOCTETS,
},
{
.name = "ip6bcastp",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Broadcast Packets",
.rxid = RTNL_LINK_IP6_INBCASTPKTS,
.txid = RTNL_LINK_IP6_OUTBCASTPKTS,
},
{
.name = "ip6bcast",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Ip6 Broadcast",
.rxid = RTNL_LINK_IP6_INBCASTOCTETS,
.txid = RTNL_LINK_IP6_OUTBCASTOCTETS,
},
{
.name = "ip6mcastp",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Multicast Packets",
.rxid = RTNL_LINK_IP6_INMCASTPKTS,
.txid = RTNL_LINK_IP6_OUTMCASTPKTS,
},
{
.name = "ip6mcast",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Ip6 Multicast",
.rxid = RTNL_LINK_IP6_INMCASTOCTETS,
.txid = RTNL_LINK_IP6_OUTMCASTOCTETS,
},
{
.name = "ip6noroute",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 No Route",
.rxid = RTNL_LINK_IP6_INNOROUTES,
.txid = RTNL_LINK_IP6_OUTNOROUTES,
},
{
.name = "ip6forward",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Forwarded",
.rxid = -1,
.txid = RTNL_LINK_IP6_OUTFORWDATAGRAMS,
},
{
.name = "ip6delivers",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Delivers",
.rxid = RTNL_LINK_IP6_INDELIVERS,
.txid = -1,
},
{
.name = "icmp6",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "ICMPv6",
.rxid = RTNL_LINK_ICMP6_INMSGS,
.txid = RTNL_LINK_ICMP6_OUTMSGS,
},
{
.name = "icmp6err",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "ICMPv6 Errors",
.rxid = RTNL_LINK_ICMP6_INERRORS,
.txid = RTNL_LINK_ICMP6_OUTERRORS,
},
{
.name = "ip6inhdrerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Header Error",
.rxid = RTNL_LINK_IP6_INHDRERRORS,
.txid = -1,
},
{
.name = "ip6toobigerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Too Big Error",
.rxid = RTNL_LINK_IP6_INTOOBIGERRORS,
.txid = -1,
},
{
.name = "ip6trunc",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Truncated Packets",
.rxid = RTNL_LINK_IP6_INTRUNCATEDPKTS,
.txid = -1,
},
{
.name = "ip6unkproto",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Unknown Protocol Error",
.rxid = RTNL_LINK_IP6_INUNKNOWNPROTOS,
.txid = -1,
},
{
.name = "ip6addrerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Address Error",
.rxid = RTNL_LINK_IP6_INADDRERRORS,
.txid = -1,
},
{
.name = "ip6reasmtimeo",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Reassembly Timeouts",
.rxid = RTNL_LINK_IP6_REASMTIMEOUT,
.txid = -1,
},
{
.name = "ip6fragok",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Reasm/Frag OK",
.rxid = RTNL_LINK_IP6_REASMOKS,
.txid = RTNL_LINK_IP6_FRAGOKS,
},
{
.name = "ip6fragfail",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Reasm/Frag Failures",
.rxid = RTNL_LINK_IP6_REASMFAILS,
.txid = RTNL_LINK_IP6_FRAGFAILS,
},
{
.name = "ip6fragcreate",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Ip6 Reasm/Frag Requests",
.rxid = RTNL_LINK_IP6_REASMREQDS,
.txid = RTNL_LINK_IP6_FRAGCREATES,
}
};
static struct attr_map tc_attrs[] = {
{
.name = "tc_bytes",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Bytes",
.rxid = -1,
.txid = RTNL_TC_BYTES,
},
{
.name = "tc_packets",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Packets",
.rxid = -1,
.txid = RTNL_TC_PACKETS,
},
{
.name = "tc_overlimits",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Overlimits",
.rxid = -1,
.txid = RTNL_TC_OVERLIMITS,
},
{
.name = "tc_drop",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Dropped",
.rxid = -1,
.txid = RTNL_TC_DROPS,
},
{
.name = "tc_bps",
.type = ATTR_TYPE_RATE,
.unit = UNIT_BYTE,
.description = "Byte Rate/s",
.rxid = -1,
.txid = RTNL_TC_RATE_BPS,
},
{
.name = "tc_pps",
.type = ATTR_TYPE_RATE,
.unit = UNIT_NUMBER,
.description = "Packet Rate/s",
.rxid = -1,
.txid = RTNL_TC_RATE_PPS,
},
{
.name = "tc_qlen",
.type = ATTR_TYPE_RATE,
.unit = UNIT_NUMBER,
.description = "Queue Length",
.rxid = -1,
.txid = RTNL_TC_QLEN,
},
{
.name = "tc_backlog",
.type = ATTR_TYPE_RATE,
.unit = UNIT_NUMBER,
.description = "Backlog",
.rxid = -1,
.txid = RTNL_TC_BACKLOG,
},
{
.name = "tc_requeues",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Requeues",
.rxid = -1,
.txid = RTNL_TC_REQUEUES,
}
};
struct rdata {
struct element * parent;
int level;
};
static struct nl_sock *sock;
static struct nl_cache *link_cache, *qdisc_cache, *class_cache;
static void update_tc_attrs(struct element *e, struct rtnl_tc *tc)
{
int i;
for (i = 0; i < ARRAY_SIZE(tc_attrs); i++) {
uint64_t c_tx = rtnl_tc_get_stat(tc, tc_attrs[i].txid);
attr_update(e, tc_attrs[i].attrid, 0, c_tx, UPDATE_FLAG_TX);
}
}
static void update_tc_infos(struct element *e, struct rtnl_tc *tc)
{
char buf[64];
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mtu(tc));
element_update_info(e, "MTU", buf);
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mpu(tc));
element_update_info(e, "MPU", buf);
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_overhead(tc));
element_update_info(e, "Overhead", buf);
snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_handle(tc));
element_update_info(e, "Id", buf);
snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_parent(tc));
element_update_info(e, "Parent", buf);
}
static void handle_qdisc(struct nl_object *obj, void *);
static void find_classes(uint32_t, struct rdata *);
static void find_qdiscs(uint32_t, struct rdata *);
static struct element *handle_tc_obj(struct rtnl_tc *tc, const char *prefix,
struct rdata *rdata)
{
char buf[IFNAME_MAX], name[IFNAME_MAX];
uint32_t id = rtnl_tc_get_handle(tc);
struct element *e;
rtnl_tc_handle2str(id, buf, sizeof(buf));
snprintf(name, sizeof(name), "%s %s (%s)",
prefix, buf, rtnl_tc_get_kind(tc));
if (!(e = element_lookup(grp, name, id, rdata ? rdata->parent : NULL, ELEMENT_CREAT)))
return NULL;
if (e->e_flags & ELEMENT_FLAG_CREATED) {
e->e_level = rdata ? rdata->level : 0;
if (element_set_key_attr(e, "tc_bytes", "tc_packets") ||
element_set_usage_attr(e, "tc_bytes"))
BUG();
update_tc_infos(e, tc);
e->e_flags &= ~ELEMENT_FLAG_CREATED;
}
update_tc_attrs(e, tc);
element_notify_update(e, NULL);
element_lifesign(e, 1);
return e;
}
static void handle_cls(struct nl_object *obj, void *arg)
{
struct rtnl_cls *cls = (struct rtnl_cls *) obj;
struct rdata *rdata = arg;
handle_tc_obj((struct rtnl_tc *) cls, "cls", rdata);
}
static void handle_class(struct nl_object *obj, void *arg)
{
struct rtnl_tc *tc = (struct rtnl_tc *) obj;
struct element *e;
struct rdata *rdata = arg;
struct rdata ndata = {
.level = rdata->level + 1,
};
if (!(e = handle_tc_obj(tc, "class", rdata)))
return;
ndata.parent = e;
if (!strcmp(rtnl_tc_get_kind(tc), "htb"))
element_set_txmax(e, rtnl_htb_get_rate((struct rtnl_class *) tc));
find_classes(rtnl_tc_get_handle(tc), &ndata);
find_qdiscs(rtnl_tc_get_handle(tc), &ndata);
}
static void find_qdiscs(uint32_t parent, struct rdata *rdata)
{
struct rtnl_qdisc *filter;
if (!(filter = rtnl_qdisc_alloc()))
return;
rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter),
handle_qdisc, rdata);
rtnl_qdisc_put(filter);
}
static void find_cls(int ifindex, uint32_t parent, struct rdata *rdata)
{
struct nl_cache *cls_cache;
if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
return;
nl_cache_foreach(cls_cache, handle_cls, rdata);
nl_cache_free(cls_cache);
}
static void find_classes(uint32_t parent, struct rdata *rdata)
{
struct rtnl_class *filter;
if (!(filter = rtnl_class_alloc()))
return;
rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
nl_cache_foreach_filter(class_cache, OBJ_CAST(filter),
handle_class, rdata);
rtnl_class_put(filter);
}
static void handle_qdisc(struct nl_object *obj, void *arg)
{
struct rtnl_tc *tc = (struct rtnl_tc *) obj;
struct element *e;
struct rdata *rdata = arg;
struct rdata ndata = {
.level = rdata->level + 1,
};
if (!(e = handle_tc_obj(tc, "qdisc", rdata)))
return;
ndata.parent = e;
find_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata);
if (rtnl_tc_get_parent(tc) == TC_H_ROOT) {
find_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT, &ndata);
find_classes(TC_H_ROOT, &ndata);
}
find_classes(rtnl_tc_get_handle(tc), &ndata);
}
static void handle_tc(struct element *e, struct rtnl_link *link)
{
struct rtnl_qdisc *qdisc;
int ifindex = rtnl_link_get_ifindex(link);
struct rdata rdata = {
.level = 1,
.parent = e,
};
if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0)
return;
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT);
if (qdisc) {
handle_qdisc(OBJ_CAST(qdisc), &rdata);
rtnl_qdisc_put(qdisc);
}
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0);
if (qdisc) {
handle_qdisc(OBJ_CAST(qdisc), &rdata);
rtnl_qdisc_put(qdisc);
}
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS);
if (qdisc) {
handle_qdisc(OBJ_CAST(qdisc), &rdata);
rtnl_qdisc_put(qdisc);
}
nl_cache_free(class_cache);
}
static void update_link_infos(struct element *e, struct rtnl_link *link)
{
char buf[64];
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_mtu(link));
element_update_info(e, "MTU", buf);
rtnl_link_flags2str(rtnl_link_get_flags(link), buf, sizeof(buf));
element_update_info(e, "Flags", buf);
rtnl_link_operstate2str(rtnl_link_get_operstate(link),
buf, sizeof(buf));
element_update_info(e, "Operstate", buf);
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_ifindex(link));
element_update_info(e, "IfIndex", buf);
nl_addr2str(rtnl_link_get_addr(link), buf, sizeof(buf));
element_update_info(e, "Address", buf);
nl_addr2str(rtnl_link_get_broadcast(link), buf, sizeof(buf));
element_update_info(e, "Broadcast", buf);
rtnl_link_mode2str(rtnl_link_get_linkmode(link),
buf, sizeof(buf));
element_update_info(e, "Mode", buf);
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_txqlen(link));
element_update_info(e, "TXQlen", buf);
nl_af2str(rtnl_link_get_family(link), buf, sizeof(buf));
element_update_info(e, "Family", buf);
element_update_info(e, "Alias",
rtnl_link_get_ifalias(link) ? : "");
element_update_info(e, "Qdisc",
rtnl_link_get_qdisc(link) ? : "");
if (rtnl_link_get_link(link)) {
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_link(link));
element_update_info(e, "SlaveOfIndex", buf);
}
}
static void do_link(struct nl_object *obj, void *arg)
{
struct rtnl_link *link = (struct rtnl_link *) obj;
struct element *e, *e_parent = NULL;
int i, master_ifindex;
if (!cfg_show_all && !(rtnl_link_get_flags(link) & IFF_UP)) {
/* FIXME: delete element */
return;
}
/* Check if the interface is a slave of another interface */
if ((master_ifindex = rtnl_link_get_link(link))) {
char parent[IFNAMSIZ+1];
rtnl_link_i2name(link_cache, master_ifindex,
parent, sizeof(parent));
e_parent = element_lookup(grp, parent, master_ifindex, NULL, 0);
}
if (!(e = element_lookup(grp, rtnl_link_get_name(link),
rtnl_link_get_ifindex(link), e_parent, ELEMENT_CREAT)))
return;
if (e->e_flags & ELEMENT_FLAG_CREATED) {
if (e->e_parent)
e->e_level = e->e_parent->e_level + 1;
if (element_set_key_attr(e, "bytes", "packets") ||
element_set_usage_attr(e, "bytes"))
BUG();
/* FIXME: Update link infos every 1s or so */
update_link_infos(e, link);
e->e_flags &= ~ELEMENT_FLAG_CREATED;
}
for (i = 0; i < ARRAY_SIZE(link_attrs); i++) {
struct attr_map *m = &link_attrs[i];
uint64_t c_rx = 0, c_tx = 0;
int flags = 0;
if (m->rxid >= 0) {
c_rx = rtnl_link_get_stat(link, m->rxid);
flags |= UPDATE_FLAG_RX;
}
if (m->txid >= 0) {
c_tx = rtnl_link_get_stat(link, m->txid);
flags |= UPDATE_FLAG_TX;
}
attr_update(e, m->attrid, c_rx, c_tx, flags);
}
if (!c_notc)
handle_tc(e, link);
element_notify_update(e, NULL);
element_lifesign(e, 1);
}
static void netlink_read(void)
{
int err;
if ((err = nl_cache_resync(sock, link_cache, NULL, NULL)) < 0) {
fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err));
goto disable;
}
if ((err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) {
fprintf(stderr, "Unable to resync qdisc cache: %s\n", nl_geterror(err));
goto disable;
}
nl_cache_foreach(link_cache, do_link, NULL);
return;
disable:
netlink_ops.m_flags &= ~BMON_MODULE_ENABLED;
}
static void netlink_shutdown(void)
{
nl_cache_free(link_cache);
nl_cache_free(qdisc_cache);
nl_socket_free(sock);
}
static int netlink_do_init(void)
{
int err, i;
if (!(sock = nl_socket_alloc())) {
fprintf(stderr, "Unable to allocate netlink socket\n");
goto disable;
}
if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0) {
fprintf(stderr, "Unable to connect netlink socket: %s\n", nl_geterror(err));
goto disable;
}
if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) {
fprintf(stderr, "Unable to allocate link cache: %s\n", nl_geterror(err));
goto disable;
}
if ((err = rtnl_qdisc_alloc_cache(sock, &qdisc_cache)) < 0) {
fprintf(stderr, "Unable to allocate qdisc cache: %s\n", nl_geterror(err));
goto disable;
}
if (attr_map_load(link_attrs, ARRAY_SIZE(link_attrs)) ||
attr_map_load(tc_attrs, ARRAY_SIZE(tc_attrs)))
BUG();
if (!(grp = group_lookup(DEFAULT_GROUP, GROUP_CREATE)))
BUG();
return 0;
disable:
return -EOPNOTSUPP;
}
static int netlink_probe(void)
{
struct nl_sock *sock;
struct nl_cache *lc;
int ret = 0;
if (!(sock = nl_socket_alloc()))
return 0;
if (nl_connect(sock, NETLINK_ROUTE) < 0)
return 0;
if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &lc) == 0) {
nl_cache_free(lc);
ret = 1;
}
nl_socket_free(sock);
return ret;
}
static void print_help(void)
{
printf(
"netlink - Netlink statistic collector for Linux\n" \
"\n" \
" Powerful statistic collector for Linux using netlink sockets\n" \
" to collect link and traffic control statistics.\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" notc Do not collect traffic control statistics\n");
}
static void netlink_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "notc"))
c_notc = 1;
else if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static struct bmon_module netlink_ops = {
.m_name = "netlink",
.m_flags = BMON_MODULE_DEFAULT,
.m_do = netlink_read,
.m_shutdown = netlink_shutdown,
.m_parse_opt = netlink_parse_opt,
.m_probe = netlink_probe,
.m_init = netlink_do_init,
};
static void __init netlink_init(void)
{
input_register(&netlink_ops);
}

62
src/in_null.c Normal file
View File

@ -0,0 +1,62 @@
/*
* in_null.c Null Input Method
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/input.h>
#include <bmon/utils.h>
static void null_read(void)
{
DBG("null: reading...");
}
static void print_help(void)
{
printf(
"null - Do not collect statistics at all\n" \
"\n" \
" Will not collect any statistics at all, used to disable \n" \
" local statistics collection.\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n");
}
static void null_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static struct bmon_module null_ops = {
.m_name = "null",
.m_do = null_read,
.m_parse_opt = null_parse_opt,
};
static void __init null_init(void)
{
input_register(&null_ops);
}

234
src/in_proc.c Normal file
View File

@ -0,0 +1,234 @@
/*
* in_proc.c /proc/net/dev Input (Linux)
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/input.h>
#include <bmon/element.h>
#include <bmon/group.h>
#include <bmon/attr.h>
#include <bmon/utils.h>
static const char *c_path = "/proc/net/dev";
static const char *c_group = DEFAULT_GROUP;
static struct element_group *grp;
enum {
PROC_BYTES,
PROC_PACKETS,
PROC_ERRORS,
PROC_DROP,
PROC_COMPRESSED,
PROC_FIFO,
PROC_FRAME,
PROC_MCAST,
NUM_PROC_VALUE,
};
static struct attr_map link_attrs[NUM_PROC_VALUE] = {
{
.name = "bytes",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_BYTE,
.description = "Bytes",
},
{
.name = "packets",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Packtes",
},
{
.name = "errors",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Errors",
},
{
.name = "drop",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Dropped",
},
{
.name = "compressed",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Compressed",
},
{
.name = "fifoerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "FIFO Error",
},
{
.name = "frameerr",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Frame Error",
},
{
.name = "mcast",
.type = ATTR_TYPE_COUNTER,
.unit = UNIT_NUMBER,
.description = "Multicast",
}
};
static void proc_read(void)
{
struct element *e;
FILE *fd;
char buf[512], *p, *s;
int w;
if (!(fd = fopen(c_path, "r")))
quit("Unable to open file %s: %s\n", c_path, strerror(errno));
/* Ignore header */
fgets(buf, sizeof(buf), fd);
fgets(buf, sizeof(buf), fd);
for (; fgets(buf, sizeof(buf), fd);) {
uint64_t data[NUM_PROC_VALUE][2];
int i;
if (buf[0] == '\r' || buf[0] == '\n')
continue;
if (!(p = strchr(buf, ':')))
continue;
*p = '\0';
s = (p + 1);
for (p = &buf[0]; *p == ' '; p++);
w = sscanf(s, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " "
"%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " "
"%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " "
"%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64
"\n",
&data[PROC_BYTES][0],
&data[PROC_PACKETS][0],
&data[PROC_ERRORS][0],
&data[PROC_DROP][0],
&data[PROC_FIFO][0],
&data[PROC_FRAME][0],
&data[PROC_COMPRESSED][0],
&data[PROC_MCAST][0],
&data[PROC_BYTES][1],
&data[PROC_PACKETS][1],
&data[PROC_ERRORS][1],
&data[PROC_DROP][1],
&data[PROC_FIFO][1],
&data[PROC_FRAME][1],
&data[PROC_COMPRESSED][1],
&data[PROC_MCAST][1]);
if (w != 16)
continue;
if (!(e = element_lookup(grp, p, 0, NULL, ELEMENT_CREAT)))
return;
if (e->e_flags & ELEMENT_FLAG_CREATED) {
if (element_set_key_attr(e, "bytes", "packets") ||
element_set_usage_attr(e, "bytes"))
BUG();
e->e_flags &= ~ELEMENT_FLAG_CREATED;
}
for (i = 0; i < ARRAY_SIZE(link_attrs); i++) {
struct attr_map *m = &link_attrs[i];
attr_update(e, m->attrid, data[i][0], data[i][1],
UPDATE_FLAG_RX | UPDATE_FLAG_TX);
}
element_notify_update(e, NULL);
element_lifesign(e, 1);
}
fclose(fd);
}
static void print_help(void)
{
printf(
"proc - procfs statistic collector for Linux" \
"\n" \
" Reads statistics from procfs (/proc/net/dev)\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" file=PATH Path to statistics file (default: /proc/net/dev)\n"
" group=NAME Name of group\n");
}
static void proc_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "file") && value)
c_path = value;
else if (!strcasecmp(type, "group") && value)
c_group = value;
else if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static int proc_do_init(void)
{
if (attr_map_load(link_attrs, ARRAY_SIZE(link_attrs)) ||
!(grp = group_lookup(c_group, GROUP_CREATE)))
BUG();
return 0;
}
static int proc_probe(void)
{
FILE *fd = fopen(c_path, "r");
if (fd) {
fclose(fd);
return 1;
}
return 0;
}
static struct bmon_module proc_ops = {
.m_name = "proc",
.m_do = proc_read,
.m_parse_opt = proc_parse_opt,
.m_probe = proc_probe,
.m_init = proc_do_init,
};
static void __init proc_init(void)
{
input_register(&proc_ops);
}

83
src/input.c Normal file
View File

@ -0,0 +1,83 @@
/*
* input.c Input API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/input.h>
#include <bmon/module.h>
#include <bmon/utils.h>
static struct bmon_subsys input_subsys;
void input_register(struct bmon_module *m)
{
module_register(&input_subsys, m);
}
static void activate_default(void)
{
/*
* Try to activate a default input module if the user did not make
* a selection
*/
if (!input_subsys.s_nmod) {
struct bmon_module *m;
if (!input_set("netlink"))
return;
if (!input_set("proc"))
return;
/* Fall back to anything that could act as default */
list_for_each_entry(m, &input_subsys.s_mod_list, m_list) {
if (m->m_flags & BMON_MODULE_DEFAULT)
if (!input_set(m->m_name))
return;
}
quit("No input module found\n");
}
}
void input_read(void)
{
module_foreach_run_enabled(&input_subsys);
}
int input_set(const char *name)
{
return module_set(&input_subsys, name);
}
static struct bmon_subsys input_subsys = {
.s_name = "input",
.s_activate_default = &activate_default,
.s_mod_list = LIST_SELF(input_subsys.s_mod_list),
};
static void __init __input_init(void)
{
module_register_subsys(&input_subsys);
}

195
src/module.c Normal file
View File

@ -0,0 +1,195 @@
/*
* module.c Module API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/module.h>
#include <bmon/utils.h>
static LIST_HEAD(subsys_list);
static void module_foreach(struct bmon_subsys *ss, void (*cb)(struct bmon_module *))
{
struct bmon_module *m;
list_for_each_entry(m, &ss->s_mod_list, m_list)
cb(m);
}
void module_foreach_run_enabled_pre(struct bmon_subsys *ss)
{
struct bmon_module *m;
list_for_each_entry(m, &ss->s_mod_list, m_list)
if (m->m_flags & BMON_MODULE_ENABLED && m->m_pre)
m->m_pre();
}
void module_foreach_run_enabled(struct bmon_subsys *ss)
{
struct bmon_module *m;
list_for_each_entry(m, &ss->s_mod_list, m_list)
if (m->m_flags & BMON_MODULE_ENABLED && m->m_do)
m->m_do();
}
void module_foreach_run_enabled_post(struct bmon_subsys *ss)
{
struct bmon_module *m;
list_for_each_entry(m, &ss->s_mod_list, m_list)
if (m->m_flags & BMON_MODULE_ENABLED && m->m_post)
m->m_post();
}
static struct bmon_module *module_lookup(struct bmon_subsys *ss, const char *name)
{
struct bmon_module *m;
list_for_each_entry(m, &ss->s_mod_list, m_list)
if (!strcmp(m->m_name, name))
return m;
return NULL;
}
static void module_list(struct bmon_subsys *ss, struct list_head *list)
{
printf("%s modules:\n", ss->s_name);
if (list_empty(list))
printf("\tNo %s modules found.\n", ss->s_name);
else {
struct bmon_module *m;
list_for_each_entry(m, list, m_list)
printf("\t%s\n", m->m_name);
}
}
static int module_configure(struct bmon_module *m, module_conf_t *cfg)
{
DBG("Configuring module %s", m->m_name);
if (m->m_parse_opt) {
tv_t *tv;
list_for_each_entry(tv, &cfg->m_attrs, tv_list)
m->m_parse_opt(tv->tv_type, tv->tv_value);
}
if (m->m_probe && !m->m_probe())
return -EINVAL;
return 0;
}
void module_register(struct bmon_subsys *ss, struct bmon_module *m)
{
list_add_tail(&m->m_list, &ss->s_mod_list);
}
int module_set(struct bmon_subsys *ss, const char *name)
{
struct bmon_module *mod;
struct list_head *list;
LIST_HEAD(tmp_list);
module_conf_t *m;
int nmod = 0;
if (!name || !strcasecmp(name, "list")) {
module_list(ss, &ss->s_mod_list);
return 1;
}
parse_module_param(name, &tmp_list);
list_for_each_entry(m, &tmp_list, m_list) {
if (!(mod = module_lookup(ss, m->m_name)))
quit("Unknown %s module: %s\n", ss->s_name, m->m_name);
if (module_configure(mod, m) == 0) {
DBG("Enabling module %s", mod->m_name);
mod->m_flags |= BMON_MODULE_ENABLED;
nmod++;
}
}
if (!nmod)
quit("No working %s module found\n", ss->s_name);
DBG("Configured %d modules", nmod);
ss->s_nmod = nmod;
return 0;
}
static void __module_init(struct bmon_module *m)
{
if (m->m_init) {
DBG("Initializing %s...", m->m_name);
if (m->m_init())
m->m_flags &= ~BMON_MODULE_ENABLED;
}
}
void module_init(void)
{
struct bmon_subsys *ss;
DBG("Initializing modules");
list_for_each_entry(ss, &subsys_list, s_list) {
if (ss->s_activate_default)
ss->s_activate_default();
module_foreach(ss, __module_init);
}
}
static void __module_shutdown(struct bmon_module *m)
{
if (m->m_shutdown) {
DBG("Shutting down %s...", m->m_name);
m->m_shutdown();
m->m_flags &= ~BMON_MODULE_ENABLED;
}
}
void module_shutdown(void)
{
DBG("Shutting down modules");
struct bmon_subsys *ss;
list_for_each_entry(ss, &subsys_list, s_list)
module_foreach(ss, __module_shutdown);
}
void module_register_subsys(struct bmon_subsys *ss)
{
DBG("Module %s registered", ss->s_name);
list_add_tail(&ss->s_list, &subsys_list);
}

299
src/out_ascii.c Normal file
View File

@ -0,0 +1,299 @@
/*
* out_ascii.c ASCII Output
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/output.h>
#include <bmon/group.h>
#include <bmon/element.h>
#include <bmon/attr.h>
#include <bmon/graph.h>
#include <bmon/history.h>
#include <bmon/utils.h>
typedef enum diagram_type_e {
D_LIST,
D_GRAPH,
D_DETAILS
} diagram_type_t;
static struct graph_cfg graph_cfg = {
.gc_foreground = '*',
.gc_background = ' ',
.gc_noise = '.',
.gc_unknown = '?',
.gc_height = 6,
};
static diagram_type_t c_diagram_type = D_LIST;
static char *c_hist = "second";
static int c_quit_after = -1;
static void print_list(struct element *e)
{
char *rxu1 = "", *txu1 = "", *rxu2 = "", *txu2 = "";
double rx1 = 0.0f, tx1 = 0.0f, rx2 = 0.0f, tx2 = 0.0f;
int rx1prec, tx1prec, rx2prec, tx2prec;
char pad[IFNAMSIZ + 32];
struct attr *a;
if (e->e_key_attr[GT_MAJOR] &&
(a = attr_lookup(e, e->e_key_attr[GT_MAJOR]->ad_id)))
attr_rate2float(a, &rx1, &rxu1, &rx1prec,
&tx1, &txu1, &tx1prec);
if (e->e_key_attr[GT_MINOR] &&
(a = attr_lookup(e, e->e_key_attr[GT_MINOR]->ad_id)))
attr_rate2float(a, &rx2, &rxu2, &rx2prec,
&tx2, &txu2, &tx2prec);
memset(pad, 0, sizeof(pad));
memset(pad, ' ', e->e_level < 15 ? e->e_level : 15);
strncat(pad, e->e_name, sizeof(pad) - strlen(pad) - 1);
if (e->e_description) {
strncat(pad, " (", sizeof(pad) - strlen(pad) - 1);
strncat(pad, e->e_description, sizeof(pad) - strlen(pad) - 1);
strncat(pad, ")", sizeof(pad) - strlen(pad) - 1);
}
printf(" %-36s %8.*f%-3s %8.*f%-3s ",
pad, rx1prec, rx1, rxu1, rx2prec, rx2, rxu2);
if (e->e_rx_usage == FLT_MAX)
printf(" ");
else
printf("%2.0f%%", e->e_rx_usage);
printf(" %8.*f%-3s %8.*f%-3s ", tx1prec, tx1, txu1, tx2prec, tx2, txu2);
if (e->e_tx_usage == FLT_MAX)
printf(" \n");
else
printf("%2.0f%%\n", e->e_tx_usage);
}
static void print_attr_detail(struct element *e, struct attr *a, void *arg)
{
char *rx_u, *tx_u;
int rxprec, txprec;
double rx = unit_value2str(a->a_rx_rate.r_total,
a->a_def->ad_unit,
&rx_u, &rxprec);
double tx = unit_value2str(a->a_tx_rate.r_total,
a->a_def->ad_unit,
&tx_u, &txprec);
printf(" %-36s %12.*f%-3s %12.*f%-3s\n",
a->a_def->ad_description, rxprec, rx, rx_u, txprec, tx, tx_u);
}
static void print_details(struct element *e)
{
printf(" %s", e->e_name);
if (e->e_id)
printf(" (%u)", e->e_id);
printf("\n");
element_foreach_attr(e, print_attr_detail, NULL);
printf("\n");
}
static void print_table(struct graph *g, struct graph_table *tbl, const char *hdr)
{
int i;
if (!tbl->gt_table)
return;
printf("%s %s\n", hdr, tbl->gt_y_unit);
for (i = (g->g_cfg.gc_height - 1); i >= 0; i--)
printf("%8.2f %s\n", tbl->gt_scale[i],
tbl->gt_table + (i * graph_row_size(&g->g_cfg)));
printf(" 1 5 10 15 20 25 30 35 40 " \
"45 50 55 60\n");
}
static void __print_graph(struct element *e, struct attr *a, void *arg)
{
struct history *h;
struct graph *g;
if (!(a->a_flags & ATTR_DOING_HISTORY))
return;
graph_cfg.gc_unit = a->a_def->ad_unit;
list_for_each_entry(h, &a->a_history_list, h_list) {
if (strcasecmp(c_hist, h->h_definition->hd_name))
continue;
g = graph_alloc(h, &graph_cfg);
graph_refill(g, h);
printf("Interface: %s\n", e->e_name);
printf("Attribute: %s\n", a->a_def->ad_description);
print_table(g, &g->g_rx, "RX");
print_table(g, &g->g_tx, "TX");
graph_free(g);
}
}
static void print_graph(struct element *e)
{
element_foreach_attr(e, __print_graph, NULL);
}
static void ascii_draw_element(struct element_group *g, struct element *e,
void *arg)
{
switch (c_diagram_type) {
case D_LIST:
print_list(e);
break;
case D_DETAILS:
print_details(e);
break;
case D_GRAPH:
print_graph(e);
break;
}
}
static void ascii_draw_group(struct element_group *g, void *arg)
{
if (c_diagram_type == D_LIST)
printf("%-37s%10s %11s %%%10s %11s %%\n",
g->g_hdr->gh_title,
g->g_hdr->gh_column[0],
g->g_hdr->gh_column[1],
g->g_hdr->gh_column[2],
g->g_hdr->gh_column[3]);
else
printf("%s\n", g->g_hdr->gh_title);
group_foreach_element(g, ascii_draw_element, NULL);
}
static void ascii_draw(void)
{
group_foreach(ascii_draw_group, NULL);
if (c_quit_after > 0)
if (--c_quit_after == 0)
exit(0);
}
static void print_help(void)
{
printf(
"ascii - ASCII Output\n" \
"\n" \
" Plain configurable ASCII output.\n" \
"\n" \
" scriptable: (output graph for eth1 10 times)\n" \
" bmon -p eth1 -o 'ascii:diagram=graph;quitafter=10'\n" \
" show details for all ethernet interfaces:\n" \
" bmon -p 'eth*' -o 'ascii:diagram=details;quitafter=1'\n" \
"\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" diagram=TYPE Diagram type\n" \
" fgchar=CHAR Foreground character (default: '*')\n" \
" bgchar=CHAR Background character (default: '.')\n" \
" nchar=CHAR Noise character (default: ':')\n" \
" uchar=CHAR Unknown character (default: '?')\n" \
" height=NUM Height of graph (default: 6)\n" \
" xunit=UNIT X-Axis Unit (default: seconds)\n" \
" yunit=UNIT Y-Axis Unit (default: dynamic)\n" \
" quitafter=NUM Quit bmon after NUM outputs\n");
}
static void ascii_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "diagram") && value) {
if (tolower(*value) == 'l')
c_diagram_type = D_LIST;
else if (tolower(*value) == 'g')
c_diagram_type = D_GRAPH;
else if (tolower(*value) == 'd')
c_diagram_type = D_DETAILS;
else
quit("Unknown diagram type '%s'\n", value);
} else if (!strcasecmp(type, "fgchar") && value)
graph_cfg.gc_foreground = value[0];
else if (!strcasecmp(type, "bgchar") && value)
graph_cfg.gc_background = value[0];
else if (!strcasecmp(type, "nchar") && value)
graph_cfg.gc_noise = value[0];
#if 0
else if (!strcasecmp(type, "uchar") && value)
set_unk_char(value[0]);
#endif
else if (!strcasecmp(type, "xunit") && value)
c_hist = strdup(value);
#if 0
else if (!strcasecmp(type, "yunit") && value) {
struct unit *u;
if (!(u = unit_lookup(value)))
quit("Unknown unit '%s'\n", value);
graph_cfg.gc_unit = u;
#endif
else if (!strcasecmp(type, "height") && value)
graph_cfg.gc_height = strtol(value, NULL, 0);
else if (!strcasecmp(type, "quitafter") && value)
c_quit_after = strtol(value, NULL, 0);
else if (!strcasecmp(type, "help")) {
print_help();
exit(0);
} else
quit("Unknown module option '%s'\n", type);
}
static struct bmon_module ascii_ops = {
.m_name = "ascii",
.m_do = ascii_draw,
.m_parse_opt = ascii_parse_opt,
};
static void __init ascii_init(void)
{
output_register(&ascii_ops);
}

1277
src/out_curses.c Normal file

File diff suppressed because it is too large Load Diff

373
src/out_format.c Normal file
View File

@ -0,0 +1,373 @@
/*
* out_format.c Formatted Output
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/graph.h>
#include <bmon/conf.h>
#include <bmon/output.h>
#include <bmon/group.h>
#include <bmon/element.h>
#include <bmon/input.h>
#include <bmon/utils.h>
#include <bmon/attr.h>
static int c_quit_after = -1;
static char *c_format;
static int c_debug = 0;
static FILE *c_fd;
enum {
OT_STRING,
OT_TOKEN
};
static struct out_token {
int ot_type;
char *ot_str;
} *out_tokens;
static int token_index;
static int out_tokens_size;
static char *get_token(struct element_group *g, struct element *e,
const char *token, char *buf, size_t len)
{
if (!strncasecmp(token, "group:", 6)) {
const char *n = token + 6;
if (!strcasecmp(n, "nelements")) {
snprintf(buf, len, "%u", g->g_nelements);
return buf;
} else if (!strcasecmp(n, "name"))
return g->g_name;
else if (!strcasecmp(n, "title"))
return g->g_hdr->gh_title;
} else if (!strncasecmp(token, "element:", 8)) {
const char *n = token + 8;
if (!strcasecmp(n, "name"))
return e->e_name;
else if (!strcasecmp(n, "description"))
return e->e_description;
else if (!strcasecmp(n, "nattrs")) {
snprintf(buf, len, "%u", e->e_nattrs);
return buf;
} else if (!strcasecmp(n, "lifecycles")) {
snprintf(buf, len, "%u", e->e_lifecycles);
return buf;
} else if (!strcasecmp(n, "level")) {
snprintf(buf, len, "%u", e->e_level);
return buf;
} else if (!strcasecmp(n, "parent"))
return e->e_parent ? e->e_parent->e_name : "";
else if (!strcasecmp(n, "id")) {
snprintf(buf, len, "%u", e->e_id);
return buf;
} else if (!strcasecmp(n, "rxusage")) {
snprintf(buf, len, "%2.0f",
e->e_rx_usage == FLT_MAX ? e->e_rx_usage : 0.0f);
return buf;
} else if (!strcasecmp(n, "txusage")) {
snprintf(buf, len, "%2.0f",
e->e_tx_usage == FLT_MAX ? e->e_tx_usage : 0.0f);
return buf;
} else if (!strcasecmp(n, "haschilds")) {
snprintf(buf, len, "%u",
list_empty(&e->e_childs) ? 0 : 1);
return buf;
}
} else if (!strncasecmp(token, "attr:", 5)) {
const char *type = token + 5;
char *name = strchr(type, ':');
struct attr_def *def;
struct attr *a;
if (!name) {
fprintf(stderr, "Invalid attribute field \"%s\"\n",
type);
goto out;
}
name++;
def = attr_def_lookup(name);
if (!def) {
fprintf(stderr, "Undefined attribute \"%s\"\n", name);
goto out;
}
if (!(a = attr_lookup(e, def->ad_id))) {
fprintf(stderr, "Unable to find attribute %u (%s)\n",
def->ad_id, name);
goto out;
}
if (!strncasecmp(type, "rx:", 3)) {
snprintf(buf, len, "%lu", a->a_rx_rate.r_total);
return buf;
} else if (!strncasecmp(type, "tx:", 3)) {
snprintf(buf, len, "%lu", a->a_tx_rate.r_total);
return buf;
} else if (!strncasecmp(type, "rxrate:", 7)) {
snprintf(buf, len, "%.2f", a->a_rx_rate.r_rate);
return buf;
} else if (!strncasecmp(token+5, "txrate:", 7))
snprintf(buf, len, "%.2f", a->a_tx_rate.r_rate);
return buf;
}
fprintf(stderr, "Unknown field \"%s\"\n", token);
out:
return "unknown";
}
static void draw_element(struct element_group *g, struct element *e, void *arg)
{
int i;
for (i = 0; i < token_index; i++) {
char buf[128];
char *p;
if (out_tokens[i].ot_type == OT_STRING)
p = out_tokens[i].ot_str;
else if (out_tokens[i].ot_type == OT_TOKEN)
p = get_token(g, e, out_tokens[i].ot_str,
buf, sizeof(buf));
else
BUG();
if (p)
fprintf(c_fd, "%s", p);
}
}
static void format_draw(void)
{
group_foreach_recursive(draw_element, NULL);
if (c_quit_after > 0)
if (--c_quit_after == 0)
exit(0);
}
static inline void add_token(int type, char *data)
{
if (!out_tokens_size) {
out_tokens_size = 32;
out_tokens = calloc(out_tokens_size, sizeof(struct out_token));
if (out_tokens == NULL)
quit("Cannot allocate out token array\n");
}
if (out_tokens_size <= token_index) {
out_tokens_size += 32;
out_tokens = realloc(out_tokens, out_tokens_size * sizeof(struct out_token));
if (out_tokens == NULL)
quit("Cannot reallocate out token array\n");
}
out_tokens[token_index].ot_type = type;
out_tokens[token_index].ot_str = data;
token_index++;
}
static int format_probe(void)
{
int new_one = 1;
char *p, *e;
for (p = c_format; *p; p++) {
if (*p == '$') {
char *s = p;
s++;
if (*s == '(') {
s++;
if (!*s)
goto unexpected_end;
e = strchr(s, ')');
if (e == NULL)
goto invalid;
*p = '\0';
*e = '\0';
add_token(OT_TOKEN, s);
new_one = 1;
p = e;
continue;
}
}
if (*p == '\\') {
char *s = p;
s++;
switch (*s) {
case 'n':
*s = '\n';
goto finish_escape;
case 't':
*s = '\t';
goto finish_escape;
case 'r':
*s = '\r';
goto finish_escape;
case 'v':
*s = '\v';
goto finish_escape;
case 'b':
*s = '\b';
goto finish_escape;
case 'f':
*s = '\f';
goto finish_escape;
case 'a':
*s = '\a';
goto finish_escape;
}
goto out;
finish_escape:
*p = '\0';
add_token(OT_STRING, s);
p = s;
new_one = 0;
continue;
}
out:
if (new_one) {
add_token(OT_STRING, p);
new_one = 0;
}
}
if (c_debug) {
int i;
for (i = 0; i < token_index; i++)
printf(">>%s<\n", out_tokens[i].ot_str);
}
return 1;
unexpected_end:
fprintf(stderr, "Unexpected end of format string\n");
return 0;
invalid:
fprintf(stderr, "Missing ')' in format string\n");
return 0;
}
static void print_help(void)
{
printf(
"format - Formatable Output\n" \
"\n" \
" Formatable ASCII output for scripts. Calls a drawing function for\n" \
" every item per node and outputs according to the specified format\n" \
" string. The format string consists of normal text and placeholders\n" \
" in the form of $(placeholder).\n" \
"\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" fmt=FORMAT Format string\n" \
" stderr Write to stderr instead of stdout\n" \
" quitafter=NUM Quit bmon after NUM outputs\n" \
"\n" \
" Placeholders:\n" \
" group:nelements Number of elements this group\n" \
" :name Name of group\n" \
" :title Title of group\n" \
" element:name Name of element\n" \
" :desc Description of element\n" \
" :nattrs Number of attributes\n" \
" :lifecycles Number of lifecycles\n" \
" :level Indentation level\n" \
" :parent Name of parent element\n" \
" :rxusage RX usage in percent\n" \
" :txusage TX usage in percent)\n" \
" :id ID of element\n" \
" :haschilds Indicate if element has childs (0|1)\n" \
" attr:rx:<name> RX counter of attribute <name>\n" \
" :tx:<name> TX counter of attribute <name>\n" \
" :rxrate:<name> RX rate of attribute <name>\n" \
" :txrate:<name> TX rate of attribute <name>\n" \
"\n" \
" Supported Escape Sequences: \\n, \\t, \\r, \\v, \\b, \\f, \\a\n" \
"\n" \
" Examples:\n" \
" \"$(element:name)\\t$(attr:rx:bytes)\\t$(attr:tx:bytes)\\n\"\n" \
" lo 12074 12074\n" \
"\n" \
" \"$(element:name) $(attr:rxrate:packets) $(attr:txrate:packets)\\n\"\n" \
" eth0 33 5\n" \
"\n" \
" \"Element: $(element:name)\\nBytes Rate: \" \\\n" \
" \"$(attr:rxrate:bytes)/$(attr:txrate:bytes)\\nPackets Rate: \" \\\n" \
" \"$(attr:rxrate:packets)/$(attr:txrate:packets)\\n\"\n" \
" Item: eth0\n" \
" Bytes Rate: 49130/2119\n" \
" Packets Rate: 40/11\n" \
"\n");
}
static void format_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "stderr"))
c_fd = stderr;
else if (!strcasecmp(type, "debug"))
c_debug = 1;
else if (!strcasecmp(type, "fmt")) {
if (c_format)
free(c_format);
c_format = strdup(value);
} else if (!strcasecmp(type, "quitafter") &&
value)
c_quit_after = strtol(value, NULL, 0);
else if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static struct bmon_module format_ops = {
.m_name = "format",
.m_do = format_draw,
.m_probe = format_probe,
.m_parse_opt = format_parse_opt,
};
static void __init ascii_init(void)
{
c_fd = stdout;
c_format = strdup("$(element:name) $(attr:rx:bytes) $(attr:tx:bytes) " \
"$(attr:rx:packets) $(attr:tx:packets)\\n");
output_register(&format_ops);
}

56
src/out_null.c Normal file
View File

@ -0,0 +1,56 @@
/*
* out_null.c Null Output
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/output.h>
#include <bmon/utils.h>
static void print_help(void)
{
printf(
"null - No output\n" \
"\n" \
" Disable primary output method\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n");
}
static void null_parse_opt(const char *type, const char *value)
{
if (!strcasecmp(type, "help")) {
print_help();
exit(0);
}
}
static struct bmon_module null_ops = {
.m_name = "null",
.m_parse_opt = null_parse_opt,
};
static void __init null_init(void)
{
output_register(&null_ops);
}

95
src/output.c Normal file
View File

@ -0,0 +1,95 @@
/*
* output.c Output API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/output.h>
#include <bmon/module.h>
#include <bmon/conf.h>
#include <bmon/group.h>
#include <bmon/utils.h>
static struct bmon_subsys output_subsys;
void output_register(struct bmon_module *m)
{
module_register(&output_subsys, m);
}
static void activate_default(void)
{
/*
* Try to activate a default output module if the user did not make
* a selection
*/
if (!output_subsys.s_nmod) {
struct bmon_module *m;
if (!output_set("curses"))
return;
if (!output_set("ascii"))
return;
/* Fall back to anything that could act as default */
list_for_each_entry(m, &output_subsys.s_mod_list, m_list) {
if (m->m_flags & BMON_MODULE_DEFAULT)
if (!output_set(m->m_name))
return;
}
quit("No output module found\n");
}
}
void output_pre(void)
{
module_foreach_run_enabled_pre(&output_subsys);
}
void output_draw(void)
{
module_foreach_run_enabled(&output_subsys);
}
void output_post(void)
{
module_foreach_run_enabled_post(&output_subsys);
}
int output_set(const char *name)
{
return module_set(&output_subsys, name);
}
static struct bmon_subsys output_subsys = {
.s_name = "output",
.s_activate_default = &activate_default,
.s_mod_list = LIST_SELF(output_subsys.s_mod_list),
};
static void __init __output_init(void)
{
return module_register_subsys(&output_subsys);
}

216
src/unit.c Normal file
View File

@ -0,0 +1,216 @@
/*
* unit.c Unit Definitions
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/utils.h>
#include <bmon/unit.h>
static struct unit *byte_unit, *bit_unit, *number_unit;
static LIST_HEAD(units);
static struct list_head *get_flist(struct unit *unit)
{
static int cached = 0, div = UNIT_DEFAULT;
if (!cached && cfg_getbool(cfg, "use_si"))
div = UNIT_SI;
if (!list_empty(&unit->u_div[UNIT_SI]) && div == UNIT_SI)
return &unit->u_div[UNIT_SI];
else
return &unit->u_div[UNIT_DEFAULT];
}
struct unit *unit_lookup(const char *name)
{
struct unit *unit;
list_for_each_entry(unit, &units, u_list)
if (!strcmp(name, unit->u_name))
return unit;
return NULL;
}
/**
* Lookup best divisor to use for a certain situation
* @hint Value in question (for DYNAMIC_EXP)
* @unit Unit of value
* @name Place to store name of divisor used
* @prec Place to store suggested precision
*
* Searches for the best divisor to be used depending on the unit
* exponent configured by the user. If a dynamic exponent is
* configured, the divisor is selected based on the value of hint
* so that hint is dividied into a small float >= 1.0. The name
* of the divisor used is stored in *name.
*
* If prec points to a vaild integer, a number of precision digits
* is suggested to avoid n.00 to make pretty printing easier.
*/
uint64_t unit_divisor(uint64_t hint, struct unit *unit, char **name,
int *prec)
{
struct list_head *flist = get_flist(unit);
struct fraction *f;
if (prec)
*prec = 2;
if (cfg_unit_exp == DYNAMIC_EXP) {
list_for_each_entry_reverse(f, flist, f_list) {
if (hint >= f->f_divisor)
goto found_it;
}
} else {
int n = cfg_unit_exp;
list_for_each_entry(f, flist, f_list) {
if (--n == 0)
goto found_it;
}
}
*name = "";
return 1;
found_it:
if (f->f_divisor == 1 && prec)
*prec = 0;
*name = f->f_name;
return f->f_divisor;
}
double unit_value2str(uint64_t value, struct unit *unit,
char **name, int *prec)
{
uint64_t div = unit_divisor(value, unit, name, prec);
if (value % div == 0 && prec)
*prec = 0;
return (double) value / (double) div;
}
void fraction_free(struct fraction *f)
{
if (!f)
return;
xfree(f->f_name);
xfree(f);
}
void unit_add_div(struct unit *unit, int type, const char *txt, float div)
{
struct fraction *f;
if (!unit)
BUG();
f = xcalloc(1, sizeof(*f));
init_list_head(&f->f_list);
f->f_divisor = div;
f->f_name = strdup(txt);
list_add_tail(&f->f_list, &unit->u_div[type]);
}
struct unit *unit_add(const char *name)
{
struct unit *unit;
int i;
if (!(unit = unit_lookup(name))) {
unit = xcalloc(1, sizeof(*unit));
unit->u_name = strdup(name);
for (i = 0; i < __UNIT_MAX; i++)
init_list_head(&unit->u_div[i]);
list_add_tail(&unit->u_list, &units);
}
return unit;
}
static void unit_free(struct unit *u)
{
struct fraction *f, *n;
if (!u)
return;
list_for_each_entry_safe(f, n, &u->u_div[UNIT_DEFAULT], f_list)
fraction_free(f);
list_for_each_entry_safe(f, n, &u->u_div[UNIT_SI], f_list)
fraction_free(f);
xfree(u->u_name);
xfree(u);
}
char *unit_bytes2str(uint64_t bytes, char *buf, size_t len)
{
char *ustr;
int prec;
double v;
if (byte_unit) {
v = unit_value2str(bytes, byte_unit, &ustr, &prec);
snprintf(buf, len, "%'.*f%3s", prec, v, ustr);
} else
snprintf(buf, len, "%llu", (unsigned long long) bytes);
return buf;
}
char *unit_bit2str(uint64_t bits, char *buf, size_t len)
{
char *ustr;
int prec;
double v;
if (bit_unit) {
v = unit_value2str(bits, bit_unit, &ustr, &prec);
snprintf(buf, len, "%'.*f%3s", prec, v, ustr);
} else
snprintf(buf, len, "%llu", (unsigned long long) bits);
return buf;
}
static void __exit unit_exit(void)
{
struct unit *u, *n;
list_for_each_entry_safe(u, n, &units, u_list)
unit_free(u);
}

421
src/utils.c Normal file
View File

@ -0,0 +1,421 @@
/*
* utils.c General purpose utilities
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/utils.h>
void *xcalloc(size_t n, size_t s)
{
void *d = calloc(n, s);
if (NULL == d) {
fprintf(stderr, "xalloc: Out of memory\n");
exit(ENOMEM);
}
return d;
}
void *xrealloc(void *p, size_t s)
{
void *d = realloc(p, s);
if (NULL == d) {
fprintf(stderr, "xrealloc: Out of memory!\n");
exit(ENOMEM);
}
return d;
}
void xfree(void *d)
{
if (d)
free(d);
}
float timestamp_to_float(timestamp_t *src)
{
return (float) src->tv_sec + ((float) src->tv_usec / 1000000.0f);
}
int64_t timestamp_to_int(timestamp_t *src)
{
return (src->tv_sec * 1000000ULL) + src->tv_usec;
}
void float_to_timestamp(timestamp_t *dst, float src)
{
dst->tv_sec = (time_t) src;
dst->tv_usec = (src - ((float) ((time_t) src))) * 1000000.0f;
}
void timestamp_add(timestamp_t *dst, timestamp_t *src1, timestamp_t *src2)
{
dst->tv_sec = src1->tv_sec + src2->tv_sec;
dst->tv_usec = src1->tv_usec + src2->tv_usec;
if (dst->tv_usec >= 1000000) {
dst->tv_sec++;
dst->tv_usec -= 1000000;
}
}
void timestamp_sub(timestamp_t *dst, timestamp_t *src1, timestamp_t *src2)
{
dst->tv_sec = src1->tv_sec - src2->tv_sec;
dst->tv_usec = src1->tv_usec - src2->tv_usec;
if (dst->tv_usec < 0) {
dst->tv_sec--;
dst->tv_usec += 1000000;
}
}
int timestamp_le(timestamp_t *a, timestamp_t *b)
{
if (a->tv_sec > b->tv_sec)
return 0;
if (a->tv_sec < b->tv_sec || a->tv_usec <= b->tv_usec)
return 1;
return 0;
}
int timestamp_is_negative(timestamp_t *ts)
{
return (ts->tv_sec < 0 || ts->tv_usec < 0);
}
void update_timestamp(timestamp_t *dst)
{
struct timeval tv;
gettimeofday(&tv, NULL);
dst->tv_sec = tv.tv_sec;
dst->tv_usec = tv.tv_usec;
}
void copy_timestamp(timestamp_t *ts1, timestamp_t *ts2)
{
ts1->tv_sec = ts2->tv_sec;
ts2->tv_usec = ts2->tv_usec;
}
float timestamp_diff(timestamp_t *t1, timestamp_t *t2)
{
float diff;
diff = t2->tv_sec - t1->tv_sec;
diff += (((float) t2->tv_usec - (float) t1->tv_usec) / 1000000.0f);
return diff;
}
#if 0
float diff_now(timestamp_t *t1)
{
timestamp_t now;
update_ts(&now);
return time_diff(t1, &now);
}
const char *xinet_ntop(struct sockaddr *src, char *dst, socklen_t cnt)
{
void *s;
int family;
if (src->sa_family == AF_INET6) {
s = &((struct sockaddr_in6 *) src)->sin6_addr;
family = AF_INET6;
} else if (src->sa_family == AF_INET) {
s = &((struct sockaddr_in *) src)->sin_addr;
family = AF_INET;
} else
return NULL;
return inet_ntop(family, s, dst, cnt);
}
uint64_t parse_size(const char *str)
{
char *p;
uint64_t l = strtol(str, &p, 0);
if (p == str)
return -1;
if (*p) {
if (!strcasecmp(p, "kb") || !strcasecmp(p, "k"))
l *= 1024;
else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g"))
l *= 1024*1024*1024;
else if (!strcasecmp(p, "gbit"))
l *= 1024*1024*1024/8;
else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m"))
l *= 1024*1024;
else if (!strcasecmp(p, "mbit"))
l *= 1024*1024/8;
else if (!strcasecmp(p, "kbit"))
l *= 1024/8;
else if (strcasecmp(p, "b") != 0)
return -1;
}
return l;
}
#ifndef HAVE_STRDUP
char *strdup(const char *s)
{
char *t = xcalloc(1, strlen(s) + 1);
memcpy(t, s, strlen(s));
return s;
}
#endif
char *timestamp2str(timestamp_t *ts, char *buf, size_t len)
{
int i, split[5];
char *units[] = {"d", "h", "m", "s", "usec"};
time_t sec = ts->tv_sec;
#define _SPLIT(idx, unit) if ((split[idx] = sec / unit) > 0) sec %= unit
_SPLIT(0, 86400); /* days */
_SPLIT(1, 3600); /* hours */
_SPLIT(2, 60); /* minutes */
_SPLIT(3, 1); /* seconds */
#undef _SPLIT
split[4] = ts->tv_usec;
memset(buf, 0, len);
for (i = 0; i < ARRAY_SIZE(split); i++) {
if (split[i] > 0) {
char t[64];
snprintf(t, sizeof(t), "%s%d%s",
strlen(buf) ? " " : "", split[i], units[i]);
strncat(buf, t, len - strlen(buf) - 1);
}
}
return buf;
}
int parse_date(const char *str, xdate_t *dst)
{
time_t now = time(NULL);
struct tm *now_tm = localtime(&now);
const char *next;
char *p;
struct tm backup;
memset(dst, 0, sizeof(*dst));
if (strchr(str, '-')) {
next = strptime(str, "%Y-%m-%d", &dst->d_tm);
if (next == NULL ||
(*next != '\0' && *next != ' '))
goto invalid_date;
} else {
dst->d_tm.tm_mday = now_tm->tm_mday;
dst->d_tm.tm_mon = now_tm->tm_mon;
dst->d_tm.tm_year = now_tm->tm_year;
dst->d_tm.tm_wday = now_tm->tm_wday;
dst->d_tm.tm_yday = now_tm->tm_yday;
dst->d_tm.tm_isdst = now_tm->tm_isdst;
next = str;
}
if (*next == '\0')
return 0;
while (*next == ' ')
next++;
if (*next == '.')
goto read_us;
/* Make a backup, we can't rely on strptime to not screw
* up what we've got so far. */
memset(&backup, 0, sizeof(backup));
memcpy(&backup, &dst->d_tm, sizeof(backup));
next = strptime(next, "%H:%M:%S", &dst->d_tm);
if (next == NULL ||
(*next != '\0' && *next != '.'))
goto invalid_date;
dst->d_tm.tm_mday = backup.tm_mday;
dst->d_tm.tm_mon = backup.tm_mon;
dst->d_tm.tm_year = backup.tm_year;
dst->d_tm.tm_wday = backup.tm_wday;
dst->d_tm.tm_yday = backup.tm_yday;
dst->d_tm.tm_isdst = backup.tm_isdst;
if (*next == '\0')
return 0;
read_us:
if (*next == '.')
next++;
else
BUG();
dst->d_usec = strtoul(next, &p, 10);
if (*p != '\0')
goto invalid_date;
return 0;
invalid_date:
fprintf(stderr, "Invalid date \"%s\"\n", str);
return -1;
}
static inline void print_token(FILE *fd, struct db_token *tok)
{
fprintf(fd, "%s", tok->t_name);
if (tok->t_flags & DB_T_ATTR)
fprintf(fd, "<attr>");
}
void db_print_filter(FILE *fd, struct db_filter *filter)
{
if (filter->f_node)
print_token(fd, filter->f_node);
if (filter->f_group) {
fprintf(fd, ".");
print_token(fd, filter->f_group);
}
if (filter->f_item) {
fprintf(fd, ".");
print_token(fd, filter->f_item);
}
if (filter->f_attr) {
fprintf(fd, ".");
print_token(fd, filter->f_attr);
}
if (filter->f_field)
fprintf(fd, "@%s", filter->f_field);
}
void *db_filter__scan_string(const char *);
void db_filter__switch_to_buffer(void *);
int db_filter_parse(void);
struct db_filter * parse_db_filter(const char *buf)
{
struct db_filter *f;
struct db_token *tok, *t;
void *state = db_filter__scan_string(buf);
db_filter__switch_to_buffer(state);
if (db_filter_parse()) {
fprintf(stderr, "Failed to parse filter \"%s\"\n", buf);
return NULL;
}
tok = db_filter_out; /* generated by yacc */
if (tok == NULL)
return NULL;
f = xcalloc(1, sizeof(*f));
f->f_node = tok;
if (!tok->t_next)
goto out;
tok = tok->t_next;
if (tok->t_flags & DB_T_ATTR) {
fprintf(stderr, "Node may not contain an attribute field\n");
goto errout;
}
f->f_group = tok;
if (!tok->t_next)
goto out;
tok = tok->t_next;
if (tok->t_flags & DB_T_ATTR) {
fprintf(stderr, "Group may not contain an attribute field\n");
goto errout;
}
f->f_item = tok;
if (!tok->t_next)
goto out;
tok = tok->t_next;
if (tok->t_flags & DB_T_ATTR) {
if (!tok->t_name)
BUG();
f->f_field = tok->t_name;
goto out;
} else
f->f_attr = tok;
if (!tok->t_next)
goto out;
tok = tok->t_next;
if (tok->t_flags & DB_T_ATTR) {
if (!tok->t_name)
BUG();
f->f_field = tok->t_name;
} else {
fprintf(stderr, "Unexpected additional token after attribute\n");
goto errout;
}
out:
/* free unused tokens */
for (tok = tok->t_next ; tok; tok = t) {
t = tok->t_next;
if (tok->t_name)
free(tok->t_name);
free(tok);
}
return f;
errout:
free(f);
f = NULL;
tok = db_filter_out;
goto out;
}
#endif