Initial import
This commit is contained in:
2
src/.gitignore
vendored
Normal file
2
src/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.o
|
||||
bmon
|
42
src/Makefile.am
Normal file
42
src/Makefile.am
Normal 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
635
src/attr.c
Normal 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
397
src/bmon.c
Normal 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
596
src/conf.c
Normal 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
537
src/element.c
Normal 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
96
src/element_cfg.c
Normal 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
235
src/graph.c
Normal 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
278
src/group.c
Normal 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
318
src/history.c
Normal 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
243
src/in_dummy.c
Normal 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
868
src/in_netlink.c
Normal 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
62
src/in_null.c
Normal 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
234
src/in_proc.c
Normal 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
83
src/input.c
Normal 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
195
src/module.c
Normal 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
299
src/out_ascii.c
Normal 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
1277
src/out_curses.c
Normal file
File diff suppressed because it is too large
Load Diff
373
src/out_format.c
Normal file
373
src/out_format.c
Normal 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
56
src/out_null.c
Normal 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
95
src/output.c
Normal 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
216
src/unit.c
Normal 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
421
src/utils.c
Normal 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
|
Reference in New Issue
Block a user