From 3be703f67d34b761725a13b6e58ab22591824bbe Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 5 Jul 2013 15:11:46 +0200 Subject: [PATCH] Initial import --- .gitignore | 21 +- ChangeLog | 0 Makefile.am | 11 + README.md | 5 + autogen.sh | 4 + configure.ac | 191 +++++ etc/bmon.conf | 81 +++ include/bmon/.gitignore | 2 + include/bmon/attr.h | 138 ++++ include/bmon/bmon.h | 82 +++ include/bmon/compile-fixes.h | 35 + include/bmon/conf.h | 91 +++ include/bmon/config.h | 158 +++++ include/bmon/defs.h.in | 246 +++++++ include/bmon/element.h | 130 ++++ include/bmon/element_cfg.h | 53 ++ include/bmon/graph.h | 72 ++ include/bmon/group.h | 100 +++ include/bmon/history.h | 93 +++ include/bmon/info.h | 42 ++ include/bmon/input.h | 52 ++ include/bmon/list.h | 93 +++ include/bmon/module.h | 75 ++ include/bmon/output.h | 39 ++ include/bmon/unit.h | 68 ++ include/bmon/utils.h | 102 +++ m4/ax_with_curses.m4 | 518 ++++++++++++++ man/Makefile.am | 3 + man/bmon.8 | 235 +++++++ src/.gitignore | 2 + src/Makefile.am | 42 ++ src/attr.c | 635 +++++++++++++++++ src/bmon.c | 397 +++++++++++ src/conf.c | 596 ++++++++++++++++ src/element.c | 537 ++++++++++++++ src/element_cfg.c | 96 +++ src/graph.c | 235 +++++++ src/group.c | 278 ++++++++ src/history.c | 318 +++++++++ src/in_dummy.c | 243 +++++++ src/in_netlink.c | 868 +++++++++++++++++++++++ src/in_null.c | 62 ++ src/in_proc.c | 234 +++++++ src/input.c | 83 +++ src/module.c | 195 ++++++ src/out_ascii.c | 299 ++++++++ src/out_curses.c | 1277 ++++++++++++++++++++++++++++++++++ src/out_format.c | 373 ++++++++++ src/out_null.c | 56 ++ src/output.c | 95 +++ src/unit.c | 216 ++++++ src/utils.c | 421 +++++++++++ 52 files changed, 10285 insertions(+), 13 deletions(-) create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 etc/bmon.conf create mode 100644 include/bmon/.gitignore create mode 100644 include/bmon/attr.h create mode 100644 include/bmon/bmon.h create mode 100644 include/bmon/compile-fixes.h create mode 100644 include/bmon/conf.h create mode 100644 include/bmon/config.h create mode 100644 include/bmon/defs.h.in create mode 100644 include/bmon/element.h create mode 100644 include/bmon/element_cfg.h create mode 100644 include/bmon/graph.h create mode 100644 include/bmon/group.h create mode 100644 include/bmon/history.h create mode 100644 include/bmon/info.h create mode 100644 include/bmon/input.h create mode 100644 include/bmon/list.h create mode 100644 include/bmon/module.h create mode 100644 include/bmon/output.h create mode 100644 include/bmon/unit.h create mode 100644 include/bmon/utils.h create mode 100644 m4/ax_with_curses.m4 create mode 100644 man/Makefile.am create mode 100644 man/bmon.8 create mode 100644 src/.gitignore create mode 100644 src/Makefile.am create mode 100644 src/attr.c create mode 100644 src/bmon.c create mode 100644 src/conf.c create mode 100644 src/element.c create mode 100644 src/element_cfg.c create mode 100644 src/graph.c create mode 100644 src/group.c create mode 100644 src/history.c create mode 100644 src/in_dummy.c create mode 100644 src/in_netlink.c create mode 100644 src/in_null.c create mode 100644 src/in_proc.c create mode 100644 src/input.c create mode 100644 src/module.c create mode 100644 src/out_ascii.c create mode 100644 src/out_curses.c create mode 100644 src/out_format.c create mode 100644 src/out_null.c create mode 100644 src/output.c create mode 100644 src/unit.c create mode 100644 src/utils.c diff --git a/.gitignore b/.gitignore index 0331bbb..ef9058c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,12 @@ -# Object files *.o - -# Libraries -*.lib *.a - -# Shared objects (inc. Windows DLLs) -*.dll *.so *.so.* -*.dylib - -# Executables -*.exe -*.out -*.app +*.swp +.deps +Makefile.in +Makefile +/build-aux/ +/aclocal.m4 +/configure +/config.* diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..533f8c5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,11 @@ +# -*- Makefile -*- + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src man + +pkgsysconfdir = $(sysconfdir) +pkgsysconf_DATA = etc/bmon.conf + +EXTRA_DIST = \ + $(pkgsysconf_DATA) diff --git a/README.md b/README.md index f47f8b3..50255a0 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,8 @@ bmon ==== bandwidth monitor and rate estimator + +bmon is a monitoring and debugging tool to capture networking related +statistics and prepare them visually in a human friendly way. It +features various output methods including an interactive curses user +interface and a programmable text output for scripting. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..a569614 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +autoreconf -fi; +rm -Rf autom4te.cache; diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..752784b --- /dev/null +++ b/configure.ac @@ -0,0 +1,191 @@ +# +# configure.in Configure Script +# +# Copyright (c) 2001-2013 Thomas Graf +# +# 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. + +AC_INIT(bmon, 3.0-git, [], [], [http://www.infradead.org/~tgr/bmon/]) +AC_CONFIG_HEADERS(include/bmon/defs.h) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) + +AC_ISC_POSIX +AC_PROG_CC +AC_PROG_CC_STDC +AC_PROG_CPP +AC_PROG_MAKE_SET +AC_PROG_INSTALL + +AC_C_CONST +AC_C_INLINE + +AC_HEADER_TIME +AC_HEADER_DIRENT + +AC_TYPE_SIZE_T +AC_TYPE_SIGNAL +AC_TYPE_PID_T + +AC_FUNC_FORK + +AC_CHECK_HEADERS(getopt.h ncurses/ncurses.h ncurses.h curses.h) +AC_CHECK_HEADERS(dirent.h sys/utsname.h sys/sockio.h netinet6/in6.h) +AC_CHECK_HEADERS(fcntl.h netdb.h netinet/in.h sysctl/ioctl.h) +AC_CHECK_HEADERS(sys/param.h sys/socket.h) + +AC_CHECK_TYPES(suseconds_t) + +AC_CHECK_FUNCS(atexit gettimeofday memset pow socket strcasecmp) +AC_CHECK_FUNCS(strchr strdup strerror strncasecmp strstr strtol) +AC_CHECK_FUNCS(uname getdate) + +AX_WITH_CURSES +if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then + AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) +fi + +PKG_CHECK_MODULES([CONFUSE], [libconfuse], [], AC_MSG_ERROR([requires libconfuse])) + +PKG_CHECK_MODULES([LIBNL], [libnl-3.0], [], AC_MSG_ERROR([requires libnl])) +PKG_CHECK_MODULES([LIBNL_ROUTE], [libnl-route-3.0], [], AC_MSG_ERROR([requires libnl3-route])) + +AC_CHECK_LIB(m, pow, [], AC_MSG_ERROR([requires libm])) + +BMON_LIB="" + +##################################################################### +## +## libcurses check +## +##################################################################### +CURSES="No " +AC_CHECK_LIB(ncurses, initscr, [ + AC_DEFINE_UNQUOTED(HAVE_NCURSES, "1", [have ncurses]) + LCURSES="ncurses" + CURSES="Yes" +],[ + AC_CHECK_LIB(curses,initscr, [ + AC_DEFINE_UNQUOTED(HAVE_CURSES, "1", [have curses]) + LCURSES="curses" + CURSES="Yes" + ],[ + echo + echo "*** Warning: Building bmon w/o curses support ***" + echo + ]) +]) + +LIBCURSES="-l$LCURSES" + +AC_CHECK_LIB($LCURSES, redrawwin, [ + AC_DEFINE_UNQUOTED(HAVE_REDRAWWIN, "1", [have redrawwin]) +]) + +AC_CHECK_LIB($LCURSES, use_default_colors, [ + AC_DEFINE_UNQUOTED(HAVE_USE_DEFAULT_COLORS, "1", [have udc]) +]) + +##################################################################### +## +## interface counter overflow workaround +## +##################################################################### +AC_ARG_ENABLE(cnt-workaround, +[ --disable-cnt-workaround Disables interface counter overflow workaround],[ + if test x$enableval = xno; then + AC_DEFINE_UNQUOTED(DISABLE_OVERFLOW_WORKAROUND,"1",[no overflow workaround]) + fi +]) + +##################################################################### +## +## curses +## +##################################################################### +AC_ARG_ENABLE(curses, +[ --disable-curses Disables curses output],[ + if test x$enableval = xno; then + CURSES="No " + fi +]) + +##################################################################### +## +## debug check +## +##################################################################### +DEBUG=0 +AC_ARG_ENABLE(debug, +[ --enable-debug Enable debug mode (default disabled)],[ + if test x$enableval = xyes; then + AC_DEFINE_UNQUOTED(DEBUG, "1", [enable debugging]) + DEBUG=1; + fi +]) + +##################################################################### +## +## target os eval +## +##################################################################### +case ${target_os} in + linux*) + AC_DEFINE_UNQUOTED(SYS_LINUX, "1", [operating system]) + ;; + + *solaris*) + AC_DEFINE_UNQUOTED(SYS_SUNOS, "1", [operating system]) + ;; + + *bsd*) + AC_DEFINE_UNQUOTED(SYS_BSD, "1", [operating system]) + ;; + + *darwin*) + AC_DEFINE_UNQUOTED(SYS_BSD, "1", [operating system]) + ;; + + *) + AC_DEFINE_UNQUOTED(SYS_OTHER, "1", [operating system]) + ;; +esac + +##################################################################### +## +## export variables +## +##################################################################### +AC_SUBST(DEBUG) +AC_SUBST(STATIC) +AC_SUBST(SYS) +AC_SUBST(ARCH) + +AC_SUBST(CURSES) + +AC_CONFIG_FILES([ +Makefile +src/Makefile +man/Makefile +]) + +AC_OUTPUT diff --git a/etc/bmon.conf b/etc/bmon.conf new file mode 100644 index 0000000..567a131 --- /dev/null +++ b/etc/bmon.conf @@ -0,0 +1,81 @@ +/* + * read_interval = 1.0 + * rate_interval = 1.0 + * variance = 0.1 + * history_variance = 0.1 + * sleep_time = 20000 + * lifetime = 30.0 + * show_all = true + * policy = "" + */ + +/* + * element eth0 { + * description = { "My description" } + * rxmax = { 10000 } + * txmax = { 10000 } + * max = { 12500000 } + * } + */ + +/* + * Default configuration + * + * The following definitions is what is compiled into bmon and used + * by default. + * + * 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. } + * size = { 60 } + * } + * + * history minute { + * interval = { 60. } + * size = { 60 } + * } + * + * history hour { + * interval = { 3600. } + * size = { 60 } + * } + * + * history day { + * interval = { 86400. } + * size = { 60 } + * } + */ diff --git a/include/bmon/.gitignore b/include/bmon/.gitignore new file mode 100644 index 0000000..7ffac03 --- /dev/null +++ b/include/bmon/.gitignore @@ -0,0 +1,2 @@ +defs.h* +stamp-h1 diff --git a/include/bmon/attr.h b/include/bmon/attr.h new file mode 100644 index 0000000..224f151 --- /dev/null +++ b/include/bmon/attr.h @@ -0,0 +1,138 @@ +/* + * bmon/attr.h Attributes + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_ATTR_H_ +#define __BMON_ATTR_H_ + +#include +#include + +struct element; + +struct rate +{ + /* Total value of attribute with eventual overflows accumulated. */ + uint64_t r_total; + + /* Current value of counter */ + uint64_t r_current; + + /* Value of r_current at last read */ + uint64_t r_prev; + + /* Rate per second calculated every `rate_interval' */ + float r_rate; + + /* Time of last calculation */ + timestamp_t r_last_calc; +}; + +enum { + ATTR_TYPE_UNSPEC, + ATTR_TYPE_COUNTER, + ATTR_TYPE_RATE, + ATTR_TYPE_PERCENT, +}; + +struct attr_def { + int ad_id; + char * ad_name; + char * ad_description; + int ad_type; + int ad_flags; + struct unit * ad_unit; + + struct list_head ad_list; +}; + +struct attr_map { + const char * name; + const char * description; + const char * unit; + int attrid, + type, + rxid, + txid, + flags; +}; + +extern int attr_def_add(const char *, const char *, + struct unit *, int, int); +extern struct attr_def * attr_def_lookup(const char *); +extern struct attr_def * attr_def_lookup_id(int); + +extern int attr_map_load(struct attr_map *map, size_t size); + +#define ATTR_FORCE_HISTORY 0x01 /* collect history */ +#define ATTR_IGNORE_OVERFLOWS 0x02 +#define ATTR_TRUE_64BIT 0x04 +#define ATTR_RX_ENABLED 0x08 /* has RX counter */ +#define ATTR_TX_ENABLED 0x10 /* has TX counter */ +#define ATTR_DOING_HISTORY 0x20 /* history collected */ + +struct attr +{ + struct rate a_rx_rate, + a_tx_rate; + + uint8_t a_flags; + struct attr_def * a_def; + timestamp_t a_last_update; + + struct list_head a_history_list; + + struct list_head a_list; + struct list_head a_sort_list; +}; + +extern struct attr * attr_lookup(const struct element *, int); +extern void attr_update(struct element *, int, + uint64_t, uint64_t , int ); +extern void attr_notify_update(struct attr *, + timestamp_t *); +extern void attr_free(struct attr *); + +extern void attr_rate2float(struct attr *, + double *, char **, int *, + double *, char **, int *); + +extern void attr_calc_usage(struct attr *, float *, float *, + uint64_t, uint64_t); + +#define ATTR_HASH_SIZE 32 + +#define UPDATE_FLAG_RX 0x01 +#define UPDATE_FLAG_TX 0x02 +#define UPDATE_FLAG_64BIT 0x04 + +extern struct attr * attr_select_first(void); +extern struct attr * attr_select_last(void); +extern struct attr * attr_select_next(void); +extern struct attr * attr_select_prev(void); +extern struct attr * attr_current(void); + +extern void attr_start_collecting_history(struct attr *); + +#endif diff --git a/include/bmon/bmon.h b/include/bmon/bmon.h new file mode 100644 index 0000000..e349110 --- /dev/null +++ b/include/bmon/bmon.h @@ -0,0 +1,82 @@ +/* + * bmon.h All-include Header + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_BMON_H_ +#define __BMON_BMON_H_ + +#include +#include + +extern int start_time; + +typedef struct timestamp_s +{ + int64_t tv_sec; + int64_t tv_usec; +} timestamp_t; + +typedef struct xdate_s +{ + struct tm d_tm; + unsigned int d_usec; +} xdate_t; + +enum { + EMPTY_LIST = 1, + END_OF_LIST = 2 +}; + +#define BUG() \ + do { \ + fprintf(stderr, "BUG: %s:%d\n", __FILE__, __LINE__); \ + assert(0); \ + exit(EINVAL); \ + } while (0); + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) + +#if defined __GNUC__ +#define __init __attribute__ ((constructor)) +#define __exit __attribute__ ((destructor)) +#else +#define __init +#define __exit +#endif + +#ifdef DEBUG +#define DBG(FMT, ARG...) \ + do { \ + fprintf(stderr, \ + "[DBG] %20s:%-4u %s: " FMT "\n", \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__, ##ARG); \ + } while (0) +#else +#define DBG(FMT, ARG...) \ + do { \ + } while (0) +#endif + +#endif diff --git a/include/bmon/compile-fixes.h b/include/bmon/compile-fixes.h new file mode 100644 index 0000000..c3e5b00 --- /dev/null +++ b/include/bmon/compile-fixes.h @@ -0,0 +1,35 @@ +/* + * compile_fixes.h + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_COMPILE_FIXES_H_ +#define __BMON_COMPILE_FIXES_H_ + +#include + +#ifndef HAVE_SUSECONDS_T +typedef long suseconds_t; +#endif + +#endif diff --git a/include/bmon/conf.h b/include/bmon/conf.h new file mode 100644 index 0000000..f95d48f --- /dev/null +++ b/include/bmon/conf.h @@ -0,0 +1,91 @@ +/* + * conf.h Config Crap + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_CONF_H_ +#define __BMON_CONF_H_ + +#include + +extern cfg_t *cfg; + +extern float cfg_read_interval; +extern float cfg_rate_interval; +extern float cfg_rate_variance; +extern float cfg_history_variance; +extern int cfg_show_all; +extern int cfg_unit_exp; + +extern void conf_init_pre(void); +extern void conf_init_post(void); +extern void configfile_read(void); +extern void set_configfile(const char *); + +extern unsigned int get_lifecycles(void); + +extern void conf_set_float(const char *, double); +extern double conf_get_float(const char *); +extern void conf_set_int(const char *, long); +extern long conf_get_int(const char *); +extern void conf_set_string(const char *, const char *); +extern const char * conf_get_string(const char *); + +typedef struct tv_s +{ + char * tv_type; + char * tv_value; + struct list_head tv_list; +} tv_t; + +typedef struct module_conf_s +{ + char * m_name; + struct list_head m_attrs; + struct list_head m_list; +} module_conf_t; + +extern int parse_module_param(const char *, struct list_head *); + +enum { + LAYOUT_UNSPEC, + LAYOUT_DEFAULT, + LAYOUT_STATUSBAR, + LAYOUT_HEADER, + LAYOUT_LIST, + LAYOUT_SELECTED, + __LAYOUT_MAX +}; + +#define LAYOUT_MAX (__LAYOUT_MAX - 1) + +struct layout +{ + int l_fg, + l_bg, + l_attr; +}; + +extern struct layout cfg_layout[]; + +#endif diff --git a/include/bmon/config.h b/include/bmon/config.h new file mode 100644 index 0000000..e717245 --- /dev/null +++ b/include/bmon/config.h @@ -0,0 +1,158 @@ +/* + * config.h Global Config + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_CONFIG_H_ +#define __BMON_CONFIG_H_ + +#include +#include + +#if STDC_HEADERS != 1 +#error "*** ERROR: ANSI C headers required for compilation ***" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#ifdef HAVE_VFORK_H +#include +#endif + +#if !HAVE_WORKING_VFORK +#define vfork fork +#endif + +#if defined HAVE_STRING_H +#include +#elif defined HAVE_STRINGS_H +#include +#else +#error "*** ERROR: No string header file found ***" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_INITTYPES_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include + +#if defined HAVE_SYS_UTSNAME_H +#include +#endif + +#if defined HAVE_NCURSESW_CURSES_H +# include +#elif defined HAVE_NCURSESW_H +# include +#elif defined HAVE_NCURSES_CURSES_H +# include +#elif defined HAVE_NCURSES_H +# include +#elif defined HAVE_CURSES_H +# include +#else +# error "SysV or X/Open-compatible Curses header file required" +#endif + +#include + +#if defined HAVE_NETINET6_IN6_H +#include +#endif + +#include +#include + +#include + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +#ifndef SCNu64 +#define SCNu64 "llu" +#endif + +#ifndef PRIu64 +#define PRIu64 "llu" +#endif + +#ifndef PRId64 +#define PRId64 "lld" +#endif + +#ifndef PRIX64 +#define PRIX64 "X" +#endif + +#define DEFAULT_GROUP "intf" + +#endif diff --git a/include/bmon/defs.h.in b/include/bmon/defs.h.in new file mode 100644 index 0000000..d176c30 --- /dev/null +++ b/include/bmon/defs.h.in @@ -0,0 +1,246 @@ +/* include/bmon/defs.h.in. Generated from configure.ac by autoheader. */ + +/* enable debugging */ +#undef DEBUG + +/* no overflow workaround */ +#undef DISABLE_OVERFLOW_WORKAROUND + +/* Define to 1 if you have the `atexit' function. */ +#undef HAVE_ATEXIT + +/* have curses */ +#undef HAVE_CURSES + +/* Define to 1 if library supports color (enhanced functions) */ +#undef HAVE_CURSES_COLOR + +/* Define to 1 if library supports X/Open Enhanced functions */ +#undef HAVE_CURSES_ENHANCED + +/* Define to 1 if is present */ +#undef HAVE_CURSES_H + +/* Define to 1 if library supports certain obsolete features */ +#undef HAVE_CURSES_OBSOLETE + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getdate' function. */ +#undef HAVE_GETDATE + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* have ncurses */ +#undef HAVE_NCURSES + +/* Define to 1 if the NcursesW library is present */ +#undef HAVE_NCURSESW + +/* Define to 1 if is present */ +#undef HAVE_NCURSESW_CURSES_H + +/* Define to 1 if is present */ +#undef HAVE_NCURSESW_H + +/* Define to 1 if is present */ +#undef HAVE_NCURSES_CURSES_H + +/* Define to 1 if is present */ +#undef HAVE_NCURSES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_NCURSES_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET6_IN6_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the `pow' function. */ +#undef HAVE_POW + +/* have redrawwin */ +#undef HAVE_REDRAWWIN + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if the system has the type `suseconds_t'. */ +#undef HAVE_SUSECONDS_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSCTL_IOCTL_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* have udc */ +#undef HAVE_USE_DEFAULT_COLORS + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* operating system */ +#undef SYS_BSD + +/* operating system */ +#undef SYS_LINUX + +/* operating system */ +#undef SYS_OTHER + +/* operating system */ +#undef SYS_SUNOS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/include/bmon/element.h b/include/bmon/element.h new file mode 100644 index 0000000..6c68250 --- /dev/null +++ b/include/bmon/element.h @@ -0,0 +1,130 @@ +/* + * bmon/element.h Elements + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_ELEMENT_H_ +#define __BMON_ELEMENT_H_ + +#include +#include +#include + +#define MAX_GRAPHS 32 +#define IFNAME_MAX 32 + +struct policy +{ + char * p_rule; + struct list_head p_list; +}; + +struct element_cfg; + +struct info +{ + char * i_name; + char * i_value; + struct list_head i_list; +}; + +struct element +{ + char * e_name; + char * e_description; + uint32_t e_id; + uint32_t e_flags; + unsigned int e_lifecycles; + unsigned int e_level; /* recursion level */ + + struct element * e_parent; + struct element_group * e_group; + + struct list_head e_list; + struct list_head e_childs; + + unsigned int e_nattrs; + struct list_head e_attrhash[ATTR_HASH_SIZE]; + struct list_head e_attr_sorted; + + unsigned int e_ninfo; + struct list_head e_info_list; + + struct attr_def * e_key_attr[__GT_MAX]; + struct attr_def * e_usage_attr; + + float e_rx_usage, + e_tx_usage; + + struct element_cfg * e_cfg; + + struct attr * e_current_attr; +}; + +#define ELEMENT_CREAT (1 << 0) + +extern struct element * element_lookup(struct element_group *, + const char *, uint32_t, + struct element *, int); + +extern void element_free(struct element *); + +extern void element_reset_update_flag(struct element_group *, + struct element *, + void *); +extern void element_notify_update(struct element *, + timestamp_t *); +extern void element_lifesign(struct element *, int); +extern void element_check_if_dead(struct element_group *, + struct element *, void *); + +extern int element_set_key_attr(struct element *, const char *, const char *); +extern int element_set_usage_attr(struct element *, const char *); + +#define ELEMENT_FLAG_FOLDED (1 << 0) +#define ELEMENT_FLAG_UPDATED (1 << 1) +#define ELEMENT_FLAG_EXCLUDE (1 << 2) +#define ELEMENT_FLAG_CREATED (1 << 3) + +extern void element_foreach_attr(struct element *, + void (*cb)(struct element *, + struct attr *, void *), + void *); + +extern struct element * element_current(void); +extern struct element * element_select_first(void); +extern struct element * element_select_last(void); +extern struct element * element_select_next(void); +extern struct element * element_select_prev(void); + +extern int element_allowed(const char *, struct element_cfg *); +extern void element_parse_policy(const char *); + +extern void element_update_info(struct element *, + const char *, + const char *); + +extern void element_set_txmax(struct element *, uint64_t); +extern void element_set_rxmax(struct element *, uint64_t); + +#endif diff --git a/include/bmon/element_cfg.h b/include/bmon/element_cfg.h new file mode 100644 index 0000000..3131841 --- /dev/null +++ b/include/bmon/element_cfg.h @@ -0,0 +1,53 @@ +/* + * element_cfg.h Element Configuration + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_ELEMENT_CFG_H_ +#define __BMON_ELEMENT_CFG_H_ + +#include +#include + +#define ELEMENT_CFG_SHOW (1 << 0) +#define ELEMENT_CFG_HIDE (1 << 1) + +struct element_cfg +{ + char * ec_name; /* Name of element config */ + char * ec_parent; /* Name of parent */ + char * ec_description; /* Human readable description */ + uint64_t ec_rxmax; /* Maximum RX value expected */ + uint64_t ec_txmax; /* Minimum TX value expected */ + unsigned int ec_flags; /* Flags */ + + struct list_head ec_list; /* Internal, do not modify */ + int ec_refcnt; /* Internal, do not modify */ +}; + +extern struct element_cfg * element_cfg_alloc(const char *); +extern struct element_cfg * element_cfg_create(const char *); +extern void element_cfg_free(struct element_cfg *); +extern struct element_cfg * element_cfg_lookup(const char *); + +#endif diff --git a/include/bmon/graph.h b/include/bmon/graph.h new file mode 100644 index 0000000..bdfac72 --- /dev/null +++ b/include/bmon/graph.h @@ -0,0 +1,72 @@ +/* + * bmon/graph.h Graph creation utility + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_GRAPH_H_ +#define __BMON_GRAPH_H_ + +#include +#include + +struct history; + +struct graph_cfg { + int gc_height, + gc_width, + gc_flags; + + char gc_background, + gc_foreground, + gc_noise, + gc_unknown; + + struct unit * gc_unit; +}; + +struct graph_table { + char * gt_table; + char * gt_y_unit; + float * gt_scale; +}; + +struct graph { + struct graph_cfg g_cfg; + struct graph_table g_rx, + g_tx; + + struct list_head g_list; +}; + +extern void graph_free(struct graph *); +extern struct graph * graph_alloc(struct history *, struct graph_cfg *); +extern void graph_refill(struct graph *, struct history *); + +extern size_t graph_row_size(struct graph_cfg *); + +extern void new_graph(void); +extern void del_graph(void); +extern int next_graph(void); +extern int prev_graph(void); + +#endif diff --git a/include/bmon/group.h b/include/bmon/group.h new file mode 100644 index 0000000..b952aac --- /dev/null +++ b/include/bmon/group.h @@ -0,0 +1,100 @@ +/* + * bmon/group.h Group Management + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_GROUP_H_ +#define __BMON_GROUP_H_ + +#include + +struct element; + +enum { + GT_MAJOR, + GT_MINOR, + __GT_MAX, +}; + +#define GT_MAX (__GT_MAX - 1) + +#define GROUP_COL_MAX (__GT_MAX * 2) + +struct group_hdr +{ + char * gh_name; + char * gh_title; + char * gh_column[GROUP_COL_MAX]; + struct list_head gh_list; +}; + +extern struct group_hdr * group_lookup_hdr(const char *); +extern int group_new_hdr(const char *, const char *, + const char *, const char *, + const char *, const char *); +extern int group_new_derived_hdr(const char *, + const char *, + const char *); + +struct element_group +{ + char * g_name; + struct group_hdr * g_hdr; + + struct list_head g_elements; + unsigned int g_nelements; + + /* Currently selected element in this group */ + struct element * g_current; + + struct list_head g_list; +}; + +#define GROUP_CREATE 1 + +extern struct element_group * group_lookup(const char *, int); +extern void reset_update_flags(void); +extern void free_unused_elements(void); +extern void calc_rates(void); + +extern void group_foreach( + void (*cb)(struct element_group *, + void *), + void *); +extern void group_foreach_recursive( + void (*cb)(struct element_group *, + struct element *, void *), + void *); + +extern void group_foreach_element(struct element_group *, + void (*cb)(struct element_group *, + struct element *, void *), + void *); + +extern struct element_group * group_current(void); +extern struct element_group * group_select_first(void); +extern struct element_group * group_select_last(void); +extern struct element_group * group_select_next(void); +extern struct element_group * group_select_prev(void); + +#endif diff --git a/include/bmon/history.h b/include/bmon/history.h new file mode 100644 index 0000000..6b7faa4 --- /dev/null +++ b/include/bmon/history.h @@ -0,0 +1,93 @@ +/* + * bmon/history.h History Management + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_HISTORY_H_ +#define __BMON_HISTORY_H_ + +#include +#include + +#define HISTORY_UNKNOWN ((uint64_t) -1) +#define HBEAT_TRIGGER 60.0f + +enum { + HISTORY_TYPE_8 = 1, + HISTORY_TYPE_16 = 2, + HISTORY_TYPE_32 = 4, + HISTORY_TYPE_64 = 8, +}; + +struct history_def +{ + char * hd_name; + int hd_size, + hd_type; + float hd_interval; + + struct list_head hd_list; +}; + +struct history_store +{ + /* TODO? store error ratio? */ + void * hs_data; + uint64_t hs_prev_total; +}; + +struct history +{ + /* index to current entry in data array */ + int h_index; + struct history_def * h_definition; + /* time of last history update */ + timestamp_t h_last_update; + struct list_head h_list; + + float h_min_interval, + h_max_interval; + + struct history_store h_rx, + h_tx; + +}; + +extern struct history_def * history_def_lookup(const char *); +extern struct history_def * history_def_alloc(const char *); + +extern uint64_t history_data(struct history *, + struct history_store *, int); +extern void history_update(struct attr *, + struct history *, timestamp_t *); +extern struct history * history_alloc(struct history_def *); +extern void history_free(struct history *); +extern void history_attach(struct attr *); + +extern struct history_def * history_select_first(void); +extern struct history_def * history_select_last(void); +extern struct history_def * history_select_next(void); +extern struct history_def * history_select_prev(void); +extern struct history_def * history_current(void); + +#endif diff --git a/include/bmon/info.h b/include/bmon/info.h new file mode 100644 index 0000000..b12b97d --- /dev/null +++ b/include/bmon/info.h @@ -0,0 +1,42 @@ +/* + * bmon/info.h Info Attributes + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_INFO_H_ +#define __BMON_INFO_H_ + +#include +#include + +struct element; + +struct info +{ + char * i_name; + char * i_value; + + struct list_head i_list; +}; + +#endif diff --git a/include/bmon/input.h b/include/bmon/input.h new file mode 100644 index 0000000..dfd24d0 --- /dev/null +++ b/include/bmon/input.h @@ -0,0 +1,52 @@ +/* + * input.h Input API + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_INPUT_H_ +#define __BMON_INPUT_H_ + +#include +#include +#include + +extern int input_set(const char *); +extern void input_register(struct bmon_module *); +extern void input_read(void); + +struct reader_timing +{ + timestamp_t rt_last_read; /* timestamp taken before read */ + timestamp_t rt_next_read; /* estimated next read */ + + struct { + float v_error; + float v_max; + float v_min; + float v_total; + } rt_variance; +}; + +extern struct reader_timing rtiming; + +#endif diff --git a/include/bmon/list.h b/include/bmon/list.h new file mode 100644 index 0000000..c7569d7 --- /dev/null +++ b/include/bmon/list.h @@ -0,0 +1,93 @@ +/* + * bmon/list.h Kernel List Implementation + * + * Copied and adapted from the kernel sources + * + */ + +#ifndef BMON_LIST_H_ +#define BMON_LIST_H_ + +struct list_head +{ + struct list_head * next; + struct list_head * prev; +}; + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *obj, struct list_head *prev, + struct list_head *next) +{ + prev->next = obj; + obj->prev = prev; + next->prev = obj; + obj->next = next; +} + +static inline void list_add_tail(struct list_head *obj, struct list_head *head) +{ + __list_add(obj, head->prev, head); +} + +static inline void list_add_head(struct list_head *obj, struct list_head *head) +{ + __list_add(obj, head, head->next); +} + +static inline void list_del(struct list_head *obj) +{ + obj->next->prev = obj->prev; + obj->prev->next = obj->next; +} + +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));}) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_at_tail(pos, head, member) \ + ((pos)->member.next == (head)) + +#define list_at_head(pos, head, member) \ + ((pos)->member.prev == (head)) + +#define LIST_SELF(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = { &(name), &(name) } + +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &(pos)->member != (head); \ + (pos) = list_entry((pos)->member.next, typeof(*(pos)), member)) + +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &(pos)->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#define init_list_head(head) \ + do { (head)->next = (head); (head)->prev = (head); } while (0) + +#endif diff --git a/include/bmon/module.h b/include/bmon/module.h new file mode 100644 index 0000000..bd1405a --- /dev/null +++ b/include/bmon/module.h @@ -0,0 +1,75 @@ +/* + * module.h Module API + * + * Copyright (c) 2001-2013 Thomas Graf + * Copyright 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. + */ + +#ifndef __BMON_MODULE_H_ +#define __BMON_MODULE_H_ + +#include +#include + +#define BMON_MODULE_ENABLED 1 +#define BMON_MODULE_DEFAULT 2 + +struct bmon_module +{ + char * m_name; + + int (*m_init)(void); + int (*m_probe)(void); + void (*m_shutdown)(void); + + void (*m_parse_opt)(const char *, const char *); + + void (*m_pre)(void); + void (*m_do)(void); + void (*m_post)(void); + + int m_flags; + struct list_head m_list; +}; + +struct bmon_subsys +{ + char * s_name; + int s_nmod; + struct list_head s_mod_list; + + void (*s_activate_default)(void); + + struct list_head s_list; +}; + +extern void module_foreach_run_enabled_pre(struct bmon_subsys *); +extern void module_foreach_run_enabled(struct bmon_subsys *); +extern void module_foreach_run_enabled_post(struct bmon_subsys *); + +extern void module_register(struct bmon_subsys *, struct bmon_module *); +extern int module_set(struct bmon_subsys *, const char *); + +extern void module_init(void); +extern void module_shutdown(void); +extern void module_register_subsys(struct bmon_subsys *); + +#endif diff --git a/include/bmon/output.h b/include/bmon/output.h new file mode 100644 index 0000000..f1fa6c5 --- /dev/null +++ b/include/bmon/output.h @@ -0,0 +1,39 @@ +/* + * output.h Output API + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_OUTPUT_H_ +#define __BMON_OUTPUT_H_ + +#include +#include +#include + +extern void output_register(struct bmon_module *); +extern int output_set(const char *); +extern void output_pre(void); +extern void output_draw(void); +extern void output_post(void); + +#endif diff --git a/include/bmon/unit.h b/include/bmon/unit.h new file mode 100644 index 0000000..e0a83d3 --- /dev/null +++ b/include/bmon/unit.h @@ -0,0 +1,68 @@ +/* + * bmon/unit.h Units + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_UNIT_H_ +#define __BMON_UNIT_H_ + +#include + +#define DYNAMIC_EXP (-1) + +enum { + UNIT_DEFAULT, + UNIT_SI, + __UNIT_MAX, +}; + +#define UNIT_MAX (__UNIT_MAX - 1) + +#define UNIT_BYTE "byte" +#define UNIT_NUMBER "number" + +struct fraction { + uint64_t f_divisor; + char * f_name; + + struct list_head f_list; +}; + +struct unit { + char * u_name; + struct list_head u_div[__UNIT_MAX]; + + struct list_head u_list; +}; + +extern struct unit * unit_lookup(const char *); +extern struct unit * unit_add(const char *name); +extern void unit_add_div(struct unit *, int, const char *, float); +extern uint64_t unit_divisor(uint64_t, struct unit *, char **, int *); +extern double unit_value2str(uint64_t, struct unit *, char **, int *); +extern void fraction_free(struct fraction *); + +extern char * unit_bytes2str(uint64_t, char *, size_t); +extern char * unit_bit2str(uint64_t, char *, size_t); + +#endif diff --git a/include/bmon/utils.h b/include/bmon/utils.h new file mode 100644 index 0000000..725a1cb --- /dev/null +++ b/include/bmon/utils.h @@ -0,0 +1,102 @@ +/* + * utils.h General purpose utilities + * + * Copyright (c) 2001-2013 Thomas Graf + * 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. + */ + +#ifndef __BMON_UTILS_H_ +#define __BMON_UTILS_H_ + +#include + +extern void * xcalloc(size_t, size_t); +extern void * xrealloc(void *, size_t); +extern void xfree(void *); +extern void quit (const char *, ...); + +extern float timestamp_to_float(timestamp_t *); +extern int64_t timestamp_to_int(timestamp_t *); + +extern void float_to_timestamp(timestamp_t *, float); +extern void int_to_timestamp(timestamp_t *, int); + +extern void timestamp_add(timestamp_t *, timestamp_t *, timestamp_t *); +extern void timestamp_sub(timestamp_t *, timestamp_t *, timestamp_t *); + +extern int timestamp_le(timestamp_t *, timestamp_t *); +extern int timestamp_is_negative(timestamp_t *ts); + +extern void update_timestamp(timestamp_t *); +extern void copy_timestamp(timestamp_t *, timestamp_t *); + +extern float timestamp_diff(timestamp_t *, timestamp_t *); + +#if 0 + + + +#if __BYTE_ORDER == __BIG_ENDIAN +#define xntohll(N) (N) +#define xhtonll(N) (N) +#else +#define xntohll(N) ((((uint64_t) ntohl(N)) << 32) + ntohl((N) >> 32)) +#define xhtonll(N) ((((uint64_t) htonl(N)) << 32) + htonl((N) >> 32)) +#endif + +enum { + U_NUMBER, + U_BYTES, + U_BITS +}; + +extern float read_delta; + + +const char * xinet_ntop(struct sockaddr *, char *, socklen_t); + + +extern float time_diff(timestamp_t *, timestamp_t *); +extern float diff_now(timestamp_t *); + +extern uint64_t parse_size(const char *); + +extern int parse_date(const char *str, xdate_t *dst); + +#ifndef HAVE_STRDUP +extern char *strdup(const char *); +#endif + +extern char * timestamp2str(timestamp_t *ts, char *buf, size_t len); + +static inline char *xstrncat(char *dest, const char *src, size_t n) +{ + return strncat(dest, src, n - strlen(dest) - 1); +} + +static inline void xdate_to_ts(timestamp_t *dst, xdate_t *src) +{ + dst->tv_sec = mktime(&src->d_tm); + dst->tv_usec = src->d_usec; +}; +#endif + +#endif diff --git a/m4/ax_with_curses.m4 b/m4/ax_with_curses.m4 new file mode 100644 index 0000000..33a37ac --- /dev/null +++ b/m4/ax_with_curses.m4 @@ -0,0 +1,518 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_with_curses.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_WITH_CURSES +# +# DESCRIPTION +# +# This macro checks whether a SysV or X/Open-compatible Curses library is +# present, along with the associated header file. The NcursesW +# (wide-character) library is searched for first, followed by Ncurses, +# then the system-default plain Curses. The first library found is the +# one returned. +# +# The following options are understood: --with-ncursesw, --with-ncurses, +# --without-ncursesw, --without-ncurses. The "--with" options force the +# macro to use that particular library, terminating with an error if not +# found. The "--without" options simply skip the check for that library. +# The effect on the search pattern is: +# +# (no options) - NcursesW, Ncurses, Curses +# --with-ncurses --with-ncursesw - NcursesW only [*] +# --without-ncurses --with-ncursesw - NcursesW only [*] +# --with-ncursesw - NcursesW only [*] +# --with-ncurses --without-ncursesw - Ncurses only [*] +# --with-ncurses - NcursesW, Ncurses [**] +# --without-ncurses --without-ncursesw - Curses only +# --without-ncursesw - Ncurses, Curses +# --without-ncurses - NcursesW, Curses +# +# [*] If the library is not found, abort the configure script. +# +# [**] If the second library (Ncurses) is not found, abort configure. +# +# The following preprocessor symbols may be defined by this macro if the +# appropriate conditions are met: +# +# HAVE_CURSES - if any SysV or X/Open Curses library found +# HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions +# HAVE_CURSES_COLOR - if library supports color (enhanced functions) +# HAVE_CURSES_OBSOLETE - if library supports certain obsolete features +# HAVE_NCURSESW - if NcursesW (wide char) library is to be used +# HAVE_NCURSES - if the Ncurses library is to be used +# +# HAVE_CURSES_H - if is present and should be used +# HAVE_NCURSESW_H - if should be used +# HAVE_NCURSES_H - if should be used +# HAVE_NCURSESW_CURSES_H - if should be used +# HAVE_NCURSES_CURSES_H - if should be used +# +# (These preprocessor symbols are discussed later in this document.) +# +# The following output variable is defined by this macro; it is precious +# and may be overridden on the ./configure command line: +# +# CURSES_LIB - library to add to xxx_LDADD +# +# The library listed in CURSES_LIB is NOT added to LIBS by default. You +# need to add CURSES_LIB to the appropriate xxx_LDADD line in your +# Makefile.am. For example: +# +# prog_LDADD = @CURSES_LIB@ +# +# If CURSES_LIB is set on the configure command line (such as by running +# "./configure CURSES_LIB=-lmycurses"), then the only header searched for +# is . The user may use the CPPFLAGS precious variable to +# override the standard #include search path. If the user needs to +# specify an alternative path for a library (such as for a non-standard +# NcurseW), the user should use the LDFLAGS variable. +# +# The following shell variables may be defined by this macro: +# +# ax_cv_curses - set to "yes" if any Curses library found +# ax_cv_curses_enhanced - set to "yes" if Enhanced functions present +# ax_cv_curses_color - set to "yes" if color functions present +# ax_cv_curses_obsolete - set to "yes" if obsolete features present +# +# ax_cv_ncursesw - set to "yes" if NcursesW library found +# ax_cv_ncurses - set to "yes" if Ncurses library found +# ax_cv_plaincurses - set to "yes" if plain Curses library found +# ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no" +# +# These variables can be used in your configure.ac to determine the level +# of support you need from the Curses library. For example, if you must +# have either Ncurses or NcursesW, you could include: +# +# AX_WITH_CURSES +# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then +# AX_MSG_ERROR([requires either NcursesW or Ncurses library]) +# fi +# +# If any Curses library will do (but one must be present and must support +# color), you could use: +# +# AX_WITH_CURSES +# if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then +# AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) +# fi +# +# Certain preprocessor symbols and shell variables defined by this macro +# can be used to determine various features of the Curses library. In +# particular, HAVE_CURSES and ax_cv_curses are defined if the Curses +# library found conforms to the traditional SysV and/or X/Open Base Curses +# definition. Any working Curses library conforms to this level. +# +# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the +# library supports the X/Open Enhanced Curses definition. In particular, +# the wide-character types attr_t, cchar_t and wint_t, the functions +# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES +# are checked. The Ncurses library does NOT conform to this definition, +# although NcursesW does. +# +# HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library +# supports color functions and macros such as COLOR_PAIR, A_COLOR, +# COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the +# X/Open Base Curses definition, but are part of the Enhanced set of +# functions. The Ncurses library DOES support these functions, as does +# NcursesW. +# +# HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the +# library supports certain features present in SysV and BSD Curses but not +# defined in the X/Open definition. In particular, the functions +# getattrs(), getcurx() and getmaxx() are checked. +# +# To use the HAVE_xxx_H preprocessor symbols, insert the following into +# your system.h (or equivalent) header file: +# +# #if defined HAVE_NCURSESW_CURSES_H +# # include +# #elif defined HAVE_NCURSESW_H +# # include +# #elif defined HAVE_NCURSES_CURSES_H +# # include +# #elif defined HAVE_NCURSES_H +# # include +# #elif defined HAVE_CURSES_H +# # include +# #else +# # error "SysV or X/Open-compatible Curses header file required" +# #endif +# +# For previous users of this macro: you should not need to change anything +# in your configure.ac or Makefile.am, as the previous (serial 10) +# semantics are still valid. However, you should update your system.h (or +# equivalent) header file to the fragment shown above. You are encouraged +# also to make use of the extended functionality provided by this version +# of AX_WITH_CURSES, as well as in the additional macros +# AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM. +# +# LICENSE +# +# Copyright (c) 2009 Mark Pulford +# Copyright (c) 2009 Damian Pietras +# Copyright (c) 2012 Reuben Thomas +# Copyright (c) 2011 John Zaitseff +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 13 + +AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES]) +AC_DEFUN([AX_WITH_CURSES], [ + AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses]) + AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses], + [force the use of Ncurses or NcursesW])], + [], [with_ncurses=check]) + AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw], + [do not use NcursesW (wide character support)])], + [], [with_ncursesw=check]) + + ax_saved_LIBS=$LIBS + AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes], + [ax_with_plaincurses=no], [ax_with_plaincurses=check]) + + ax_cv_curses_which=no + + # Test for NcursesW + + AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [ + LIBS="$ax_saved_LIBS -lncursesw" + + AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_ncursesw=yes], [ax_cv_ncursesw=no]) + ]) + AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [ + AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library]) + ]) + + AS_IF([test "x$ax_cv_ncursesw" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncursesw + CURSES_LIB="-lncursesw" + AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_curses_h=yes], + [ax_cv_header_ncursesw_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_h=yes], + [ax_cv_header_ncursesw_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncurses_h_with_ncursesw=yes], + [ax_cv_header_ncurses_h_with_ncursesw=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [ + AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h]) + ]) + ]) + ]) + + # Test for Ncurses + + AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [ + LIBS="$ax_saved_LIBS -lncurses" + + AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_ncurses=yes], [ax_cv_ncurses=no]) + ]) + AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [ + AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library]) + ]) + + AS_IF([test "x$ax_cv_ncurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncurses + CURSES_LIB="-lncurses" + AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_curses_h=yes], + [ax_cv_header_ncurses_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_h=yes], + [ax_cv_header_ncurses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [ + AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h]) + ]) + ]) + ]) + + # Test for plain Curses (or if CURSES_LIB was set by user) + + AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [ + AS_IF([test "x$CURSES_LIB" != x], [ + LIBS="$ax_saved_LIBS $CURSES_LIB" + ], [ + LIBS="$ax_saved_LIBS -lcurses" + ]) + + AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no]) + ]) + + AS_IF([test "x$ax_cv_plaincurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=plaincurses + AS_IF([test "x$CURSES_LIB" = x], [ + CURSES_LIB="-lcurses" + ]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + # Check for base conformance (and header file) + + AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + initscr(); + ]])], + [ax_cv_header_curses_h=yes], + [ax_cv_header_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_curses_h" = xyes], [ + AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present]) + + # Check for X/Open Enhanced conformance + + AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + @%:@ifndef _XOPEN_CURSES + @%:@error "this Curses library is not enhanced" + "this Curses library is not enhanced" + @%:@endif + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_plaincurses_enhanced=yes], + [ax_cv_plaincurses_enhanced=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for color functions + + AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_plaincurses_color=yes], + [ax_cv_plaincurses_color=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [ + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for obsolete functions + + AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + ]])], + [ax_cv_plaincurses_obsolete=yes], + [ax_cv_plaincurses_obsolete=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [ + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + ]) + ]) + + AS_IF([test "x$ax_cv_header_curses_h" = xno], [ + AC_MSG_WARN([could not find a working curses.h]) + ]) + ]) + ]) + + AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no]) + AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no]) + AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no]) + AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no]) + + LIBS=$ax_saved_LIBS +])dnl diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..8861ecc --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +dist_man8_MANS = bmon.8 diff --git a/man/bmon.8 b/man/bmon.8 new file mode 100644 index 0000000..38bdd79 --- /dev/null +++ b/man/bmon.8 @@ -0,0 +1,235 @@ +.TH "bmon" "8" "" "Bandwidth Monitor" "bmon" +.SH "NAME" +bmon \- bandwidth monitor and rate estimator +.SH "SYNOPSIS" +.B bmon +[\fB\-\-show\-all\fR] +[\fB\-\-use\-si\fR] +[\fB\-\-input\fR=\fIMODULE\fR] +[\fB\-\-output\fR=\fIMODULE\fR] +[OPTIONS...] + +.SH "DESCRIPTION" +bmon is a monitoring and debugging tool to capture networking related +statistics and prepare them visually in a human friendly way. It +features various output methods including an interactive curses user +interface and a programmable text output for scripting. + +.SH "OPTIONS" +.PP +\fB -h\fR, \fB\-\-help\fR +.RS 4 +Prints a short help text and exits\&. +.RE +.PP +\fB -V\fR, \fB\-\-version\fR +.RS 4 +Prints the versioning identifier and exits\&. +.RE +.PP +\fB -i\fR, \fB\-\-input=\fRMODULE[:OPTIONS][,MODULE...] +.RS 4 +Set list of input modules to load and use. Multiple modules can be used +in parallel. bmon automatically loads a useful and working input module +by default. See INPUT MODULES for more details. +.RE +.PP +\fB -o\fR, \fB\-\-ouptut\fRMODULE[:OPTIONS][,MODULE...] +.RS 4 +Set list of output modules to load and use. Multiple modules can be used +in parallel. By default, bmon will use the curses output mode, if that is +not available due to an incompatible console it will fall back to a simple +text mode. See OUTPUT MODULES for more details. +.RE +.PP +\fB -U\fR, \fB\-\-use\-si\fR +.RS 4 +Use SI unit system instead of 1KB = 1'024 bytes. +.RE +.PP +\fB -f\fR, \fB\-\-configfile=\fRFILE +.RS 4 +Set alternative path to configuration file. +.RE +.PP +\fB -p\fR, \fB\-\-policy=\fRPOLICY +.RS 4 +Set policy defining which network interfaces to display. See +INTERFACE SELECTION for more details. +.RE +.PP +\fB -a\fR, \fB\-\-show\-all=\fR +.RS 4 +Display all interfaces, even interface that are administratively down. +.RE +.PP +\fB -r\fR, \fB\-\-read\-interval=\fRFLOAT +.RS 4 +Set interval in seconds in which input modules read statistics from their +source. The default is 1.0 seconds. +.RE +.PP +\fB -R\fR, \fB\-\-rate\-interval=\fRFLOAT +.RS 4 +Set interval in seconds in which the rate per counter is calculated. +The default is 1.0 seconds. +.RE +.PP +\fB -L\fR, \fB\-\-lifetime=\fRFLOAT +.RS 4 +Set lifetime of an element in seconds before it is no longer displayed +without receiving any statistical updates. The default is 30 seconds. +.RE + +.SH "INPUT MODULES" +.PP +Input modules provide statistical data about elements. Each element consists +of attributes which represents a counter, a rate, or a percentage. Elements +may carry additional child elements to represent a hierarchy. Each element is +assigned to a group defined by the input module. Input modules are polled in +the frequence of the configured read interval. +.PP +The following input modules are available: +.TP +\fBnetlink\fR +Uses the Netlink protocol to collect interface and traffic control statistics +from the kernel. This is the default input module. + +.TP +\fBproc\fR +Reads interface statistics from the /proc/net/dev file. This is considered a +legacy interface and provided for backwards compatibily reasons. This is a +fallback module if the Netlink interface is not available. + +.TP +\fBdummy\fR +Programmable input module for debugging and testing purposes. + +.TP +\fBnull\fR +No data collected. + +.PP +To receive additional information about a module, run the module with the +"help" option set like this: + +.PP +.RS 4 +bmon \-i netlink:help +.RE + +See MODULE CONFIGURATION for more details. + +.SH "OUTPUT MODULES" +.PP +Output modules display or export the statistical data collected by input +modules. Multiple output modules can be run at the same time. bmon will +not prevent possible conflicts such as multiple output modules writing to +the console. +.PP +The following output modules exist: + +.TP +\fBcurses\fR +Interactive curses based text user interface providing real time rate +estimations and a graphical representatio nof each attribute. Press '?' +to display the quick reference guide. This is the default output mode. + +.TP +\fBascii\fR +Simple programmable text output intended for human consumption. Capable +of printing list of interfaces, detailed counters and graphs to the +console. This is the default fallback output mode if curses is not +available. + +.TP +\fBformat\fR +Fully scriptable output mode inteded for consumption by other programs. +See the module help text for additional information. + +.TP +\fBnull\fR +Disable output. + +.PP +To receive additional information about a module, run the module with the +"help" option set like this: + +.PP +.RS 4 +bmon \-o curses:help +.RE + +See MODULE CONFIGURATION for more details. + +.SH "MODULE CONFIGURATION" +.PP +The syntax to configure modules is as follows: +.PP +.RS 4 +ARGUMENT ::= mod1[:OPTS][,mod2[:OPTS]...] +.br +OPTS ::= OPTION[;OPTION...] +.br +OPTION ::= option[=value] +.RE + +.PP +Run the module with option "help" to receive the list of options for each +module: + +.PP +.RS 4 +bmon -i module:help +.RE + +.SH "INTERFACE SELECTION" +.PP +The following syntax is used to define the interface selection policy: +.PP +.RS 4 +SELECTION ::= NAME[,NAME[,...]] +.br +NAME ::= [!]interface +.RE + +.PP +The interface name may contain the character '*' which will act as a wildcard +and represents any number of any character type, e.g. eth*, h*0, ... + +.TP +Examples: +.RS +.NF +lo,eth0,eth1 +.FI +.RE +.RS +.NF +eth*,!eth0 +.FI +.RE + +.SH "EXAMPLES" +.PP +To run bmon in curses mode monitoring the interfaces eth0 +and eth1: +.PP +.RS 4 +\fBbmon \-p eth0,eth1 \-o curses\fP +.RE + +.SH "FILES" +/etc/bmon.conf +.br +$HOME/.bmonrc + +.SH "SEE ALSO" +.PP +\fBip\fR(8), +\fBnetstat\fR(8), +\fBifconfig\fR(8), +\fBnetlink\fR(7), + +.SH "AUTHOR" +Thomas Graf diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..95d74e9 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +*.o +bmon diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..dcc7bfd --- /dev/null +++ b/src/Makefile.am @@ -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 diff --git a/src/attr.c b/src/attr.c new file mode 100644 index 0000000..bd001eb --- /dev/null +++ b/src/attr.c @@ -0,0 +1,635 @@ +/* + * attr.c Attributes + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/bmon.c b/src/bmon.c new file mode 100644 index 0000000..22492d0 --- /dev/null +++ b/src/bmon.c @@ -0,0 +1,397 @@ +/* + * src/bmon.c Bandwidth Monitor + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 \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); +} diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..c1a1676 --- /dev/null +++ b/src/conf.c @@ -0,0 +1,596 @@ +/* + * conf.c Config Crap + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/src/element.c b/src/element.c new file mode 100644 index 0000000..d138e36 --- /dev/null +++ b/src/element.c @@ -0,0 +1,537 @@ +/* + * src/element.c Elements + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/src/element_cfg.c b/src/element_cfg.c new file mode 100644 index 0000000..7e7ef5b --- /dev/null +++ b/src/element_cfg.c @@ -0,0 +1,96 @@ +/* + * element_cfg.c Element Configuration + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include + +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); +} diff --git a/src/graph.c b/src/graph.c new file mode 100644 index 0000000..3580238 --- /dev/null +++ b/src/graph.c @@ -0,0 +1,235 @@ +/* + * graph.c Graph creation utility + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..e745bbe --- /dev/null +++ b/src/group.c @@ -0,0 +1,278 @@ +/* + * group.c Group Management + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include + +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); +} diff --git a/src/history.c b/src/history.c new file mode 100644 index 0000000..39171a0 --- /dev/null +++ b/src/history.c @@ -0,0 +1,318 @@ +/* + * history.c History Management + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include + +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); +} diff --git a/src/in_dummy.c b/src/in_dummy.c new file mode 100644 index 0000000..00eca82 --- /dev/null +++ b/src/in_dummy.c @@ -0,0 +1,243 @@ +/* + * in_dummy.c Dummy Input Method + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include + +#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 \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); +} diff --git a/src/in_netlink.c b/src/in_netlink.c new file mode 100644 index 0000000..ddd687c --- /dev/null +++ b/src/in_netlink.c @@ -0,0 +1,868 @@ +/* + * in_netlink.c Netlink input + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include + +static int c_notc = 0; +static struct element_group *grp; +static struct bmon_module netlink_ops; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 \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); +} diff --git a/src/in_null.c b/src/in_null.c new file mode 100644 index 0000000..fb47a4a --- /dev/null +++ b/src/in_null.c @@ -0,0 +1,62 @@ +/* + * in_null.c Null Input Method + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include + +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 \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); +} diff --git a/src/in_proc.c b/src/in_proc.c new file mode 100644 index 0000000..b360de2 --- /dev/null +++ b/src/in_proc.c @@ -0,0 +1,234 @@ +/* + * in_proc.c /proc/net/dev Input (Linux) + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include + +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 \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); +} diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..f4afac7 --- /dev/null +++ b/src/input.c @@ -0,0 +1,83 @@ +/* + * input.c Input API + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include + +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); +} diff --git a/src/module.c b/src/module.c new file mode 100644 index 0000000..e805ce0 --- /dev/null +++ b/src/module.c @@ -0,0 +1,195 @@ +/* + * module.c Module API + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include + +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); +} diff --git a/src/out_ascii.c b/src/out_ascii.c new file mode 100644 index 0000000..40a70e2 --- /dev/null +++ b/src/out_ascii.c @@ -0,0 +1,299 @@ +/* + * out_ascii.c ASCII Output + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 \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); +} diff --git a/src/out_curses.c b/src/out_curses.c new file mode 100644 index 0000000..602998c --- /dev/null +++ b/src/out_curses.c @@ -0,0 +1,1277 @@ +/* + * out_curses.c Curses Output + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + GRAPH_DISPLAY_SIDE_BY_SIDE = 1, + GRAPH_DISPLAY_STANDARD = 2, +}; + +enum { + KEY_TOGGLE_LIST = 'l', + KEY_TOGGLE_GRAPH = 'g', + KEY_TOGGLE_DETAILS = 'd', + KEY_TOGGLE_INFO = 'i', + KEY_COLLECT_HISTORY = 'h', +}; + +#define DETAILS_COLS 40 + +#define LIST_COL_1 31 +#define LIST_COL_2 55 + +/* Set to element_current() before drawing */ +static struct element *current_element; + +static struct attr *current_attr; + +/* Length of list to draw, updated in draw_content() */ +static int list_length; + +static int list_req; + +/* Number of graphs to draw (may be < c_ngraph) */ +static int ngraph; + +/* + * Offset in number of lines within the the element list of the currently + * selected element. Updated while summing up required lines. + */ +static unsigned int selection_offset; + +/* + * Offset in number of lines of the first element to be drawn. Updated + * in draw_content() + */ +static int offset; + +/* + * Offset to the first graph to draw in number of attributes with graphs. + */ +static unsigned int graph_offset; + +static int graph_display = GRAPH_DISPLAY_STANDARD; + +/* + * Number of detail columns + */ +static int detail_cols; +static int info_cols; + +static int initialized; +static int print_help; +static int quit_mode; +static int help_page; + +/* Current row */ +static int row; + +/* Number of rows */ +static int rows; + +/* Number of columns */ +static int cols; + +static int c_show_graph = 1; +static int c_ngraph = 1; +static int c_use_colors = 1; +static int c_show_details = 0; +static int c_show_list = 1; +static int c_show_info = 0; +static int c_list_min = 6; + +static struct graph_cfg c_graph_cfg = { + .gc_width = 60, + .gc_height = 6, + .gc_foreground = '|', + .gc_background = '.', + .gc_noise = ':', + .gc_unknown = '?', +}; + +#define NEXT_ROW() \ + do { \ + row++; \ + if (row >= rows - 1) \ + return; \ + move(row, 0); \ + } while(0) + +static void apply_layout(int layout) +{ + if (c_use_colors) + attrset(COLOR_PAIR(layout) | cfg_layout[layout].l_attr); + else + attrset(cfg_layout[layout].l_attr); +} + +char *float2str(double value, int width, int prec, char *buf, size_t len) +{ + snprintf(buf, len, "%'*.*f", width, value == 0.0f ? 0 : prec, value); + + return buf; +} + +static void put_line(const char *fmt, ...) +{ + va_list args; + char buf[2048]; + int x, y; + + memset(buf, 0, sizeof(buf)); + getyx(stdscr, y, x); + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (strlen(buf) > cols-x) + buf[cols - x] = '\0'; + else + memset(&buf[strlen(buf)], ' ', cols - strlen(buf)-x); + + addstr(buf); +} + +static void center_text(const char *fmt, ...) +{ + va_list args; + char *str; + unsigned int col; + + va_start(args, fmt); + vasprintf(&str, fmt, args); + va_end(args); + + col = (cols / 2) - (strlen(str) / 2); + if (col > cols - 1) + col = cols - 1; + + move(row, col); + addstr(str); + move(row, 0); + + free(str); +} + +static int curses_init(void) +{ + if (!initscr()) { + fprintf(stderr, "Unable to initialize curses screen\n"); + return -EOPNOTSUPP; + } + + initialized = 1; + + if (!has_colors()) + c_use_colors = 0; + + if (c_use_colors) { + int i; + + start_color(); + +#if defined HAVE_USE_DEFAULT_COLORS + use_default_colors(); +#endif + for (i = 1; i < LAYOUT_MAX+1; i++) + init_pair(i, cfg_layout[i].l_fg, cfg_layout[i].l_bg); + } + + keypad(stdscr, TRUE); + nonl(); + cbreak(); + noecho(); + nodelay(stdscr, TRUE); /* getch etc. must be non-blocking */ + clear(); + curs_set(0); + + return 0; +} + +static void curses_shutdown(void) +{ + if (initialized) + endwin(); +} + +struct detail_arg +{ + int nattr; +}; + +static void draw_attr_detail(struct element *e, struct attr *a, void *arg) +{ + char *rx_u, *tx_u, buf1[32], buf2[32]; + int rxprec, txprec, ncol; + struct detail_arg *da = arg; + + 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); + + if (da->nattr >= detail_cols) { + NEXT_ROW(); + da->nattr = 0; + } + + ncol = (da->nattr * DETAILS_COLS) - 1; + move(row, ncol); + if (ncol > 0) + addch(ACS_VLINE); + + put_line(" %-14.14s %8s%-3s %8s%-3s\n", + a->a_def->ad_description, + (a->a_flags & ATTR_RX_ENABLED) ? + float2str(rx, 8, rxprec, buf1, sizeof(buf1)) : "-", rx_u, + (a->a_flags & ATTR_TX_ENABLED) ? + float2str(tx, 8, txprec, buf2, sizeof(buf2)) : "-", tx_u); + + da->nattr++; +} + +static void draw_details(void) +{ + int i; + struct detail_arg arg = { + .nattr = 0, + }; + + if (!current_element->e_nattrs) + return; + + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS) - 1, ACS_TTEE); + + NEXT_ROW(); + put_line(""); + for (i = 0; i < detail_cols; i++) { + if (i > 0) + mvaddch(row, (i * DETAILS_COLS) - 1, ACS_VLINE); + move(row, (i * DETAILS_COLS) + 22); + put_line("RX TX"); + } + + NEXT_ROW(); + element_foreach_attr(current_element, draw_attr_detail, &arg); + + /* + * If the last row was incomplete, not all vlines have been drawn. + * draw them here + */ + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE); +} + +static void print_message(const char *text) +{ + int i, y = (rows/2) - 2; + int len = strlen(text); + int x = (cols/2) - (len / 2); + + attrset(A_STANDOUT); + mvaddch(y - 2, x - 1, ACS_ULCORNER); + mvaddch(y + 2, x - 1, ACS_LLCORNER); + mvaddch(y - 2, x + len, ACS_URCORNER); + mvaddch(y + 2, x + len, ACS_LRCORNER); + + for (i = 0; i < 3; i++) { + mvaddch(y - 1 + i, x + len, ACS_VLINE); + mvaddch(y - 1 + i, x - 1 ,ACS_VLINE); + } + + for (i = 0; i < len; i++) { + mvaddch(y - 2, x + i, ACS_HLINE); + mvaddch(y - 1, x + i, ' '); + mvaddch(y + 1, x + i, ' '); + mvaddch(y + 2, x + i, ACS_HLINE); + } + + mvaddstr(y, x, text); + attroff(A_STANDOUT); + + row = y + 2; +} + +static void draw_help(void) +{ +#define HW 46 +#define HH 19 + int i, y = (rows/2) - (HH/2); + int x = (cols/2) - (HW/2); + char pad[HW+1]; + + memset(pad, ' ', sizeof(pad)); + pad[sizeof(pad) - 1] = '\0'; + + attron(A_STANDOUT); + + for (i = 0; i < HH; i++) + mvaddnstr(y + i, x, pad, -1); + + mvaddch(y - 1, x - 1, ACS_ULCORNER); + mvaddch(y + HH, x - 1, ACS_LLCORNER); + + mvaddch(y - 1, x + HW, ACS_URCORNER); + mvaddch(y + HH, x + HW, ACS_LRCORNER); + + for (i = 0; i < HH; i++) { + mvaddch(y + i, x - 1, ACS_VLINE); + mvaddch(y + i, x + HW, ACS_VLINE); + } + + for (i = 0; i < HW; i++) { + mvaddch(y - 1, x + i, ACS_HLINE); + mvaddch(y + HH, x + i, ACS_HLINE); + } + + attron(A_BOLD); + mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); + attron(A_UNDERLINE); + mvaddnstr(y+ 0, x+1, "Navigation", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+ 1, x+3, "Up, Down Previous/Next element", -1); + mvaddnstr(y+ 2, x+3, "PgUp, PgDown Scroll up/down entire page", -1); + mvaddnstr(y+ 3, x+3, "Left, Right Previous/Next attribute", -1); + mvaddnstr(y+ 4, x+3, "[, ] Previous/Next group", -1); + mvaddnstr(y+ 5, x+3, "? Toggle quick reference", -1); + mvaddnstr(y+ 6, x+3, "q Quit bmon", -1); + + attron(A_BOLD | A_UNDERLINE); + mvaddnstr(y+ 8, x+1, "Display Settings", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+ 9, x+3, "d Toggle detailed statistics", -1); + mvaddnstr(y+10, x+3, "l Toggle element list", -1); + mvaddnstr(y+11, x+3, "i Toggle additional info", -1); + + attron(A_BOLD | A_UNDERLINE); + mvaddnstr(y+13, x+1, "Graph Settings", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+14, x+3, "g Toggle graphical statistics", -1); + mvaddnstr(y+15, x+3, "H Start recording history data", -1); + mvaddnstr(y+16, x+3, "TAB Switch time unit of graph", -1); + mvaddnstr(y+17, x+3, "<, > Change number of graphs", -1); + + attroff(A_STANDOUT); + + row = y + HH; +} + +static int lines_required_for_header(void) +{ + return 1; +} + +static void draw_header(void) +{ + apply_layout(LAYOUT_STATUSBAR); + + if (current_element) + put_line(" %s %c%s%c", + current_element->e_name, + current_element->e_description ? '(' : ' ', + current_element->e_description ? : "", + current_element->e_description ? ')' : ' '); + else + put_line(""); + + move(row, COLS - strlen(PACKAGE_STRING) - 1); + put_line("%s", PACKAGE_STRING); + move(row, 0); +} + +static int lines_required_for_statusbar(void) +{ + return 1; +} + +static void draw_statusbar(void) +{ + static const char *help_text = "Press ? for help"; + char s[27]; + time_t t = time(0); + + apply_layout(LAYOUT_STATUSBAR); + + asctime_r(localtime(&t), s); + s[strlen(s) - 1] = '\0'; + + row = rows-1; + move(row, 0); + put_line(" %s", s); + + move(row, COLS - strlen(help_text) - 1); + put_line("%s", help_text); + + move(row, 0); +} + +static void count_attr_graph(struct element *g, struct attr *a, void *arg) +{ + if (a == current_attr) + graph_offset = ngraph; + + ngraph++; +} + +static int lines_required_for_graph(void) +{ + int lines = 0; + + ngraph = 0; + + if (c_show_graph && current_element) { + graph_display = GRAPH_DISPLAY_STANDARD; + + element_foreach_attr(current_element, count_attr_graph, NULL); + + if (ngraph > c_ngraph) + ngraph = c_ngraph; + + /* check if we have room to draw graphs on the same level */ + if (cols > (2 * (c_graph_cfg.gc_width + 10))) + graph_display = GRAPH_DISPLAY_SIDE_BY_SIDE; + + /* +2 = header + time axis */ + lines = ngraph * (graph_display * (c_graph_cfg.gc_height + 2)); + } + + return lines + 1; +} + +static int lines_required_for_details(void) +{ + int lines = 1; + + if (c_show_details && current_element) { + lines++; /* header */ + + detail_cols = cols / DETAILS_COLS; + + if (!detail_cols) + detail_cols = 1; + + lines += (current_element->e_nattrs / detail_cols); + if (current_element->e_nattrs % detail_cols) + lines++; + } + + return lines; +} + +static void count_element_lines(struct element_group *g, struct element *e, + void *arg) +{ + int *lines = arg; + + if (e == current_element) + selection_offset = *lines; + + (*lines)++; +} + +static void count_group_lines(struct element_group *g, void *arg) +{ + int *lines = arg; + + /* group title */ + (*lines)++; + + group_foreach_element(g, &count_element_lines, arg); +} + +static int lines_required_for_list(void) +{ + int lines = 0; + + if (c_show_list) + group_foreach(&count_group_lines, &lines); + else + lines = 1; + + return lines; +} + +static inline int line_visible(int line) +{ + return line >= offset && line < (offset + list_length); +} + +static void draw_attr(double rate1, int prec1, char *unit1, + double rate2, int prec2, char *unit2, + float usage, int ncol) +{ + char buf[32]; + + move(row, ncol); + addch(ACS_VLINE); + printw("%7s%-3s", + float2str(rate1, 7, prec1, buf, sizeof(buf)), unit1); + + printw("%7s%-3s", + float2str(rate2, 7, prec2, buf, sizeof(buf)), unit2); + + if (usage != FLT_MAX) + printw("%2.0f%%", usage); + else + printw("%3s", ""); +} + +static void draw_element(struct element_group *g, struct element *e, + void *arg) +{ + int *line = arg; + + apply_layout(LAYOUT_LIST); + + if (line_visible(*line)) { + char *rxu1 = "", *txu1 = "", *rxu2 = "", *txu2 = ""; + double rx1 = 0.0f, tx1 = 0.0f, rx2 = 0.0f, tx2 = 0.0f; + char pad[IFNAMSIZ + 32]; + int rx1prec = 0, tx1prec = 0, rx2prec = 0, tx2prec = 0; + struct attr *a; + + NEXT_ROW(); + + 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 < 6 ? e->e_level * 2 : 12); + + 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); + } + + if (*line == offset) { + attron(A_BOLD); + addch(ACS_UARROW); + attroff(A_BOLD); + addch(' '); + } else if (e == current_element) { + apply_layout(LAYOUT_SELECTED); + printw("->"); + apply_layout(LAYOUT_LIST); + } else if (*line == offset + list_length - 1 && + *line < (list_req - 1)) { + attron(A_BOLD); + addch(ACS_DARROW); + attroff(A_BOLD); + addch(' '); + } else + printw(" "); + + put_line("%-30.30s", pad); + + draw_attr(rx1, rx1prec, rxu1, rx2, rx2prec, rxu2, + e->e_rx_usage, LIST_COL_1); + + draw_attr(tx1, tx1prec, txu1, tx2, tx2prec, txu2, + e->e_tx_usage, LIST_COL_2); + + } + + (*line)++; +} + +static void draw_group(struct element_group *g, void *arg) +{ + int *line = arg; + + if (line_visible(*line)) { + NEXT_ROW(); + attron(A_BOLD); + put_line("%s", g->g_hdr->gh_title); + + attroff(A_BOLD); + mvaddch(row, LIST_COL_1, ACS_VLINE); + attron(A_BOLD); + put_line("%7s %7s %%", + g->g_hdr->gh_column[0], + g->g_hdr->gh_column[1]); + + attroff(A_BOLD); + mvaddch(row, LIST_COL_2, ACS_VLINE); + attron(A_BOLD); + put_line("%7s %7s %%", + g->g_hdr->gh_column[2], + g->g_hdr->gh_column[3]); + } + + (*line)++; + + group_foreach_element(g, draw_element, arg); +} + +static void draw_element_list(void) +{ + int line = 0; + + group_foreach(draw_group, &line); +} + +static inline int attr_visible(int nattr) +{ + return nattr >= graph_offset && nattr < (graph_offset + ngraph); +} + +static void draw_graph_centered(struct graph *g, int row, int ncol, + const char *text) +{ + int hcenter = (g->g_cfg.gc_width / 2) - (strlen(text) / 2) + 8; + + if (hcenter < 9) + hcenter = 9; + + mvprintw(row, ncol + hcenter, "%.*s", g->g_cfg.gc_width, text); +} + +static void draw_table(struct graph *g, struct graph_table *tbl, + struct attr *a, struct history *h, + const char *hdr, int ncol) +{ + int i, save_row; + char buf[32]; + + if (!tbl->gt_table) { + for (i = g->g_cfg.gc_height; i >= 0; i--) { + move(++row, ncol); + put_line(""); + } + return; + } + + move(++row, ncol); + put_line("%8s", tbl->gt_y_unit ? : ""); + + snprintf(buf, sizeof(buf), "(%s %s/%s)", + hdr, a->a_def->ad_description, + h ? h->h_definition->hd_name : "?"); + + draw_graph_centered(g, row, ncol, buf); + + //move(row, ncol + g->g_cfg.gc_width - 3); + //put_line("[err %.2f%%]", rtiming.rt_variance.v_error); + + for (i = (g->g_cfg.gc_height - 1); i >= 0; i--) { + move(++row, ncol); + put_line("%'8.2f %s", + tbl->gt_scale[i], + tbl->gt_table + (i * graph_row_size(&g->g_cfg))); + } + + move(++row, ncol); + put_line(" 1"); + + for (i = 1; i <= g->g_cfg.gc_width; i++) { + if (i % 5 == 0) { + move(row, ncol + i + 7); + printw("%2d", i); + } + } + + if (!h) { + const char *t1 = " No history data available. "; + const char *t2 = " Press h to start collecting history. "; + int vcenter = g->g_cfg.gc_height / 2; + + save_row = row; + draw_graph_centered(g, save_row - vcenter - 1, ncol, t1); + draw_graph_centered(g, save_row - vcenter, ncol, t2); + row = save_row; + } +} + +static void draw_history_graph(struct attr *a, struct history *h) +{ + struct graph *g; + int ncol = 0, save_row; + + g = graph_alloc(h, &c_graph_cfg); + graph_refill(g, h); + + save_row = row; + draw_table(g, &g->g_rx, a, h, "RX", ncol); + + if (graph_display == GRAPH_DISPLAY_SIDE_BY_SIDE) { + ncol = cols / 2; + row = save_row; + } + + draw_table(g, &g->g_tx, a, h, "TX", ncol); + + graph_free(g); +} + +static void draw_attr_graph(struct element *e, struct attr *a, void *arg) +{ + int *nattr = arg; + + if (attr_visible(*nattr)) { + struct history_def *sel; + struct history *h; + + sel = history_current(); + c_graph_cfg.gc_unit = a->a_def->ad_unit; + + list_for_each_entry(h, &a->a_history_list, h_list) { + if (h->h_definition != sel) + continue; + + draw_history_graph(a, h); + goto out; + } + + draw_history_graph(a, NULL); + } + +out: + (*nattr)++; +} + +static void draw_graph(void) +{ + int nattr = 0; + + element_foreach_attr(current_element, &draw_attr_graph, &nattr); +} + +static int lines_required_for_info(void) +{ + int lines = 1; + + if (c_show_info) { + info_cols = cols / DETAILS_COLS; + + if (!info_cols) + info_cols = 1; + + lines += (current_element->e_ninfo / info_cols); + if (current_element->e_ninfo % info_cols) + lines++; + } + + return lines; +} + +static void __draw_info(struct element *e, struct info *info, int *ninfo) +{ + int ncol; + + ncol = ((*ninfo) * DETAILS_COLS) - 1; + move(row, ncol); + if (ncol > 0) + addch(ACS_VLINE); + + put_line(" %-14.14s %22.22s", info->i_name, info->i_value); + + if (++(*ninfo) >= info_cols) { + NEXT_ROW(); + *ninfo = 0; + } +} + +static void draw_info(void) +{ + struct info *info; + int i, ninfo = 0; + + if (!current_element->e_ninfo) + return; + + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS) - 1, + c_show_details ? ACS_PLUS : ACS_TTEE); + + NEXT_ROW(); + list_for_each_entry(info, ¤t_element->e_info_list, i_list) + __draw_info(current_element, info, &ninfo); + + /* + * If the last row was incomplete, not all vlines have been drawn. + * draw them here + */ + for (i = 1; i < info_cols; i++) + mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE); +} + +static void draw_content(void) +{ + int graph_req, details_req, lines_available, total_req; + int info_req, empty_lines; + int disable_graph = 0, disable_details = 0, disable_info = 0; + + if (!current_element) + return; + + /* + * Reset selection offset. Will be set in lines_required_for_list(). + */ + selection_offset = 0; + offset = 0; + + /* Reset graph offset, will be set in lines_required_for_graph() */ + graph_offset = 0; + + lines_available = rows - lines_required_for_statusbar() + - lines_required_for_header(); + + list_req = lines_required_for_list(); + graph_req = lines_required_for_graph(); + details_req = lines_required_for_details(); + info_req = lines_required_for_info(); + + total_req = list_req + graph_req + details_req + info_req; + + if (total_req <= lines_available) { + /* + * Enough lines available for all data to displayed, all + * is good. Display the full list. + */ + list_length = list_req; + goto draw; + } + + /* + * Not enough lines available for full list and all details + * requested... + */ + + if (c_show_list) { + /* + * ... try shortening the list first. + */ + list_length = lines_available - (total_req - list_req); + if (list_length >= c_list_min) + goto draw; + } + + if (c_show_info) { + /* try disabling info */ + list_length = lines_available - (total_req - info_req + 1); + if (list_length >= c_list_min) { + disable_info = 1; + goto draw; + } + } + + if (c_show_details) { + /* ... try disabling details */ + list_length = lines_available - (total_req - details_req + 1); + if (list_length >= c_list_min) { + disable_details = 1; + goto draw; + } + } + + /* ... try disabling graph, details, and info */ + list_length = lines_available - 1 - 1 - 1; + if (list_length >= c_list_min) { + disable_graph = 1; + disable_details = 1; + disable_info = 1; + goto draw; + } + + NEXT_ROW(); + put_line("A minimum of %d lines is required to display content.\n", + (rows - lines_available) + c_list_min + 2); + return; + +draw: + if (selection_offset && list_length > 0) { + /* + * Vertically align the selected element in the middle + * of the list. + */ + offset = selection_offset - (list_length / 2); + + /* + * If element 0..(list_length/2) is selected, offset is + * negative here. Start drawing from first element. + */ + if (offset < 0) + offset = 0; + + /* + * Ensure the full list length is used if one of the + * last (list_length/2) elements is selected. + */ + if (offset > (list_req - list_length)) + offset = (list_req - list_length); + + if (offset >= list_req) + BUG(); + } + + if (c_show_list) { + draw_element_list(); + } else { + NEXT_ROW(); + hline(ACS_HLINE, cols); + center_text(" Press %c to enable list view ", + KEY_TOGGLE_LIST); + } + + /* + * Graphical statistics + */ + NEXT_ROW(); + hline(ACS_HLINE, cols); + mvaddch(row, LIST_COL_1, ACS_BTEE); + mvaddch(row, LIST_COL_2, ACS_BTEE); + + if (!c_show_graph) + center_text(" Press %c to enable graphical statistics ", + KEY_TOGGLE_GRAPH); + else { + if (disable_graph) + center_text(" Increase screen height to see graphical statistics "); + else + draw_graph(); + } + + empty_lines = rows - row - details_req - info_req + - lines_required_for_statusbar() - 1; + + while (empty_lines-- > 0) { + NEXT_ROW(); + put_line(""); + } + + /* + * Detailed statistics + */ + NEXT_ROW(); + hline(ACS_HLINE, cols); + + if (!c_show_details) + center_text(" Press %c to enable detailed statistics ", + KEY_TOGGLE_DETAILS); + else { + if (disable_details) + center_text(" Increase screen height to see detailed statistics "); + else + draw_details(); + } + + /* + * Additional information + */ + NEXT_ROW(); + hline(ACS_HLINE, cols); + + if (!c_show_info) + center_text(" Press %c to enable additional information ", + KEY_TOGGLE_INFO); + else { + if (disable_info) + center_text(" Increase screen height to see additional information "); + else + draw_info(); + } +} + + +static void curses_draw(void) +{ + row = 0; + move(0,0); + + getmaxyx(stdscr, rows, cols); + + if (rows < 4) { + clear(); + put_line("Screen must be at least 4 rows in height"); + goto out; + } + + if (cols < 48) { + clear(); + put_line("Screen must be at least 48 columns width"); + goto out; + } + + current_element = element_current(); + current_attr = attr_current(); + + draw_header(); + + apply_layout(LAYOUT_DEFAULT); + draw_content(); + + /* fill empty lines with blanks */ + while (row < (rows - 1 - lines_required_for_statusbar())) { + move(++row, 0); + put_line(""); + } + + draw_statusbar(); + + if (quit_mode) + print_message(" Really Quit? (y/n) "); + else if (print_help) { + if (help_page == 0) + draw_help(); +#if 0 + else + draw_help_2(); +#endif + } + +out: + attrset(0); + refresh(); +} + +static int handle_input(int ch) +{ + switch (ch) + { + case 'q': + if (print_help) + print_help = 0; + else + quit_mode = quit_mode ? 0 : 1; + return 1; + + case 0x1b: + quit_mode = 0; + print_help = 0; + return 1; + + case 'y': + if (quit_mode) + exit(0); + break; + + case 'n': + if (quit_mode) + quit_mode = 0; + return 1; + + case 12: + case KEY_CLEAR: +#ifdef HAVE_REDRAWWIN + redrawwin(stdscr); +#endif + clear(); + return 1; + + case '?': + clear(); + print_help = 1; + return 1; + + case KEY_TOGGLE_GRAPH: + c_show_graph = !c_show_graph; + if (c_show_graph && !c_ngraph) + c_ngraph = 1; + return 1; + + case KEY_TOGGLE_DETAILS: + c_show_details = !c_show_details; + return 1; + + case KEY_TOGGLE_LIST: + c_show_list = !c_show_list; + return 1; + + case KEY_TOGGLE_INFO: + c_show_info = !c_show_info; + return 1; + + case KEY_COLLECT_HISTORY: + if (current_attr) { + attr_start_collecting_history(current_attr); + return 1; + } + break; + + case KEY_PPAGE: + { + int i; + for (i = 1; i < list_length; i++) + element_select_prev(); + } + return 1; + + case KEY_NPAGE: + { + int i; + for (i = 1; i < list_length; i++) + element_select_next(); + } + return 1; + + case KEY_DOWN: + element_select_next(); + return 1; + + case KEY_UP: + element_select_prev(); + return 1; + + case KEY_LEFT: + attr_select_prev(); + return 1; + + case KEY_RIGHT: + attr_select_next(); + return 1; + + case ']': + group_select_next(); + return 1; + + case '[': + group_select_prev(); + return 1; + + case '<': + c_ngraph--; + if (c_ngraph <= 1) + c_ngraph = 1; + return 1; + + case '>': + c_ngraph++; + if (c_ngraph > 32) + c_ngraph = 32; + return 1; + + case '\t': + history_select_next(); + return 1; + } + + return 0; +} + +static void curses_pre(void) +{ + for (;;) { + int ch = getch(); + + if (ch == -1) + break; + + if (handle_input(ch)) + curses_draw(); + } +} + +static void print_module_help(void) +{ + printf( + "curses - Curses Output\n" \ + "\n" \ + " Interactive curses UI. Press '?' to see help.\n" \ + " Author: Thomas Graf \n" \ + "\n" \ + " Options:\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" \ + " gheight=NUM Height of graph (default: 6)\n" \ + " gwidth=NUM Width of graph (default: 60)\n" \ + " ngraph=NUM Number of graphs (default: 1)\n" \ + " nocolors Do not use colors\n" \ + " graph Show graphical stats by default\n" \ + " details Show detailed stats by default\n" \ + " minlist=INT Minimum item list length\n"); +} + +static void curses_parse_opt(const char *type, const char *value) +{ + if (!strcasecmp(type, "fgchar") && value) + c_graph_cfg.gc_foreground = value[0]; + else if (!strcasecmp(type, "bgchar") && value) + c_graph_cfg.gc_background = value[0]; + else if (!strcasecmp(type, "nchar") && value) + c_graph_cfg.gc_noise = value[0]; + else if (!strcasecmp(type, "uchar") && value) + c_graph_cfg.gc_unknown = value[0]; + else if (!strcasecmp(type, "gheight") && value) + c_graph_cfg.gc_height = strtol(value, NULL, 0); + else if (!strcasecmp(type, "gwidth") && value) + c_graph_cfg.gc_width = strtol(value, NULL, 0); + else if (!strcasecmp(type, "ngraph")) { + c_ngraph = strtol(value, NULL, 0); + c_show_graph = !!c_ngraph; + } else if (!strcasecmp(type, "details")) + c_show_details = 1; + else if (!strcasecmp(type, "nocolors")) + c_use_colors = 0; + else if (!strcasecmp(type, "minlist") && value) + c_list_min = strtol(value, NULL, 0); + else if (!strcasecmp(type, "help")) { + print_module_help(); + exit(0); + } +} + +static struct bmon_module curses_ops = { + .m_name = "curses", + .m_flags = BMON_MODULE_DEFAULT, + .m_init = curses_init, + .m_shutdown = curses_shutdown, + .m_pre = curses_pre, + .m_do = curses_draw, + .m_parse_opt = curses_parse_opt, +}; + +static void __init do_curses_init(void) +{ + output_register(&curses_ops); +} diff --git a/src/out_format.c b/src/out_format.c new file mode 100644 index 0000000..e373b81 --- /dev/null +++ b/src/out_format.c @@ -0,0 +1,373 @@ +/* + * out_format.c Formatted Output + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 \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: RX counter of attribute \n" \ + " :tx: TX counter of attribute \n" \ + " :rxrate: RX rate of attribute \n" \ + " :txrate: TX rate of attribute \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); +} diff --git a/src/out_null.c b/src/out_null.c new file mode 100644 index 0000000..bd84989 --- /dev/null +++ b/src/out_null.c @@ -0,0 +1,56 @@ +/* + * out_null.c Null Output + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include + +static void print_help(void) +{ + printf( + "null - No output\n" \ + "\n" \ + " Disable primary output method\n" \ + " Author: Thomas Graf \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); +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..819cb1b --- /dev/null +++ b/src/output.c @@ -0,0 +1,95 @@ +/* + * output.c Output API + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include +#include +#include + +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); +} diff --git a/src/unit.c b/src/unit.c new file mode 100644 index 0000000..4e70fbe --- /dev/null +++ b/src/unit.c @@ -0,0 +1,216 @@ +/* + * unit.c Unit Definitions + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include +#include + +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); +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..bf49d9f --- /dev/null +++ b/src/utils.c @@ -0,0 +1,421 @@ +/* + * utils.c General purpose utilities + * + * Copyright (c) 2001-2013 Thomas Graf + * 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 +#include +#include + +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, ""); +} + +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