Initial import

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

21
.gitignore vendored
View File

@ -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.*

0
ChangeLog Normal file
View File

11
Makefile.am Normal file
View File

@ -0,0 +1,11 @@
# -*- Makefile -*-
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src man
pkgsysconfdir = $(sysconfdir)
pkgsysconf_DATA = etc/bmon.conf
EXTRA_DIST = \
$(pkgsysconf_DATA)

View File

@ -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.

4
autogen.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
autoreconf -fi;
rm -Rf autom4te.cache;

191
configure.ac Normal file
View File

@ -0,0 +1,191 @@
#
# configure.in Configure Script
#
# Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
#
# 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

81
etc/bmon.conf Normal file
View File

@ -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 }
* }
*/

2
include/bmon/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
defs.h*
stamp-h1

138
include/bmon/attr.h Normal file
View File

@ -0,0 +1,138 @@
/*
* bmon/attr.h Attributes
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_ATTR_H_
#define __BMON_ATTR_H_
#include <bmon/bmon.h>
#include <bmon/unit.h>
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

82
include/bmon/bmon.h Normal file
View File

@ -0,0 +1,82 @@
/*
* bmon.h All-include Header
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_BMON_H_
#define __BMON_BMON_H_
#include <bmon/config.h>
#include <bmon/list.h>
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

View File

@ -0,0 +1,35 @@
/*
* compile_fixes.h
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_COMPILE_FIXES_H_
#define __BMON_COMPILE_FIXES_H_
#include <bmon/defs.h>
#ifndef HAVE_SUSECONDS_T
typedef long suseconds_t;
#endif
#endif

91
include/bmon/conf.h Normal file
View File

@ -0,0 +1,91 @@
/*
* conf.h Config Crap
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_CONF_H_
#define __BMON_CONF_H_
#include <bmon/bmon.h>
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

158
include/bmon/config.h Normal file
View File

@ -0,0 +1,158 @@
/*
* config.h Global Config
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_CONFIG_H_
#define __BMON_CONFIG_H_
#include <bmon/defs.h>
#include <bmon/compile-fixes.h>
#if STDC_HEADERS != 1
#error "*** ERROR: ANSI C headers required for compilation ***"
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <signal.h>
#include <limits.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
#include <sys/file.h>
#include <assert.h>
#include <syslog.h>
#include <sys/wait.h>
#include <dirent.h>
#include <values.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
#if !HAVE_WORKING_VFORK
#define vfork fork
#endif
#if defined HAVE_STRING_H
#include <string.h>
#elif defined HAVE_STRINGS_H
#include <strings.h>
#else
#error "*** ERROR: No string header file found ***"
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_INITTYPES_H
#include <inittypes.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if defined HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <sys/stat.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#if defined HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h>
#elif defined HAVE_NCURSESW_H
# include <ncursesw.h>
#elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h>
#elif defined HAVE_NCURSES_H
# include <ncurses.h>
#elif defined HAVE_CURSES_H
# include <curses.h>
#else
# error "SysV or X/Open-compatible Curses header file required"
#endif
#include <netinet/in.h>
#if defined HAVE_NETINET6_IN6_H
#include <netinet6/in6.h>
#endif
#include <sys/socket.h>
#include <arpa/inet.h>
#include <confuse.h>
#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

246
include/bmon/defs.h.in Normal file
View File

@ -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 <curses.h> 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 <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the <fcntl.h> 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 <getopt.h> 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 <inttypes.h> 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 <memory.h> 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 <ncursesw/curses.h> is present */
#undef HAVE_NCURSESW_CURSES_H
/* Define to 1 if <ncursesw.h> is present */
#undef HAVE_NCURSESW_H
/* Define to 1 if <ncurses/curses.h> is present */
#undef HAVE_NCURSES_CURSES_H
/* Define to 1 if <ncurses.h> is present */
#undef HAVE_NCURSES_H
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet6/in6.h> header file. */
#undef HAVE_NETINET6_IN6_H
/* Define to 1 if you have the <netinet/in.h> 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 <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> 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 <sysctl/ioctl.h> header file. */
#undef HAVE_SYSCTL_IOCTL_H
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_NDIR_H
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
#undef HAVE_SYS_SOCKIO_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/utsname.h> 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 <unistd.h> 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 <vfork.h> 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 <sys/time.h> and <time.h>. */
#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 <sys/types.h> does not define. */
#undef pid_t
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define as `fork' if `vfork' does not work. */
#undef vfork

130
include/bmon/element.h Normal file
View File

@ -0,0 +1,130 @@
/*
* bmon/element.h Elements
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_ELEMENT_H_
#define __BMON_ELEMENT_H_
#include <bmon/bmon.h>
#include <bmon/group.h>
#include <bmon/attr.h>
#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

View File

@ -0,0 +1,53 @@
/*
* element_cfg.h Element Configuration
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_ELEMENT_CFG_H_
#define __BMON_ELEMENT_CFG_H_
#include <bmon/bmon.h>
#include <bmon/conf.h>
#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

72
include/bmon/graph.h Normal file
View File

@ -0,0 +1,72 @@
/*
* bmon/graph.h Graph creation utility
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_GRAPH_H_
#define __BMON_GRAPH_H_
#include <bmon/bmon.h>
#include <bmon/unit.h>
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

100
include/bmon/group.h Normal file
View File

@ -0,0 +1,100 @@
/*
* bmon/group.h Group Management
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_GROUP_H_
#define __BMON_GROUP_H_
#include <bmon/bmon.h>
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

93
include/bmon/history.h Normal file
View File

@ -0,0 +1,93 @@
/*
* bmon/history.h History Management
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_HISTORY_H_
#define __BMON_HISTORY_H_
#include <bmon/bmon.h>
#include <bmon/attr.h>
#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

42
include/bmon/info.h Normal file
View File

@ -0,0 +1,42 @@
/*
* bmon/info.h Info Attributes
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_INFO_H_
#define __BMON_INFO_H_
#include <bmon/bmon.h>
#include <bmon/unit.h>
struct element;
struct info
{
char * i_name;
char * i_value;
struct list_head i_list;
};
#endif

52
include/bmon/input.h Normal file
View File

@ -0,0 +1,52 @@
/*
* input.h Input API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_INPUT_H_
#define __BMON_INPUT_H_
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/module.h>
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

93
include/bmon/list.h Normal file
View File

@ -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

75
include/bmon/module.h Normal file
View File

@ -0,0 +1,75 @@
/*
* module.h Module API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* 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 <bmon/bmon.h>
#include <bmon/conf.h>
#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

39
include/bmon/output.h Normal file
View File

@ -0,0 +1,39 @@
/*
* output.h Output API
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_OUTPUT_H_
#define __BMON_OUTPUT_H_
#include <bmon/bmon.h>
#include <bmon/conf.h>
#include <bmon/module.h>
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

68
include/bmon/unit.h Normal file
View File

@ -0,0 +1,68 @@
/*
* bmon/unit.h Units
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_UNIT_H_
#define __BMON_UNIT_H_
#include <bmon/bmon.h>
#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

102
include/bmon/utils.h Normal file
View File

@ -0,0 +1,102 @@
/*
* utils.h General purpose utilities
*
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2013 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __BMON_UTILS_H_
#define __BMON_UTILS_H_
#include <bmon/bmon.h>
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

518
m4/ax_with_curses.m4 Normal file
View File

@ -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 <curses.h> is present and should be used
# HAVE_NCURSESW_H - if <ncursesw.h> should be used
# HAVE_NCURSES_H - if <ncurses.h> should be used
# HAVE_NCURSESW_CURSES_H - if <ncursesw/curses.h> should be used
# HAVE_NCURSES_CURSES_H - if <ncurses/curses.h> 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 <curses.h>. 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 <ncursesw/curses.h>
# #elif defined HAVE_NCURSESW_H
# # include <ncursesw.h>
# #elif defined HAVE_NCURSES_CURSES_H
# # include <ncurses/curses.h>
# #elif defined HAVE_NCURSES_H
# # include <ncurses.h>
# #elif defined HAVE_CURSES_H
# # include <curses.h>
# #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 <mark@kyne.com.au>
# Copyright (c) 2009 Damian Pietras <daper@daper.net>
# Copyright (c) 2012 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2011 John Zaitseff <J.Zaitseff@zap.org.au>
#
# 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 <http://www.gnu.org/licenses/>.
#
# 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 <ncursesw/curses.h>
]], [[
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 <ncursesw/curses.h> 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 <ncursesw.h>
]], [[
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 <ncursesw.h> 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 <ncurses.h>
]], [[
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 <ncurses.h> 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 <ncurses/curses.h>
]], [[
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 <ncurses/curses.h> is present])
])
AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@include <ncurses.h>
]], [[
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 <ncurses.h> 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 <curses.h>
]], [[
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 <curses.h> 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 <curses.h>
@%:@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 <curses.h>
]], [[
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 <curses.h>
]], [[
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

3
man/Makefile.am Normal file
View File

@ -0,0 +1,3 @@
# -*- Makefile -*-
dist_man8_MANS = bmon.8

235
man/bmon.8 Normal file
View File

@ -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 <tgraf@suug.ch>

2
src/.gitignore vendored Normal file
View File

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

42
src/Makefile.am Normal file
View File

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

635
src/attr.c Normal file
View File

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

397
src/bmon.c Normal file
View File

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

596
src/conf.c Normal file
View File

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

537
src/element.c Normal file
View File

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

96
src/element_cfg.c Normal file
View File

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

235
src/graph.c Normal file
View File

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

278
src/group.c Normal file
View File

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

318
src/history.c Normal file
View File

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

243
src/in_dummy.c Normal file
View File

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

868
src/in_netlink.c Normal file
View File

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

62
src/in_null.c Normal file
View File

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

234
src/in_proc.c Normal file
View File

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

83
src/input.c Normal file
View File

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

195
src/module.c Normal file
View File

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

299
src/out_ascii.c Normal file
View File

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

1277
src/out_curses.c Normal file

File diff suppressed because it is too large Load Diff

373
src/out_format.c Normal file
View File

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

56
src/out_null.c Normal file
View File

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

95
src/output.c Normal file
View File

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

216
src/unit.c Normal file
View File

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

421
src/utils.c Normal file
View File

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