diff --git a/.travis.yml b/.travis.yml index e1755fd..bac4cb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,40 @@ language: c + +os: + - linux + - osx + +env: + global: + - secure: "C87Pgf5AVDoyQfm9MIv81g" + compiler: - gcc - clang -before_install: - - sudo apt-get install libconfuse-dev - - sudo apt-get install libnl-3-dev libnl-route-3-dev - - sudo apt-get install libncurses-dev -# Change this to your needs -script: ./autogen.sh && ./configure && make + +# container-based builds +sudo: false +addons: + apt: + packages: + # packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise + - libconfuse-dev + - libncurses5-dev + - libnl-3-dev + - libnl-route-3-dev + coverity_scan: + project: + name: "tgraf/bmon" + description: "bandwidth monitor" + notification_email: tgraf@suug.ch + build_command_prepend: "./autogen.sh && ./configure" + build_command: "make -j2" + branch_pattern: coverity_scan + + +install: + - if [ "$TRAVIS_BRANCH" = "coverity_scan" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$CC" = "gcc" ]; then exit ; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null && brew install confuse ; fi + +script: + - ./.travis/run.sh diff --git a/.travis/run.sh b/.travis/run.sh new file mode 100755 index 0000000..46ea437 --- /dev/null +++ b/.travis/run.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +FLAGS="-Werror" + +if [ $CC = "clang" ]; then + FLAGS="$FLAGS -Wno-error=unused-command-line-argument" +fi + +./autogen.sh && ./configure && make CFLAGS="$FLAGS" && make distcheck diff --git a/Makefile.am b/Makefile.am index d4e5b01..93da670 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,4 +4,4 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src man include examples -EXTRA_DIST = ChangeLog LICENSE.BSD LICENSE.MIT NEWS README.adoc +EXTRA_DIST = ChangeLog LICENSE.BSD LICENSE.MIT NEWS README.md diff --git a/NEWS b/NEWS index dd3baff..b62f316 100644 --- a/NEWS +++ b/NEWS @@ -1,12 +1,46 @@ +HEAD + +v4.0 - Dec 13, 2016 + * Use monotonic clock instead of realtime clock + * Pick default selected interface based on policy + * Collect RX NoHandler statistics if available (Linux) + * CentOS installation instructions + * Proper stdout flush in ASCII mode + * Bugfixes + +v3.9 - Jul 19, 2016 + * Color support + * Add ability to reset statistics from curses UI + * NetBSD compile fix + * Option to enable info display by default + * Additional IPv6 statistics + * Various fixes + +v3.8 - July 25, 2015 + * Don't disable Netlink if TC stats are unavailable + +v3.7 - November 22, 2014 + * Bugfixes + * Documentation updates + * Provide minimal interface information on BSD + +v3.6 - November 22, 2014 + * Build fix for uclinux + * Fix LICENSE links + +v3.5 - August 30, 2014 + * Fixes for all defects identified by coverity + * Fix accuracy issue on total rate calculation + * Travis-CI support + * Various other small bugfixes + v3.4 - August 24, 2014 ----------------------- * Bugfixes * blank screen with config file * quick-help toggle with '?' in curses * Better bmon.conf example v3.3 - July 6, 2014 -------------------- * MacOS X port * Only initialize curses module if actually used * Assorted bug and spelling fixes diff --git a/README.md b/README.md index 3c8055d..4d6acb7 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,86 @@ 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. -## Changes - -### New in 3.4 +## Download + + * [Latest Release](https://github.com/tgraf/bmon/releases/latest) + * [Older Releases](https://github.com/tgraf/bmon/releases) + +## Debian/Ubuntu Installation + +``` +git clone https://github.com/tgraf/bmon.git +cd bmon +apt-get install build-essential make libconfuse-dev libnl-3-dev libnl-route-3-dev libncurses-dev pkg-config dh-autoreconf +./autogen.sh +./configure +make +make install +bmon +``` +## CentOS 6 Installation + +``` +git clone https://github.com/tgraf/bmon.git +cd bmon +yum install make libconfuse-devel libnl3-devel libnl-route3-devel ncurses-devel +./autogen.sh +./configure +make +make install +bmon +``` + +## CentOS 7 Installation + +``` +yum install bmon +``` + +## Fedora Installation + +``` +dnf install bmon +``` + +## OSX Installation + +### Brew +``` +brew install bmon +``` + +### Compile yourself +Install libconfuse +``` +wget https://github.com/martinh/libconfuse/releases/download/v2.8/confuse-2.8.zip +unzip confuse-2.8.zip && cd confuse-2.8 +PATH=/usr/local/opt/gettext/bin:$PATH ./configure +make +make install +``` + +Install bmon +``` +git clone https://github.com/tgraf/bmon.git +cd bmon +./autogen.sh +./configure +make +make install +bmon +``` + +------------- +## New in 4.0 + * Use monotonic clock instead of realtime clock + * Pick default selected interface based on policy + * Collect RX NoHandler statistics if available (Linux) + * CentOS installation instructions + * Proper stdout flush in ASCII mode * Bugfixes - * blank screen with config file - * quick-help toggle with '?' in curses - * Better bmon.conf example - -## New in 3.3 - * MacOS X port - * Only initialize curses module if actually used - * Assorted bug and spelling fixes - * Various build fixes +------------- ### Usage To run bmon in the default curses mode: @@ -35,14 +101,18 @@ provided via: ## Screenshots -![Screenshot 1](https://github.com/tgraf/bmon/raw/gh-pages/images/shot1.png) -![Screenshot 2](https://github.com/tgraf/bmon/raw/gh-pages/images/shot2.png) +![Screenshot 1](https://github.com/tgraf/bmon/raw/gh-pages/images/shot3.png) +![Screenshot 2](https://github.com/tgraf/bmon/raw/gh-pages/images/shot1.png) +![Screenshot 3](https://github.com/tgraf/bmon/raw/gh-pages/images/shot2.png) ## Copyright -> *Copyright (c) 2001-2014 Thomas Graf -> Copyright (c) 2013 Red Hat, Inc.* +Various authors, see git commit log. + +> *Copyright (c) 2001-2016 Thomas Graf * +> *Copyright (c) 2013 Red Hat, Inc.* -Please see the [LICENSE](https://github.com/tgraf/bmon/blob/master/LICENSE) -file for additional details. +Please see the [LICENSE.BSD](https://github.com/tgraf/bmon/blob/master/LICENSE.BSD) +and [LICENSE.MIT](https://github.com/tgraf/bmon/blob/master/LICENSE.MIT) files for +additional details. diff --git a/configure.ac b/configure.ac index d2040c5..afc43c7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # # configure.in Configure Script # -# Copyright (c) 2001-2013 Thomas Graf +# Copyright (c) 2001-2016 Thomas Graf # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -21,7 +21,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -AC_INIT(bmon, 3.4, [], [], [http://www.infradead.org/~tgr/bmon/]) +AC_INIT(bmon, 4.0, [], [], [https://github.com/tgraf/bmon]) AC_CONFIG_HEADERS(include/bmon/defs.h) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -58,10 +58,18 @@ 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(atexit clock_gettime memset pow socket strcasecmp) AC_CHECK_FUNCS(strchr strdup strerror strncasecmp strstr strtol) AC_CHECK_FUNCS(uname getdate) +AC_PATH_PROG([PKG_CONFIG], [pkg-config], [no]) +AS_IF([test "x$PKG_CONFIG" = "xno"],[ + AC_MSG_ERROR([ + *** The pkg-config script could not be found. Make sure it is + *** in your path, or set the PKG_CONFIG environment variable + *** to the full path to pkg-config.]) + ]) + 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]) @@ -70,7 +78,7 @@ fi PKG_CHECK_MODULES([CONFUSE], [libconfuse], [], AC_MSG_ERROR([requires libconfuse])) case ${target_os} in - linux*) + linux*|uclinux*) PKG_CHECK_MODULES([LIBNL], [libnl-3.0], [], AC_MSG_ERROR([requires libnl3-dev])) PKG_CHECK_MODULES([LIBNL_ROUTE], [libnl-route-3.0], [], AC_MSG_ERROR([requires libnl3-route])) ;; @@ -78,6 +86,9 @@ esac AC_CHECK_LIB(m, pow, [], AC_MSG_ERROR([requires libm])) +# Don't fail if not found (for instance, OS X does not have clock_gettime) +AC_CHECK_LIB(rt, clock_gettime, [], []) + BMON_LIB="" ##################################################################### @@ -164,7 +175,7 @@ case ${target_os} in AC_DEFINE_UNQUOTED(SYS_SUNOS, "1", [operating system]) ;; - *bsd*) + *bsd*|dragonfly*) AC_DEFINE_UNQUOTED(SYS_BSD, "1", [operating system]) ;; diff --git a/examples/bmon.conf b/examples/bmon.conf index 389b054..7cf43b9 100644 --- a/examples/bmon.conf +++ b/examples/bmon.conf @@ -11,10 +11,10 @@ /* * element eth0 { - * description = { "My description" } - * rxmax = { 10000 } - * txmax = { 10000 } - * max = { 12500000 } + * description = "My description" + * rxmax = 10000 + * txmax = 10000 + * max = 12500000 * } */ @@ -86,3 +86,27 @@ history day { interval = 86400. size = 60 } + +layout colors { + color default { + color_pair = {"white", "black"} + } + color statusbar { + color_pair = {"blue", "white", "reverse"} + } + color header { + color_pair = {"yellow", "black"} + } + color list { + color_pair = {"white", "black"} + } + color selected { + color_pair = {"white", "black", "reverse"} + } + color RX_graph { + color_pair = {"green", "black"} + } + color TX_graph { + color_pair = {"red", "black"} + } +} diff --git a/include/Makefile.am b/include/Makefile.am index 327db14..75a88fc 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -16,4 +16,5 @@ noinst_HEADERS = \ bmon/module.h \ bmon/output.h \ bmon/unit.h \ + bmon/layout.h \ bmon/utils.h diff --git a/include/bmon/attr.h b/include/bmon/attr.h index 224f151..9d52c78 100644 --- a/include/bmon/attr.h +++ b/include/bmon/attr.h @@ -42,6 +42,9 @@ struct rate /* Value of r_current at last read */ uint64_t r_prev; + /* Reset value to substract to emulate statistics reset */ + uint64_t r_reset; + /* Rate per second calculated every `rate_interval' */ float r_rate; @@ -49,6 +52,8 @@ struct rate timestamp_t r_last_calc; }; +extern uint64_t rate_get_total(struct rate *); + enum { ATTR_TYPE_UNSPEC, ATTR_TYPE_COUNTER, @@ -134,5 +139,6 @@ extern struct attr * attr_select_prev(void); extern struct attr * attr_current(void); extern void attr_start_collecting_history(struct attr *); +extern void attr_reset_counter(struct attr *a); #endif diff --git a/include/bmon/bmon.h b/include/bmon/bmon.h index e349110..05934ec 100644 --- a/include/bmon/bmon.h +++ b/include/bmon/bmon.h @@ -60,9 +60,11 @@ enum { #if defined __GNUC__ #define __init __attribute__ ((constructor)) #define __exit __attribute__ ((destructor)) +#define __unused__ __attribute__ ((unused)) #else #define __init #define __exit +#define __unused__ #endif #ifdef DEBUG diff --git a/include/bmon/conf.h b/include/bmon/conf.h index f95d48f..6f248af 100644 --- a/include/bmon/conf.h +++ b/include/bmon/conf.h @@ -74,6 +74,8 @@ enum { LAYOUT_HEADER, LAYOUT_LIST, LAYOUT_SELECTED, + LAYOUT_RX_GRAPH, + LAYOUT_TX_GRAPH, __LAYOUT_MAX }; diff --git a/include/bmon/config.h b/include/bmon/config.h index ad6a367..e1d749f 100644 --- a/include/bmon/config.h +++ b/include/bmon/config.h @@ -48,7 +48,7 @@ #include #ifdef SYS_BSD # include -#else +#elif !defined(__ANDROID__) # include #endif diff --git a/include/bmon/defs.h.in b/include/bmon/defs.h.in index d176c30..92b72eb 100644 --- a/include/bmon/defs.h.in +++ b/include/bmon/defs.h.in @@ -9,6 +9,9 @@ /* Define to 1 if you have the `atexit' function. */ #undef HAVE_ATEXIT +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + /* have curses */ #undef HAVE_CURSES @@ -39,15 +42,15 @@ /* 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 `rt' library (-lrt). */ +#undef HAVE_LIBRT + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H diff --git a/include/bmon/layout.h b/include/bmon/layout.h new file mode 100644 index 0000000..4633806 --- /dev/null +++ b/include/bmon/layout.h @@ -0,0 +1,130 @@ + +#ifndef __BMON_LAYOUT_H_ +#define __BMON_LAYOUT_H_ + +#include +#include +#include +#include + + +static int parse_color(const char* color) +{ + int color_code = -1; + + if ((strcasestr(color, "red") != NULL)) + color_code = COLOR_RED; + else if ((strcasestr(color, "green") != NULL)) + color_code = COLOR_GREEN; + else if ((strcasestr(color, "white") != NULL)) + color_code = COLOR_WHITE; + else if ((strcasestr(color, "black") != NULL)) + color_code = COLOR_BLACK; + else if ((strcasestr(color, "blue") != NULL)) + color_code = COLOR_BLUE; + else if ((strcasestr(color, "yellow") != NULL)) + color_code = COLOR_YELLOW; + else if ((strcasestr(color, "magenta") != NULL)) + color_code = COLOR_MAGENTA; + else if ((strcasestr(color, "cyan") != NULL)) + color_code = COLOR_CYAN; + else if ((atoi(color) >= 0)) + color_code = atoi(color); + + return color_code; +} + +/* + A_NORMAL Normal display (no highlight) + A_STANDOUT Best highlighting mode of the terminal. + A_UNDERLINE Underlining + A_REVERSE Reverse video + A_BLINK Blinking + A_DIM Half bright + A_BOLD Extra bright or bold + A_PROTECT Protected mode + A_INVIS Invisible or blank mode + A_ALTCHARSET Alternate character set + A_CHARTEXT Bit-mask to extract a character +*/ + +static int parse_attribute(const char* attr) +{ + /* no attribute is valid, so we have nothing to do */ + if (attr == NULL) + return 0; + + if ((strcasestr(attr, "normal") != NULL)) + return A_NORMAL; + else if ((strcasestr(attr, "standout") != NULL)) + return A_STANDOUT; + else if ((strcasestr(attr, "underline") != NULL)) + return A_UNDERLINE; + else if ((strcasestr(attr, "reverse") != NULL)) + return A_REVERSE; + else if ((strcasestr(attr, "blink") != NULL)) + return A_BLINK; + else if ((strcasestr(attr, "dim") != NULL)) + return A_DIM; + else if ((strcasestr(attr, "bold") != NULL)) + return A_BOLD; + else if ((strcasestr(attr, "protect") != NULL)) + return A_PROTECT; + else if ((strcasestr(attr, "invis") != NULL)) + return A_INVIS; + else if ((strcasestr(attr, "altcharset") != NULL)) + return A_ALTCHARSET; + else if ((strcasestr(attr, "chartext") != NULL)) + return A_CHARTEXT; + + return -1; +} + + +static void add_layout(const char *layout_name, cfg_t *color_cfg) +{ + const char *fg, *bg, *attr_str = NULL; + int size = -1, fg_code, bg_code, attr_mask, layout_idx = 0; + + size = cfg_size(color_cfg, "color_pair"); + fg = cfg_getnstr(color_cfg, "color_pair", 0); + bg = cfg_getnstr(color_cfg, "color_pair", 1); + if (size > 2) + attr_str = cfg_getnstr(color_cfg, "color_pair", 2); + + fg_code = parse_color(fg); + bg_code = parse_color(bg); + if (fg_code == -1 || bg_code == -1) { + quit("Unknown color [%s]: %s\n", (fg_code == -1) ? "fg" : "bg", + (fg_code == -1) ? fg : bg); + } + attr_mask = parse_attribute(attr_str); + if (attr_mask == -1) { + quit("Unknown attribute: '%s'\n", attr_str); + } + + DBG("%s:\tfg: %s bg: %s attr: %s\n", layout_name, fg, bg, attr_str); + + if ((strcasecmp(layout_name, "default") == 0)) + layout_idx = LAYOUT_DEFAULT; + else if ((strcasecmp(layout_name, "statusbar") == 0)) + layout_idx = LAYOUT_STATUSBAR; + else if ((strcasecmp(layout_name, "header") == 0)) + layout_idx = LAYOUT_HEADER; + else if ((strcasecmp(layout_name, "list") == 0)) + layout_idx = LAYOUT_LIST; + else if ((strcasecmp(layout_name, "selected") == 0)) + layout_idx = LAYOUT_SELECTED; + else if ((strcasecmp(layout_name, "rx_graph") == 0)) + layout_idx = LAYOUT_RX_GRAPH; + else if ((strcasecmp(layout_name, "tx_graph") == 0)) + layout_idx = LAYOUT_TX_GRAPH; + else { + quit("Unknown layout name: '%s'\n", layout_name); + } + + struct layout l = { fg_code, bg_code, attr_mask}; + cfg_layout[layout_idx] = l; +} + +#endif /* __BMON_LAYOUT_H_ */ diff --git a/man/bmon.8 b/man/bmon.8 index c94f1ba..36e0d53 100644 --- a/man/bmon.8 +++ b/man/bmon.8 @@ -34,7 +34,7 @@ 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...] +\fB \-o\fR, \fB\-\-output=\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 @@ -44,7 +44,7 @@ text mode. See OUTPUT MODULES for more details. .PP \fB \-U\fR, \fB\-\-use\-si\fR .RS 4 -Use SI unit system instead of 1KB = 1'024 bytes. +Use SI unit system (1KB = 1'000 bytes) instead of 1KB = 1'024 bytes. .RE .PP \fB \-f\fR, \fB\-\-configfile=\fRFILE @@ -58,7 +58,7 @@ Set policy defining which network interfaces to display. See INTERFACE SELECTION for more details. .RE .PP -\fB \-a\fR, \fB\-\-show\-all=\fR +\fB \-a\fR, \fB\-\-show\-all\fR .RS 4 Display all interfaces, even interface that are administratively down. .RE @@ -75,6 +75,11 @@ Set interval in seconds in which the rate per counter is calculated. The default is 1.0 seconds. .RE .PP +\fB \-b\fR, \fB\-\-use\-bit\fR +.RS 4 +Show rates in bits per second instead of bytes per second. +.RE +.PP \fB \-L\fR, \fB\-\-lifetime=\fRFLOAT .RS 4 Set lifetime of an element in seconds before it is no longer displayed @@ -132,7 +137,7 @@ 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 '?' +estimations and a graphical representation of each attribute. Press '?' to display the quick reference guide. This is the default output mode. .TP @@ -215,6 +220,14 @@ and eth1: .RS 4 \fBbmon \-p eth0,eth1 \-o curses\fP .RE +.PP +To run bmon in format mode, monitoring any eth* interfaces, with a specified +format string: +.PP +.RS 4 +\fBbmon \-p \(aqeth*\(aq \-o format:fmt=\(aq$(element:name) $(attr:rxrate:packets)\en\(aq\fP +.RE +.PP .SH "FILES" /etc/bmon.conf diff --git a/src/Makefile.am b/src/Makefile.am index c773410..2e2aeb1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,6 +7,7 @@ bmon_CFLAGS = \ -I${top_builddir}/include \ -DSYSCONFDIR=\"$(sysconfdir)\" \ -D_GNU_SOURCE \ + -Wall \ $(CURSES_CFLAGS) \ $(CONFUSE_CFLAGS) \ $(LIBNL_CFLAGS) \ diff --git a/src/attr.c b/src/attr.c index bd001eb..cb33bb5 100644 --- a/src/attr.c +++ b/src/attr.c @@ -111,7 +111,7 @@ int attr_def_add(const char *name, const char *desc, struct unit *unit, return def->ad_id; } -void attr_def_free(struct attr_def *def) +static void attr_def_free(struct attr_def *def) { if (!def) return; @@ -524,6 +524,11 @@ static float __calc_usage(double rate, uint64_t max) return 100.0f / ((double) max / (rate * cfg_rate_interval)); } +uint64_t rate_get_total(struct rate *r) +{ + return r->r_total - r->r_reset; +} + void attr_calc_usage(struct attr *a, float *rx, float *tx, uint64_t rxmax, uint64_t txmax) { @@ -626,6 +631,14 @@ void attr_notify_update(struct attr *a, timestamp_t *ts) } } +void attr_reset_counter(struct attr *a) +{ + if (a->a_def->ad_type == ATTR_TYPE_COUNTER) { + a->a_rx_rate.r_reset = a->a_rx_rate.r_total; + a->a_tx_rate.r_reset = a->a_tx_rate.r_total; + } +} + static void __exit attr_exit(void) { struct attr_def *ad, *n; diff --git a/src/bmon.c b/src/bmon.c index ccb9167..882ab67 100644 --- a/src/bmon.c +++ b/src/bmon.c @@ -33,8 +33,6 @@ #include int start_time; -int do_quit = 0; -int is_daemon = 0; struct reader_timing rtiming; @@ -69,14 +67,14 @@ static char *usage_text = " 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" \ +" -o curses:help # Shows a help text for curses 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"; +"Please see the bmon(8) man pages for full documentation.\n"; static void do_shutdown(void) { @@ -88,14 +86,7 @@ static void do_shutdown(void) } } -RETSIGTYPE sig_int(int unused) -{ - if (do_quit) - exit(-1); - do_quit = 1; -} - -void sig_exit(void) +static void sig_exit(void) { do_shutdown(); } @@ -120,7 +111,7 @@ void quit(const char *fmt, ...) static inline void print_version(void) { printf("bmon %s\n", PACKAGE_VERSION); - printf("Copyright (C) 2001-2013 by Thomas Graf \n"); + printf("Copyright (C) 2001-2015 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 " \ @@ -136,10 +127,10 @@ static void parse_args_pre(int argc, char *argv[]) char *gostr = "+:hvVf:"; struct option long_opts[] = { - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, - {"configfile", 1, 0, 'f'}, - {0, 0, 0, 0}, + {"help", 0, NULL, 'h'}, + {"version", 0, NULL, 'v'}, + {"configfile", 1, NULL, 'f'}, + {NULL, 0, NULL, 0}, }; int c = getopt_long(argc, argv, gostr, long_opts, NULL); if (c == -1) @@ -175,17 +166,17 @@ static int parse_args_post(int argc, char *argv[]) "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'}, - {"use-bit", 0, 0, 'b'}, - {"lifetime", 1, 0, 'L'}, - {0, 0, 0, 0}, + {"input", 1, NULL, 'i'}, + {"output", 1, NULL, 'o'}, + {"policy", 1, NULL, 'p'}, + {"read-interval", 1, NULL, 'r'}, + {"rate-interval", 1, NULL, 'R'}, + {"sleep-interval", 1, NULL, 's'}, + {"show-all", 0, NULL, 'a'}, + {"use-si", 0, NULL, 'U'}, + {"use-bit", 0, NULL, 'b'}, + {"lifetime", 1, NULL, 'L'}, + {NULL, 0, NULL, 0}, }; int c = getopt_long(argc, argv, gostr, long_opts, NULL); if (c == -1) @@ -220,7 +211,7 @@ static int parse_args_post(int argc, char *argv[]) break; case 'a': - cfg_setint(cfg, "show_all", 1); + cfg_setbool(cfg, "show_all", cfg_true); break; case 'U': @@ -270,7 +261,7 @@ int main(int argc, char *argv[]) unsigned long sleep_time; double read_interval; - start_time = time(0); + start_time = time(NULL); memset(&rtiming, 0, sizeof(rtiming)); rtiming.rt_variance.v_min = FLT_MAX; @@ -361,9 +352,6 @@ int main(int argc, char *argv[]) output_post(); } - if (do_quit) - exit(0); - /* * ST := Configured ST */ @@ -399,5 +387,4 @@ int main(int argc, char *argv[]) static void __init bmon_init(void) { atexit(&sig_exit); - //signal(SIGINT, &sig_int); } diff --git a/src/conf.c b/src/conf.c index 48d07c8..c61fcf8 100644 --- a/src/conf.c +++ b/src/conf.c @@ -30,6 +30,7 @@ #include #include #include +#include #include cfg_t *cfg; @@ -69,6 +70,16 @@ static cfg_opt_t unit_opts[] = { CFG_END() }; +static cfg_opt_t color_opts[] = { + CFG_STR_LIST("color_pair", "", CFGF_NONE), + CFG_END() +}; + +static cfg_opt_t layout_opts[] = { + CFG_SEC("color", color_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), @@ -87,6 +98,7 @@ static cfg_opt_t global_opts[] = { 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), + CFG_SEC("layout", layout_opts, CFGF_MULTI | CFGF_TITLE), CFG_END() }; @@ -103,22 +115,26 @@ static char * configfile = NULL; #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 */ + {-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 */ + {-1, -1, 0}, /* RX graph */ + {-1, -1, 0}, /* TX graph */ }; #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 */ + {0, 0, 0}, /* dummy, not used */ + {COLOR_WHITE, COLOR_BLACK, 0}, /* default */ + {COLOR_BLUE, COLOR_GREEN, A_REVERSE}, /* statusbar */ + {COLOR_GREEN, COLOR_BLACK, 0}, /* header */ + {COLOR_WHITE, COLOR_BLACK, 0}, /* list */ + {COLOR_YELLOW, COLOR_BLACK, A_REVERSE}, /* selected */ + {COLOR_GREEN, COLOR_BLACK, 0}, /* RX graph */ + {COLOR_RED, COLOR_BLACK, 0}, /* TX graph */ }; #endif #endif @@ -371,7 +387,7 @@ static void configfile_read_units(void) static void configfile_read_attrs(void) { - int i, nattrs, t; + int i, nattrs, t = 0; nattrs = cfg_size(cfg, "attr"); @@ -423,6 +439,41 @@ static void configfile_read_attrs(void) } } +static void configfile_read_layout_cfg(void) +{ + int i, nlayouts; + cfg_t *lout; + nlayouts = cfg_size(cfg, "layout"); + for (i = 0; i < nlayouts; i++) + { + int c, ncolors; + const char *name; + if (!(lout = cfg_getnsec(cfg, "layout", i))) + BUG(); + + if (!(name = cfg_title(lout))) + BUG(); + + ncolors = cfg_size(lout, "color"); + if (ncolors > LAYOUT_MAX) { + fprintf(stderr, "Warning excceeded maximum number of layouts\n"); + ncolors = LAYOUT_MAX; + } + + for (c = 0; c < ncolors; c++) { + cfg_t *color_pair; + + if (!(color_pair = cfg_getnsec(lout, "color", c))) + BUG(); + + if (!(name = cfg_title(color_pair))) + BUG(); + + add_layout(name, color_pair); + } + } +} + static void conf_read(const char *path, int must) { int err; @@ -450,6 +501,7 @@ static void conf_read(const char *path, int must) configfile_read_history(); configfile_read_attrs(); configfile_read_element_cfg(); + configfile_read_layout_cfg(); } static const char default_config[] = \ @@ -508,6 +560,29 @@ static const char default_config[] = \ "history day {" \ " interval = 86400.0" \ " size = 60" \ +"}" +"layout colors {" \ +" color default {" \ +" color_pair = { \"white\", \"black\" }" \ +" }" \ +" color statusbar{" \ +" color_pair = { \"blue\", \"white\", \"reverse\" }" \ +" }" \ +" color header {" \ +" color_pair = { \"yellow\", \"black\" }" \ +" }" \ +" color list {" \ +" color_pair = { \"white\", \"black\" }" \ +" }" \ +" color selected {" \ +" color_pair = { \"yellow\", \"black\", \"reverse\" }" \ +" }" \ +" color rx_graph {" \ +" color_pair = { \"green\", \"black\" }" \ +" }" \ +" color tx_graph {" \ +" color_pair = { \"red\", \"black\" }" \ +" }" \ "}"; static void conf_read_default(void) @@ -524,6 +599,7 @@ static void conf_read_default(void) configfile_read_history(); configfile_read_attrs(); configfile_read_element_cfg(); + configfile_read_layout_cfg(); } void configfile_read(void) diff --git a/src/element.c b/src/element.c index f7c569b..32917db 100644 --- a/src/element.c +++ b/src/element.c @@ -113,8 +113,9 @@ void element_parse_policy(const char *policy) xfree(copy); } -struct element *__lookup_element(struct element_group *group, const char *name, - uint32_t id, struct element *parent) +static struct element *__lookup_element(struct element_group *group, + const char *name, uint32_t id, + struct element *parent) { struct list_head *list; struct element *e; @@ -200,12 +201,6 @@ void element_free(struct element *e) 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); @@ -220,6 +215,12 @@ void element_free(struct element *e) list_for_each_entry_safe(a, an, &e->e_attrhash[i], a_list) attr_free(a); + if (e->e_group->g_current == e) { + element_select_prev(); + if (e->e_group->g_current == e) + e->e_group->g_current = NULL; + } + list_del(&e->e_list); e->e_group->g_nelements--; @@ -357,6 +358,26 @@ int element_set_usage_attr(struct element *e, const char *usage) return 0; } +void element_pick_from_policy(struct element_group *g) +{ + if (!list_empty(&allowed)) { + struct policy *p; + + list_for_each_entry(p, &allowed, p_list) { + struct element *e; + + list_for_each_entry(e, &g->g_elements, e_list) { + if (match_mask(p, e->e_name)) { + g->g_current = e; + return; + } + } + } + } + + element_select_first(); +} + struct element *element_current(void) { struct element_group *g; @@ -364,8 +385,12 @@ struct element *element_current(void) if (!(g = group_current())) return NULL; + /* + * If no element is picked yet, pick a default interface according to + * the selection policy. + */ if (!g->g_current) - element_select_first(); + element_pick_from_policy(g); return g->g_current; } diff --git a/src/graph.c b/src/graph.c index e223b28..95866bd 100644 --- a/src/graph.c +++ b/src/graph.c @@ -58,26 +58,6 @@ 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) { diff --git a/src/group.c b/src/group.c index e745bbe..6991f5f 100644 --- a/src/group.c +++ b/src/group.c @@ -261,7 +261,7 @@ static void __init group_init(void) { DBG("init"); - group_new_hdr("intf", "Interfaces", + group_new_hdr(DEFAULT_GROUP, "Interfaces", "RX bps", "pps", "TX bps", "pps"); } diff --git a/src/in_dummy.c b/src/in_dummy.c index 9cf2c05..14edbce 100644 --- a/src/in_dummy.c +++ b/src/in_dummy.c @@ -89,7 +89,11 @@ static void dummy_read(void) char gname[32]; struct element_group *group; - snprintf(gname, sizeof(gname), "group%02d", gidx); + if (gidx == 0) + snprintf(gname, sizeof(gname), "%s", DEFAULT_GROUP); + else + snprintf(gname, sizeof(gname), "group%02d", gidx); + group = group_lookup(gname, GROUP_CREATE); for (n = 0; n < c_numdev; n++) { @@ -185,7 +189,7 @@ static void dummy_parse_opt(const char *type, const char *value) c_numdev = strtol(value, NULL, 0); else if (!strcasecmp(type, "randomize")) { c_randomize = 1; - srand(time(0)); + srand(time(NULL)); } else if (!strcasecmp(type, "seed") && value) srand(strtol(value, NULL, 0)); else if (!strcasecmp(type, "mtu") && value) diff --git a/src/in_netlink.c b/src/in_netlink.c index 64021f5..4b5e3b1 100644 --- a/src/in_netlink.c +++ b/src/in_netlink.c @@ -37,6 +37,8 @@ static int c_notc = 0; static struct element_group *grp; static struct bmon_module netlink_ops; +#include + #include #include #include @@ -47,6 +49,22 @@ static struct bmon_module netlink_ops; #include #include +/* These counters are not available prior to libnl 3.2.25. Set them to -1 so + * rtnl_link_get_stat() won't be called for them. */ +#if LIBNL_CURRENT < 220 +# define RTNL_LINK_ICMP6_CSUMERRORS -1 +# define RTNL_LINK_IP6_CSUMERRORS -1 +# define RTNL_LINK_IP6_NOECTPKTS -1 +# define RTNL_LINK_IP6_ECT1PKTS -1 +# define RTNL_LINK_IP6_ECT0PKTS -1 +# define RTNL_LINK_IP6_CEPKTS -1 +#endif + +/* Not available prior to libnl 3.2.29 */ +#if LIBNL_CURRENT < 224 +# define RTNL_LINK_RX_NOHANDLER -1 +#endif + static struct attr_map link_attrs[] = { { .name = "bytes", @@ -88,6 +106,14 @@ static struct attr_map link_attrs[] = { .rxid = RTNL_LINK_RX_COMPRESSED, .txid = RTNL_LINK_TX_COMPRESSED, }, +{ + .name = "nohandler", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "No Handler", + .rxid = RTNL_LINK_RX_NOHANDLER, + .txid = -1, +}, { .name = "fifoerr", .type = ATTR_TYPE_COUNTER, @@ -280,6 +306,14 @@ static struct attr_map link_attrs[] = { .rxid = RTNL_LINK_ICMP6_INERRORS, .txid = RTNL_LINK_ICMP6_OUTERRORS, }, +{ + .name = "icmp6csumerr", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "ICMPv6 Checksum Errors", + .rxid = RTNL_LINK_ICMP6_CSUMERRORS, + .txid = -1, +}, { .name = "ip6inhdrerr", .type = ATTR_TYPE_COUNTER, @@ -320,6 +354,14 @@ static struct attr_map link_attrs[] = { .rxid = RTNL_LINK_IP6_INADDRERRORS, .txid = -1, }, +{ + .name = "ip6csumerr", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "Ip6 Checksum Error", + .rxid = RTNL_LINK_IP6_CSUMERRORS, + .txid = -1, +}, { .name = "ip6reasmtimeo", .type = ATTR_TYPE_COUNTER, @@ -351,6 +393,38 @@ static struct attr_map link_attrs[] = { .description = "Ip6 Reasm/Frag Requests", .rxid = RTNL_LINK_IP6_REASMREQDS, .txid = RTNL_LINK_IP6_FRAGCREATES, +}, +{ + .name = "ip6noectpkts", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "Ip6 Non-ECT Packets", + .rxid = RTNL_LINK_IP6_NOECTPKTS, + .txid = -1, +}, +{ + .name = "ip6ect1pkts", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "Ip6 ECT(1) Packets", + .rxid = RTNL_LINK_IP6_ECT1PKTS, + .txid = -1, +}, +{ + .name = "ip6ect0pkts", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "Ip6 ECT(0) Packets", + .rxid = RTNL_LINK_IP6_ECT0PKTS, + .txid = -1, +}, +{ + .name = "ip6cepkts", + .type = ATTR_TYPE_COUNTER, + .unit = UNIT_NUMBER, + .description = "Ip6 CE Packets", + .rxid = RTNL_LINK_IP6_CEPKTS, + .txid = -1, } }; @@ -430,12 +504,13 @@ static struct attr_map tc_attrs[] = { }; struct rdata { + struct nl_cache * class_cache; struct element * parent; int level; }; static struct nl_sock *sock; -static struct nl_cache *link_cache, *qdisc_cache, *class_cache; +static struct nl_cache *link_cache, *qdisc_cache; static void update_tc_attrs(struct element *e, struct rtnl_tc *tc) { @@ -470,10 +545,10 @@ static void update_tc_infos(struct element *e, struct rtnl_tc *tc) 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 void find_qdiscs(int, uint32_t, struct rdata *); static struct element *handle_tc_obj(struct rtnl_tc *tc, const char *prefix, - struct rdata *rdata) + const struct rdata *rdata) { char buf[IFNAME_MAX], name[IFNAME_MAX]; uint32_t id = rtnl_tc_get_handle(tc); @@ -483,11 +558,14 @@ static struct element *handle_tc_obj(struct rtnl_tc *tc, const char *prefix, 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))) + if (!rdata || !rdata->parent) + BUG(); + + if (!(e = element_lookup(grp, name, id, rdata->parent, ELEMENT_CREAT))) return NULL; if (e->e_flags & ELEMENT_FLAG_CREATED) { - e->e_level = rdata ? rdata->level : 0; + e->e_level = rdata->level; if (element_set_key_attr(e, "tc_bytes", "tc_packets") || element_set_usage_attr(e, "tc_bytes")) @@ -518,8 +596,9 @@ 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; + const struct rdata *rdata = arg; struct rdata ndata = { + .class_cache = rdata->class_cache, .level = rdata->level + 1, }; @@ -532,10 +611,10 @@ static void handle_class(struct nl_object *obj, void *arg) 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); + find_qdiscs(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata); } -static void find_qdiscs(uint32_t parent, struct rdata *rdata) +static void find_qdiscs(int ifindex, uint32_t parent, struct rdata *rdata) { struct rtnl_qdisc *filter; @@ -543,6 +622,7 @@ static void find_qdiscs(uint32_t parent, struct rdata *rdata) return; rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); + rtnl_tc_set_ifindex((struct rtnl_tc *) filter, ifindex); nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter), handle_qdisc, rdata); @@ -571,7 +651,7 @@ static void find_classes(uint32_t parent, struct rdata *rdata) rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); - nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), + nl_cache_foreach_filter(rdata->class_cache, OBJ_CAST(filter), handle_class, rdata); rtnl_class_put(filter); @@ -581,8 +661,9 @@ 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; + const struct rdata *rdata = arg; struct rdata ndata = { + .class_cache = rdata->class_cache, .level = rdata->level + 1, }; @@ -604,6 +685,7 @@ static void handle_qdisc(struct nl_object *obj, void *arg) static void handle_tc(struct element *e, struct rtnl_link *link) { struct rtnl_qdisc *qdisc; + struct nl_cache *class_cache; int ifindex = rtnl_link_get_ifindex(link); struct rdata rdata = { .level = 1, @@ -613,6 +695,8 @@ static void handle_tc(struct element *e, struct rtnl_link *link) if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0) return; + rdata.class_cache = class_cache; + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT); if (qdisc) { handle_qdisc(OBJ_CAST(qdisc), &rdata); @@ -736,7 +820,7 @@ static void do_link(struct nl_object *obj, void *arg) attr_update(e, m->attrid, c_rx, c_tx, flags); } - if (!c_notc) + if (!c_notc && qdisc_cache) handle_tc(e, link); element_notify_update(e, NULL); @@ -752,7 +836,8 @@ static void netlink_read(void) goto disable; } - if ((err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) { + if (qdisc_cache && + (err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) { fprintf(stderr, "Unable to resync qdisc cache: %s\n", nl_geterror(err)); goto disable; } @@ -772,9 +857,20 @@ static void netlink_shutdown(void) nl_socket_free(sock); } +static void netlink_use_bit(struct attr_map *map, const int size) +{ + if(cfg_getbool(cfg, "use_bit")) { + for(int i = 0; i < size; ++i) { + if(!strcmp(map[i].description, "Bytes")) { + map[i].description = "Bits"; + } + } + } +} + static int netlink_do_init(void) { - int err, i; + int err; if (!(sock = nl_socket_alloc())) { fprintf(stderr, "Unable to allocate netlink socket\n"); @@ -792,10 +888,13 @@ static int netlink_do_init(void) } if ((err = rtnl_qdisc_alloc_cache(sock, &qdisc_cache)) < 0) { - fprintf(stderr, "Unable to allocate qdisc cache: %s\n", nl_geterror(err)); - goto disable; + fprintf(stderr, "Warning: Unable to allocate qdisc cache: %s\n", nl_geterror(err)); + fprintf(stderr, "Disabling QoS statistics.\n"); + qdisc_cache = NULL; } + netlink_use_bit(link_attrs, ARRAY_SIZE(link_attrs)); + netlink_use_bit(tc_attrs, ARRAY_SIZE(tc_attrs)); if (attr_map_load(link_attrs, ARRAY_SIZE(link_attrs)) || attr_map_load(tc_attrs, ARRAY_SIZE(tc_attrs))) BUG(); @@ -817,17 +916,17 @@ static int netlink_probe(void) 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; } diff --git a/src/in_proc.c b/src/in_proc.c index aead5ce..b14269b 100644 --- a/src/in_proc.c +++ b/src/in_proc.c @@ -101,7 +101,7 @@ static void proc_read(void) { struct element *e; FILE *fd; - char buf[512], *p, *s, *unused; + char buf[512], *p, *s, *unused __unused__; int w; if (!(fd = fopen(c_path, "r"))) diff --git a/src/in_sysctl.c b/src/in_sysctl.c index 6583574..1112e35 100644 --- a/src/in_sysctl.c +++ b/src/in_sysctl.c @@ -163,6 +163,7 @@ sysctl_read(void) struct element *e, *e_parent = NULL; struct if_msghdr *ifm, *nextifm; struct sockaddr_dl *sdl; + char info_buf[64]; ifm = (struct if_msghdr *) next; if (ifm->ifm_type != RTM_IFINFO) @@ -225,6 +226,20 @@ sysctl_read(void) attr_update(e, m->attrid, rx, tx, flags); } + snprintf(info_buf, sizeof(info_buf), "%ju", (uintmax_t)ifm->ifm_data.ifi_mtu); + element_update_info(e, "MTU", info_buf); + + snprintf(info_buf, sizeof(info_buf), "%ju", (uintmax_t)ifm->ifm_data.ifi_metric); + element_update_info(e, "Metric", info_buf); + +#if !(defined(__NetBSD__) || defined(__FreeBSD__)) + snprintf(info_buf, sizeof(info_buf), "%u", ifm->ifm_data.ifi_recvquota); + element_update_info(e, "RX-Quota", info_buf); + + snprintf(info_buf, sizeof(info_buf), "%u", ifm->ifm_data.ifi_xmitquota); + element_update_info(e, "TX-Quota", info_buf); +#endif + element_notify_update(e, NULL); element_lifesign(e, 1); } diff --git a/src/module.c b/src/module.c index b245f72..9c83fe1 100644 --- a/src/module.c +++ b/src/module.c @@ -130,7 +130,6 @@ static void __auto_load(struct bmon_module *m) 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; diff --git a/src/out_ascii.c b/src/out_ascii.c index b3e8d2d..19762ba 100644 --- a/src/out_ascii.c +++ b/src/out_ascii.c @@ -101,10 +101,10 @@ 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, + double rx = unit_value2str(rate_get_total(&a->a_rx_rate), a->a_def->ad_unit, &rx_u, &rxprec); - double tx = unit_value2str(a->a_tx_rate.r_total, + double tx = unit_value2str(rate_get_total(&a->a_tx_rate), a->a_def->ad_unit, &tx_u, &txprec); @@ -212,6 +212,7 @@ static void ascii_draw_group(struct element_group *g, void *arg) static void ascii_draw(void) { group_foreach(ascii_draw_group, NULL); + fflush(stdout); if (c_quit_after > 0) if (--c_quit_after == 0) diff --git a/src/out_curses.c b/src/out_curses.c index cbd19ae..330e8f6 100644 --- a/src/out_curses.c +++ b/src/out_curses.c @@ -45,6 +45,8 @@ enum { KEY_TOGGLE_DETAILS = 'd', KEY_TOGGLE_INFO = 'i', KEY_COLLECT_HISTORY = 'h', + KEY_CTRL_N = 14, + KEY_CTRL_P = 16, }; #define DETAILS_COLS 40 @@ -137,7 +139,7 @@ static void apply_layout(int layout) attrset(cfg_layout[layout].l_attr); } -char *float2str(double value, int width, int prec, char *buf, size_t len) +static char *float2str(double value, int width, int prec, char *buf, size_t len) { snprintf(buf, len, "%'*.*f", width, value == 0.0f ? 0 : prec, value); @@ -147,22 +149,24 @@ char *float2str(double value, int width, int prec, char *buf, size_t len) static void put_line(const char *fmt, ...) { va_list args; - char buf[2048]; - int x, y; + char *buf; + int len; + int x, y __unused__; - memset(buf, 0, sizeof(buf)); getyx(stdscr, y, x); + len = cols - x; + buf = xcalloc(len+1, 1); + va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + vsnprintf(buf, len+1, fmt, args); va_end(args); - if (strlen(buf) > cols-x) - buf[cols - x] = '\0'; - else - memset(&buf[strlen(buf)], ' ', cols - strlen(buf)-x); + if (strlen(buf) < len) + memset(&buf[strlen(buf)], ' ', len - strlen(buf)); addstr(buf); + xfree(buf); } static void center_text(const char *fmt, ...) @@ -241,10 +245,10 @@ static void draw_attr_detail(struct element *e, struct attr *a, void *arg) int rxprec, txprec, ncol; struct detail_arg *da = arg; - double rx = unit_value2str(a->a_rx_rate.r_total, + double rx = unit_value2str(rate_get_total(&a->a_rx_rate), a->a_def->ad_unit, &rx_u, &rxprec); - double tx = unit_value2str(a->a_tx_rate.r_total, + double tx = unit_value2str(rate_get_total(&a->a_tx_rate), a->a_def->ad_unit, &tx_u, &txprec); @@ -258,7 +262,7 @@ static void draw_attr_detail(struct element *e, struct attr *a, void *arg) if (ncol > 0) addch(ACS_VLINE); - put_line(" %-14.14s %8s%-3s %8s%-3s\n", + put_line(" %-14.14s %8s%-3s %8s%-3s", a->a_def->ad_description, (a->a_flags & ATTR_RX_ENABLED) ? float2str(rx, 8, rxprec, buf1, sizeof(buf1)) : "-", rx_u, @@ -392,6 +396,7 @@ static void draw_help(void) 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); + mvaddnstr(y+18, x+3, "r Reset counter of element", -1); attroff(A_STANDOUT); @@ -419,6 +424,7 @@ static void draw_header(void) move(row, COLS - strlen(PACKAGE_STRING) - 1); put_line("%s", PACKAGE_STRING); move(row, 0); + apply_layout(LAYOUT_LIST); } static int lines_required_for_statusbar(void) @@ -430,7 +436,7 @@ static void draw_statusbar(void) { static const char *help_text = "Press ? for help"; char s[27]; - time_t t = time(0); + time_t t = time(NULL); apply_layout(LAYOUT_STATUSBAR); @@ -631,6 +637,7 @@ static void draw_element(struct element_group *g, struct element *e, static void draw_group(struct element_group *g, void *arg) { + apply_layout(LAYOUT_HEADER); int *line = arg; if (line_visible(*line)) { @@ -683,7 +690,7 @@ static void draw_graph_centered(struct graph *g, int row, int ncol, static void draw_table(struct graph *g, struct graph_table *tbl, struct attr *a, struct history *h, - const char *hdr, int ncol) + const char *hdr, int ncol, int layout) { int i, save_row; char buf[32]; @@ -708,11 +715,14 @@ static void draw_table(struct graph *g, struct graph_table *tbl, //move(row, ncol + g->g_cfg.gc_width - 3); //put_line("[err %.2f%%]", rtiming.rt_variance.v_error); + memset(buf, 0, strlen(buf)); 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))); + sprintf(buf, "%'8.2f ", tbl->gt_scale[i]); + addstr(buf); + apply_layout(layout); + put_line("%s", tbl->gt_table + (i * graph_row_size(&g->g_cfg))); + apply_layout(LAYOUT_LIST); } move(++row, ncol); @@ -746,14 +756,14 @@ static void draw_history_graph(struct attr *a, struct history *h) graph_refill(g, h); save_row = row; - draw_table(g, &g->g_rx, a, h, "RX", ncol); + draw_table(g, &g->g_rx, a, h, "RX", ncol, LAYOUT_RX_GRAPH); if (graph_display == GRAPH_DISPLAY_SIDE_BY_SIDE) { ncol = cols / 2; row = save_row; } - draw_table(g, &g->g_tx, a, h, "TX", ncol); + draw_table(g, &g->g_tx, a, h, "TX", ncol, LAYOUT_TX_GRAPH); graph_free(g); } @@ -973,8 +983,10 @@ static void draw_content(void) */ NEXT_ROW(); hline(ACS_HLINE, cols); - mvaddch(row, LIST_COL_1, ACS_BTEE); - mvaddch(row, LIST_COL_2, ACS_BTEE); + if (c_show_list) { + 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 ", @@ -1016,6 +1028,12 @@ static void draw_content(void) NEXT_ROW(); hline(ACS_HLINE, cols); + if (c_show_details) { + int i; + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS) - 1, ACS_BTEE); + } + if (!c_show_info) center_text(" Press %c to enable additional information ", KEY_TOGGLE_INFO); @@ -1079,6 +1097,16 @@ static void curses_draw(void) refresh(); } +static void __reset_attr_counter(struct element *e, struct attr *a, void *arg) +{ + attr_reset_counter(a); +} + +static void reset_counters(void) +{ + element_foreach_attr(current_element, __reset_attr_counter, NULL); +} + static int handle_input(int ch) { switch (ch) @@ -1160,10 +1188,12 @@ static int handle_input(int ch) return 1; case KEY_DOWN: + case KEY_CTRL_N: element_select_next(); return 1; case KEY_UP: + case KEY_CTRL_P: element_select_prev(); return 1; @@ -1198,6 +1228,10 @@ static int handle_input(int ch) case '\t': history_select_next(); return 1; + + case 'r': + reset_counters(); + return 1; } return 0; @@ -1232,7 +1266,7 @@ static void print_module_help(void) " Author: Thomas Graf \n" \ "\n" \ " Options:\n" \ - " fgchar=CHAR Foreground character (default: '*')\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" \ @@ -1242,6 +1276,7 @@ static void print_module_help(void) " nocolors Do not use colors\n" \ " graph Show graphical stats by default\n" \ " details Show detailed stats by default\n" \ + " info Show additional info screen by default\n" \ " minlist=INT Minimum item list length\n"); } @@ -1264,6 +1299,8 @@ static void curses_parse_opt(const char *type, const char *value) c_show_graph = !!c_ngraph; } else if (!strcasecmp(type, "details")) c_show_details = 1; + else if (!strcasecmp(type, "info")) + c_show_info = 1; else if (!strcasecmp(type, "nocolors")) c_use_colors = 0; else if (!strcasecmp(type, "minlist") && value) diff --git a/src/out_format.c b/src/out_format.c index 84a5a2b..263f8c9 100644 --- a/src/out_format.c +++ b/src/out_format.c @@ -119,24 +119,22 @@ static char *get_token(struct element_group *g, struct element *e, goto out; } - if (!(a = attr_lookup(e, def->ad_id))) { - fprintf(stderr, "Unable to find attribute %u (%s)\n", - def->ad_id, name); + if (!(a = attr_lookup(e, def->ad_id))) goto out; - } if (!strncasecmp(type, "rx:", 3)) { - snprintf(buf, len, "%" PRIu64, a->a_rx_rate.r_total); + snprintf(buf, len, "%" PRIu64, rate_get_total(&a->a_rx_rate)); return buf; } else if (!strncasecmp(type, "tx:", 3)) { - snprintf(buf, len, "%" PRIu64, a->a_tx_rate.r_total); + snprintf(buf, len, "%" PRIu64, rate_get_total(&a->a_tx_rate)); 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)) + } else if (!strncasecmp(type, "txrate:", 7)) { snprintf(buf, len, "%.2f", a->a_tx_rate.r_rate); return buf; + } } fprintf(stderr, "Unknown field \"%s\"\n", token); @@ -168,6 +166,7 @@ static void draw_element(struct element_group *g, struct element *e, void *arg) static void format_draw(void) { group_foreach_recursive(draw_element, NULL); + fflush(stdout); if (c_quit_after > 0) if (--c_quit_after == 0) @@ -189,8 +188,8 @@ static inline void add_token(int type, char *data) 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++; @@ -250,7 +249,7 @@ static int format_probe(void) } goto out; - + finish_escape: *p = '\0'; add_token(OT_STRING, s); @@ -259,7 +258,7 @@ static int format_probe(void) continue; } -out: +out: if (new_one) { add_token(OT_STRING, p); new_one = 0; @@ -322,15 +321,15 @@ static void print_help(void) " 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" \ + " '$(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" \ + " '$(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: $(element:name)\\nBytes Rate: $(attr:rxrate:bytes)/" \ + "$(attr:txrate:bytes)\\nPackets Rate: $(attr:rxrate:packets)/" \ + "$(attr:txrate:packets)\\n'\n" \ " Item: eth0\n" \ " Bytes Rate: 49130/2119\n" \ " Packets Rate: 40/11\n" \ diff --git a/src/unit.c b/src/unit.c index 38fc468..81f1f63 100644 --- a/src/unit.c +++ b/src/unit.c @@ -28,7 +28,7 @@ #include #include -static struct unit *byte_unit, *bit_unit, *number_unit; +static struct unit *byte_unit, *bit_unit; static LIST_HEAD(units); diff --git a/src/utils.c b/src/utils.c index 248d829..7db96e7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,6 +27,11 @@ #include #include +#ifdef __MACH__ +#include +#include +#endif + void *xcalloc(size_t n, size_t s) { void *d = calloc(n, s); @@ -112,12 +117,21 @@ int timestamp_is_negative(timestamp_t *ts) void update_timestamp(timestamp_t *dst) { - struct timeval tv; +#ifdef __MACH__ + clock_serv_t cclock; + mach_timespec_t tp; - gettimeofday(&tv, NULL); + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &tp); + mach_port_deallocate(mach_task_self(), cclock); +#else + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); +#endif - dst->tv_sec = tv.tv_sec; - dst->tv_usec = tv.tv_usec; + dst->tv_sec = tp.tv_sec; + dst->tv_usec = tp.tv_nsec / 1000; } void copy_timestamp(timestamp_t *ts1, timestamp_t *ts2)