From: Florian Forster Date: Sat, 8 Nov 2008 08:00:00 +0000 (+0100) Subject: Merge branch 'collectd-4.5' X-Git-Tag: collectd-4.6.0~163 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=576d966de9a98aacd1a2740abb349022cc32d913;hp=e628f39838a67b40d52dfb8425b4d8474fbd0550 Merge branch 'collectd-4.5' --- diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..9bb4399e --- /dev/null +++ b/.mailmap @@ -0,0 +1,8 @@ +Anthony +Florian Forster +Florian Forster +Luboš Staněk +Luboš Staněk +Niki W. Waibel +Sebastian Harl + diff --git a/README b/README index e698786a..5c9eed03 100644 --- a/README +++ b/README @@ -40,6 +40,10 @@ Features - cpufreq CPU frequency (For laptops with speed step or a similar technology) + - dbi + Executes SQL statements on various databases and interprets the returned + data. + - df Mountpoint usage (Basically the values `df(1)' delivers) @@ -76,6 +80,9 @@ Features Iptables' counters: Number of bytes that were matched by a certain iptables rule. + - ipmi + IPMI (Intelligent Platform Management Interface) sensors information. + - ipvs IPVS connection statistics (number of connections, octets and packets for each service and destination). @@ -152,9 +159,15 @@ Features PostgreSQL database statistics: active server connections, transaction numbers, block IO, table row manipulations. + - powerdns + PowerDNS name server statistics. + - processes Process counts: Number of running, sleeping, zombie, ... processes. + - rrdcached + RRDtool caching daemon (RRDcacheD) statistics. + - sensors System sensors, accessed using lm_sensors: Voltages, temperatures and fan rotation speeds. @@ -180,6 +193,12 @@ Features - tcpconns Number of TCP connections to specific local and remote ports. + - teamspeak2 + TeamSpeak2 server statistics. + + - thermal + Linux ACPI thermal zone information. + - users Users currently logged in. @@ -214,6 +233,11 @@ Features you can easily do weird stuff with the plugins we didn't dare think of ;) See collectd-perl(5). + - rrdcached + Output to round-robin-database (RRD) files using the RRDtool caching + daemon (RRDcacheD) - see rrdcached(1). That daemon provides a general + implementation of the caching done by the `rrdtool' plugin. + - rrdtool Output to round-robin-database (RRD) files using librrd. See rrdtool(1). This is likely the most popular destination for such values. Since @@ -226,6 +250,13 @@ Features needed. Please read collectd-unixsock(5) for a description on how that's done. + * Filtering and rewriting values dispatched to collectd can be done by the + following plugins: + + - filter_pcre + Filter and rewrite value lists based on Perl-compatible regular + expressions. + * Logging is, as everything in collectd, provided by plugins. The following plugins keep up informed about what's going on: @@ -279,7 +310,7 @@ Features since collectd is programmed multithreaded it benefits from hyperthreading and multicore processors and makes sure that the daemon isn't idle if only one plugins waits for an IO-operation to complete. - + * Once set up, hardly any maintenance is necessary. Setup is kept as easy as possible and the default values should be okay for most users. @@ -339,76 +370,105 @@ Prerequisites * CoreFoundation.framework and IOKit.framework (optional) For compiling on Darwin in general and the `apple_sensors' plugin in particular. + * libcurl (optional) If you want to use the `apache', `ascent', or `nginx' plugin. + + + * libdbi (optional) + Used by the `dbi' plugin to connect to various databases. + * libesmtp (optional) For the `notify_email' plugin. + * libhal (optional) If present, the uuid plugin will check for UUID from HAL. + - * libiptc (optional) + * libiptc (optional, if not found a version shipped with this distribution + can be used if the Linux kernel headers are available) For querying iptables counters. + * libmysqlclient (optional) Unsurprisingly used by the `mysql' plugin. + * libnetlink (optional) Used, obviously, for the `netlink' plugin. + * libnetsnmp (optional) For the `snmp' plugin. + * libnotify (optional) For the `notify_desktop' plugin. + * liboping (optional, if not found a version shipped with this distribution can be used) Used by the `ping' plugin to send and receive ICMP packets. + * libowcapi (optional) Used by the `onewire' plugin to read values from onewire sensors (or the owserver(1) daemon). + * libpcap (optional) Used to capture packets by the `dns' plugin. + + + * libpcre (optional) + Used by the `filter_pcre' plugin. + * libperl (optional) Obviously used by the `perl' plugin. The library has to be compiled with ithread support (introduced in Perl 5.6.0). + * libpq (optional) The PostgreSQL C client library used by the `postgresql' plugin. + - * librrd (optional; headers and library; rrdtool 1.0 and 1.2 both work fine) - If built without `librrd' the resulting binary will be `client only', i.e. - will send its values via multicast and not create any RRD files itself. - Alternatively you can chose to write CSV-files (Comma Separated Values) - instead. + * librrd (optional) + Used by the `rrdtool' and `rrdcached' plugins. The latter requires RRDtool + client support which was added after version 1.3 of RRDtool. Versions 1.0, + 1.2 and 1.3 are known to work with the `rrdtool' plugin. + * librt, libsocket, libkstat, libdevinfo (optional) Various standard Solaris libraries which provide system functions. + * libsensors (optional) To read from `lm_sensors', see the `sensors' plugin. + - * libstatgrab (optional) may be used to collect statistics on systems other - than Linux and/or Solaris. Note that CPU- and disk-statistics, while being - provided by this library, are not supported in collectd right now.. - + * libstatgrab (optional) + Used by various plugins to collect statistics on systems other than Linux + and/or Solaris. + * libupsclient/nut (optional) For the `nut' plugin which queries nut's `upsd'. + * libvirt (optional) Collect statistics from virtual machines. + * libxml2 (optional) Parse XML data. This is needed for the `ascent' and `libvirt' plugins. + * libxmms (optional) + Configuring / Compiling / Installing @@ -418,7 +478,7 @@ Configuring / Compiling / Installing `./configure && make && make install'. For detailed, generic instructions see INSTALL. For a complete list of configure options and their description, run `./configure --help'. - + By default, the configure script will check for all build dependencies and disable all plugins whose requirements cannot be fulfilled (any other plugin will be enabled). To enable a plugin, install missing dependencies (see diff --git a/TODO b/TODO index b5cd6b50..3c8f49eb 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,6 @@ -* Parse options/identifiers with spaces in them correctly. -* Figure out what to do with the onewire plugin. +* Finalize the onewire plugin. * Custom notification messages? +* Implement moving-average calculation for the threshold stuff. src/battery.c: commend not working code. @@ -10,6 +10,7 @@ Wishlist: - be free of syntax errors. * Port nfs module to solaris * Port tape module to Linux +* Port the apple_sensors plugin to Linux/PPC. * Maybe look into porting the serial module * Build Darwin package * Maybe let the network plugin configure whether or not notifications should be diff --git a/configure.in b/configure.in index 0aa87ff4..21af0b54 100644 --- a/configure.in +++ b/configure.in @@ -922,102 +922,6 @@ fi m4_divert_once([HELP_WITH], [ collectd additional packages:]) -# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given]) -librrd_cflags="" -librrd_ldflags="" -librrd_threadsafe="yes" -AC_ARG_WITH(rrdtool, [AS_HELP_STRING([--with-rrdtool@<:@=PREFIX@:>@], [Path to rrdtool.])], -[ if test "x$withval" != "xno" && test "x$withval" != "xyes" - then - librrd_cflags="-I$withval/include" - librrd_ldflags="-L$withval/lib" - with_rrdtool="yes" - else - with_rrdtool="$withval" - fi -], [with_rrdtool="yes"]) -if test "x$with_rrdtool" = "xyes" -then - SAVE_CPPFLAGS="$CPPFLAGS" - SAVE_LDFLAGS="$LDFLAGS" - - CPPFLAGS="$CPPFLAGS $librrd_cflags" - LDFLAGS="$LDFLAGS $librrd_ldflags" - - AC_CHECK_HEADERS(rrd.h,, [with_rrdtool="no (rrd.h not found)"]) - - CPPFLAGS="$SAVE_CPPFLAGS" - LDFLAGS="$SAVE_LDFLAGS" -fi -if test "x$with_rrdtool" = "xyes" -then - SAVE_CPPFLAGS="$CPPFLAGS" - SAVE_LDFLAGS="$LDFLAGS" - - CPPFLAGS="$CPPFLAGS $librrd_cflags" - LDFLAGS="$LDFLAGS $librrd_ldflags" - - AC_CHECK_LIB(rrd_th, rrd_update_r, - [with_rrdtool="yes" - librrd_ldflags="$librrd_ldflags -lrrd_th -lm" - ], - [librrd_threadsafe="no" - AC_CHECK_LIB(rrd, rrd_update, - [with_rrdtool="yes" - librrd_ldflags="$librrd_ldflags -lrrd -lm" - ], - [with_rrdtool="no (symbol 'rrd_update' not found)"], - [-lm]) - ], - [-lm]) - - CPPFLAGS="$SAVE_CPPFLAGS" - LDFLAGS="$SAVE_LDFLAGS" -fi -if test "x$with_rrdtool" = "xyes" -then - BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags" - BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags" - AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS) - AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS) -fi -if test "x$librrd_threadsafe" = "xyes" -then - AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).]) -fi - -AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])], -[ if test "x$withval" != "xno" \ - && test "x$withval" != "xyes" - then - LDFLAGS="$LDFLAGS -L$withval/lib" - CPPFLAGS="$CPPFLAGS -I$withval/include" - with_libpthread="yes" - else - if test "x$withval" = "xno" - then - with_libpthread="no (disabled)" - fi - fi -], [with_libpthread="yes"]) -if test "x$with_libpthread" = "xyes" -then - AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], []) -fi -if test "x$with_libpthread" = "xyes" -then - AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"]) -fi -if test "x$with_libpthread" = "xyes" -then - collect_pthread=1 -else - collect_pthread=0 -fi -AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread], - [Wether or not to use pthread (POSIX threads) library]) -AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes") - if test "x$ac_system" = "xSolaris" then with_kstat="yes" @@ -1044,7 +948,45 @@ fi AM_CONDITIONAL(BUILD_WITH_LIBKSTAT, test "x$with_kstat" = "xyes") AM_CONDITIONAL(BUILD_WITH_LIBDEVINFO, test "x$with_devinfo" = "xyes") -### BEGIN of check for libcurl ### +with_libiokit="no" +AC_CHECK_LIB(IOKit, IOServiceGetMatchingServices, +[ + with_libiokit="yes" +], +[ + with_libiokit="no" +]) +AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes") + +with_libkvm="no" +AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"]) +if test "x$with_kvm_getprocs" = "xyes" +then + AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1, + [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)]) + with_libkvm="yes" +fi +AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes") + +AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"]) +if test "x$with_kvm_getswapinfo" = "xyes" +then + AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1, + [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)]) + with_libkvm="yes" +fi +AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes") + +AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"]) +if test "x$with_kvm_nlist" = "xyes" +then + AC_DEFINE(HAVE_LIBKVM_NLIST, 1, + [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)]) + with_libkvm="yes" +fi +AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes") + +# --with-libcurl {{{ with_curl_config="curl-config" with_curl_cflags="" with_curl_libs="" @@ -1112,221 +1054,166 @@ then AC_SUBST(BUILD_WITH_LIBCURL_LIBS) fi AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes") -### END of check for libcurl ### - -with_libiokit="no" -AC_CHECK_LIB(IOKit, IOServiceGetMatchingServices, -[ - with_libiokit="yes" -], -[ - with_libiokit="no" -]) -AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes") +# }}} -with_libstatgrab_cflags="" -with_libstatgrab_ldflags="" -AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])], +# --with-libdbi {{{ +with_libdbi_cppflags="" +with_libdbi_ldflags="" +AC_ARG_WITH(libdbi, [AS_HELP_STRING([--with-libdbi@<:@=PREFIX@:>@], [Path to libdbi.])], [ - if test "x$withval" != "xno" \ - && test "x$withval" != "xyes" + if test "x$withval" != "xno" && test "x$withval" != "xyes" then - with_libstatgrab_cflags="-I$withval/include" - with_libstatgrab_ldflags="-L$withval/lib" - with_libstatgrab="yes" + with_libdbi_cppflags="-I$withval/include" + with_libdbi_ldflags="-L$withval/lib" + with_libdbi="yes" else - with_libstatgrab="$withval" + with_libdbi="$withval" fi ], [ - if test "x$ac_system" = "xunknown" - then - with_libstatgrab="yes" - else - with_libstatgrab="no" - fi + with_libdbi="yes" ]) -with_libstatgrab_pkg_config="yes" -if test "x$with_libstatgrab" = "xyes" \ - && test "x$PKG_CONFIG" != "x" +if test "x$with_libdbi" = "xyes" then - AC_MSG_CHECKING([pkg-config for libstatgrab]) - temp_result="found" - $PKG_CONFIG --exists libstatgrab 2>/dev/null - if test "$?" != "0" - then - with_libstatgrab_pkg_config="no" - temp_result="not found" - fi - AC_MSG_RESULT([$temp_result]) -else - AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.]) - with_libstatgrab_pkg_config="no" - with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab" -fi + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags" -if test "x$with_libstatgrab" = "xyes" \ - && test "x$with_libstatgrab_pkg_config" = "xyes" \ - && test "x$with_libstatgrab_cflags" = "x" -then - AC_MSG_CHECKING([for libstatgrab CFLAGS]) - temp_result="`$PKG_CONFIG --cflags libstatgrab`" - if test "$?" = "0" - then - with_libstatgrab_cflags="$temp_result" - else - with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)" - temp_result="$PKG_CONFIG --cflags libstatgrab failed" - fi - AC_MSG_RESULT([$temp_result]) -fi + AC_CHECK_HEADERS(dbi/dbi.h, [with_libdbi="yes"], [with_libdbi="no (dbi/dbi.h not found)"]) -if test "x$with_libstatgrab" = "xyes" \ - && test "x$with_libstatgrab_pkg_config" = "xyes" \ - && test "x$with_libstatgrab_ldflags" = "x" -then - AC_MSG_CHECKING([for libstatgrab LDFLAGS]) - temp_result="`$PKG_CONFIG --libs libstatgrab`" - if test "$?" = "0" - then - with_libstatgrab_ldflags="$temp_result" - else - with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)" - temp_result="$PKG_CONFIG --libs libstatgrab failed" - fi - AC_MSG_RESULT([$temp_result]) + CPPFLAGS="$SAVE_CPPFLAGS" fi - -if test "x$with_libstatgrab" = "xyes" +if test "x$with_libdbi" = "xyes" then - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags" + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags" + LDFLAGS="$LDFLAGS $with_libdbi_ldflags" - AC_CHECK_HEADERS(statgrab.h, - [with_libstatgrab="yes"], - [with_libstatgrab="no (statgrab.h not found)"]) + AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"]) - CPPFLAGS="$SAVE_CPPFLAGS" + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" fi - -if test "x$with_libstatgrab" = "xyes" +if test "x$with_libdbi" = "xyes" then - SAVE_CFLAGS="$CFLAGS" - SAVE_LDFLAGS="$LDFLAGS" - - CFLAGS="$CFLAGS $with_libstatgrab_cflags" - LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags" - - AC_CHECK_LIB(statgrab, sg_init, - [with_libstatgrab="yes"], - [with_libstatgrab="no (symbol sg_init not found)"]) - - CFLAGS="$SAVE_CFLAGS" - LDFLAGS="$SAVE_LDFLAGS" + BUILD_WITH_LIBDBI_CPPFLAGS="$with_libdbi_cppflags" + BUILD_WITH_LIBDBI_LDFLAGS="$with_libdbi_ldflags" + BUILD_WITH_LIBDBI_LIBS="-ldbi" + AC_SUBST(BUILD_WITH_LIBDBI_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBDBI_LDFLAGS) + AC_SUBST(BUILD_WITH_LIBDBI_LIBS) fi +AM_CONDITIONAL(BUILD_WITH_LIBDBI, test "x$with_libdbi" = "xyes") +# }}} -AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes") -if test "x$with_libstatgrab" = "xyes" +# --with-libesmtp {{{ +AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + LDFLAGS="$LDFLAGS -L$withval/lib" + CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE" + with_libesmtp="yes" + else + with_libesmtp="$withval" + fi +], +[ + with_libesmtp="yes" +]) +if test "x$with_libesmtp" = "xyes" then - AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)]) - BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags" - BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags" - AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS) - AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS) + AC_CHECK_LIB(esmtp, smtp_create_session, + [ + AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).]) + ], [with_libesmtp="no (libesmtp not found)"]) fi - -with_libkvm="no" -AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"]) -if test "x$with_kvm_getprocs" = "xyes" +if test "x$with_libesmtp" = "xyes" then - AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1, - [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)]) - with_libkvm="yes" -fi -AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes") - -AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"]) -if test "x$with_kvm_getswapinfo" = "xyes" -then - AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1, - [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)]) - with_libkvm="yes" + AC_CHECK_HEADERS(libesmtp.h, + [ + AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the header file.]) + ], [with_libesmtp="no (libesmtp.h not found)"]) fi -AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes") - -AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"]) -if test "x$with_kvm_nlist" = "xyes" +if test "x$with_libesmtp" = "xyes" then - AC_DEFINE(HAVE_LIBKVM_NLIST, 1, - [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)]) - with_libkvm="yes" + collect_libesmtp=1 +else + collect_libesmtp=0 fi -AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes") +AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp], + [Wether or not to use the esmtp library]) +AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes") +# }}} -with_sensors_cflags="" -with_sensors_ldflags="" -AC_ARG_WITH(lm-sensors, [AS_HELP_STRING([--with-lm-sensors@<:@=PREFIX@:>@], [Path to lm_sensors.])], +# --with-libiptc {{{ +with_own_libiptc="no" +AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])], [ - if test "x$withval" = "xno" + if test "x$withval" != "xno" && test "x$withval" != "xyes" then - with_lm_sensors="no" + LDFLAGS="$LDFLAGS -L$withval/lib" + CPPFLAGS="$CPPFLAGS -I$withval/include" + with_libiptc="yes" else - with_lm_sensors="yes" - if test "x$withval" != "xyes" - then - with_sensors_cflags="-I$withval/include" - with_sensors_ldflags="-L$withval/lib" - with_lm_sensors="yes" - fi + with_libiptc="$withval" fi ], [ if test "x$ac_system" = "xLinux" then - with_lm_sensors="yes" + with_libiptc="yes" else - with_lm_sensors="no (Linux only library)" + with_libiptc="no (Linux only)" fi ]) -if test "x$with_lm_sensors" = "xyes" +if test "x$with_libiptc" = "xyes" then - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_sensors_cflags" - -# AC_CHECK_HEADERS(sensors/sensors.h, -# [ -# AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the header file.]) -# ], -# [with_lm_sensors="no (sensors/sensors.h not found)"]) - AC_CHECK_HEADERS(sensors/sensors.h, [], [with_lm_sensors="no (sensors/sensors.h not found)"]) - - CPPFLAGS="$SAVE_CPPFLAGS" + AC_CHECK_LIB(iptc, iptc_init, + [ + AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).]) + ], + [ + with_libiptc="yes" + with_own_libiptc="yes" + ]) fi -if test "x$with_lm_sensors" = "xyes" +if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" != "xyes" then - SAVE_CPPFLAGS="$CPPFLAGS" - SAVE_LDFLAGS="$LDFLAGS" - CPPFLAGS="$CPPFLAGS $with_sensors_cflags" - LDFLAGS="$LDFLAGS $with_sensors_ldflags" + AC_CHECK_HEADERS(libiptc/libiptc.h, + [ + AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the header file.]) + ], + [ + with_libiptc="yes" + with_own_libiptc="yes" + ]) +fi +if test "x$with_libiptc" = "xyes" +then + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $KERNEL_CFLAGS" - AC_CHECK_LIB(sensors, sensors_init, + AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [], [ - AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).]) + with_libiptc="no (Linux iptables headers not found - check KERNEL_DIR)" + with_own_libiptc="no" ], - [with_lm_sensors="no (libsensors not found)"]) + [ +#include "$srcdir/src/libiptc/ipt_kernel_headers.h" + ]) - CPPFLAGS="$SAVE_CPPFLAGS" - LDFLAGS="$SAVE_LDFLAGS" + CFLAGS=$SAVE_CFLAGS fi -if test "x$with_lm_sensors" = "xyes" +AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes") +AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_own_libiptc" = "xyes") +if test "x$with_own_libiptc" = "xyes" then - BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags" - BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags" - AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS) - AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS) + AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.]) fi -AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_lm_sensors" = "xyes") +# }}} +# --with-libmysql {{{ with_mysql_config="mysql_config" with_mysql_cflags="" with_mysql_libs="" @@ -1404,75 +1291,250 @@ then AC_SUBST(BUILD_WITH_LIBMYSQL_LIBS) fi AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes") +# }}} -with_own_liboconfig="no" -liboconfig_LDFLAGS="$LDFLAGS" -liboconfig_CPPFLAGS="$CPPFLAGS" -AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])], +# --with-libnetlink {{{ +with_libnetlink_cflags="" +with_libnetlink_libs="-lnetlink" +AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])], [ - if test "x$withval" != "xno" && test "x$withval" != "xyes" - then - if test -d "$withval/lib" - then - liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib" - fi - if test -d "$withval/include" - then - liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include" - fi - fi - if test "x$withval" = "xno" - then - AC_MSG_ERROR("liboconfig is required") - fi + echo "libnetlink: withval = $withval" + if test "x$withval" = "xyes" + then + with_libnetlink="yes" + else if test "x$withval" = "xno" + then + with_libnetlink="no" + else + if test -d "$withval/include" + then + with_libnetlink_cflags="-I$withval/include" + with_libnetlink_libs="-L$withval/lib -lnetlink" + with_libnetlink="yes" + else + AC_MSG_ERROR("no such directory: $withval/include") + fi + fi; fi ], [ - with_liboconfig="yes" + if test "x$ac_system" = "xLinux" + then + with_libnetlink="yes" + else + with_libnetlink="no (Linux only library)" + fi ]) +if test "x$with_libnetlink" = "xyes" +then + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $with_libnetlink_cflags" -save_LDFLAGS="$LDFLAGS" -save_CPPFLAGS="$CPPFLAGS" -LDFLAGS="$liboconfig_LDFLAGS" -CPPFLAGS="$liboconfig_CPPFLAGS" -AC_CHECK_LIB(oconfig, oconfig_parse_fh, -[ - with_liboconfig="yes" - with_own_liboconfig="no" -], -[ - with_liboconfig="yes" - with_own_liboconfig="yes" - LDFLAGS="$save_LDFLAGS" - CPPFLAGS="$save_CPPFLAGS" -]) + with_libnetlink="no (libnetlink.h not found)" -AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes") -if test "x$with_own_liboconfig" = "xyes" + AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h, + [ + with_libnetlink="yes" + break + ], [], +[#include +#include +#include +#include +#include +#include ]) + AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [], +[#include +#include +#include +#include ]) + + AC_COMPILE_IFELSE( +[#include +#include +#include +#include +#include +#include + +int main (void) +{ + int retval = TCA_STATS2; + return (retval); +}], + [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])] + []); + + AC_COMPILE_IFELSE( +[#include +#include +#include +#include +#include +#include + +int main (void) +{ + int retval = TCA_STATS; + return (retval); +}], + [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])] + []); + + CFLAGS="$SAVE_CFLAGS" +fi +if test "x$with_libnetlink" = "xyes" then - with_liboconfig="yes (shipped version)" + AC_CHECK_LIB(netlink, rtnl_open, + [with_libnetlink="yes"], + [with_libnetlink="no (symbol 'rtnl_open' not found)"], + [$with_libnetlink_libs]) +fi +if test "x$with_libnetlink" = "xyes" +then + BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags" + BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs" + AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS) + AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS) fi +AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes") +# }}} -#with_liboping="yes" -with_own_liboping="no" -liboping_LDFLAGS="$LDFLAGS" -liboping_CPPFLAGS="$CPPFLAGS" -AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])], +# --with-libnetsnmp {{{ +with_snmp_config="net-snmp-config" +with_snmp_cflags="" +with_snmp_libs="" +AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])], [ - if test "x$withval" != "xno" && test "x$withval" != "xyes" - then - if test -d "$withval/lib" - then - liboping_LDFLAGS="$LDFLAGS -L$withval/lib" - fi - if test -d "$withval/include" - then - liboping_CPPFLAGS="$CPPFLAGS -I$withval/include" - fi - fi if test "x$withval" = "xno" then - with_liboping="no" - with_own_liboping="no" + with_libnetsnmp="no" + else if test "x$withval" = "xyes" + then + with_libnetsnmp="yes" + else + if test -x "$withval" + then + with_snmp_config="$withval" + with_libnetsnmp="yes" + else + with_snmp_config="$withval/bin/net-snmp-config" + with_libnetsnmp="yes" + fi + fi; fi +], +[with_libnetsnmp="yes"]) +if test "x$with_libnetsnmp" = "xyes" +then + with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null` + snmp_config_status=$? + + if test $snmp_config_status -ne 0 + then + with_libnetsnmp="no ($with_snmp_config failed)" + else + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_snmp_cflags" + + AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + fi +fi +if test "x$with_libnetsnmp" = "xyes" +then + with_snmp_libs=`$with_snmp_config --libs 2>/dev/null` + snmp_config_status=$? + + if test $snmp_config_status -ne 0 + then + with_libnetsnmp="no ($with_snmp_config failed)" + else + AC_CHECK_LIB(netsnmp, init_snmp, + [with_libnetsnmp="yes"], + [with_libnetsnmp="no (libnetsnmp not found)"], + [$with_snmp_libs]) + fi +fi +if test "x$with_libnetsnmp" = "xyes" +then + BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags" + BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs" + AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS) + AC_SUBST(BUILD_WITH_LIBSNMP_LIBS) +fi +AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes") +# }}} + +# --with-liboconfig {{{ +with_own_liboconfig="no" +liboconfig_LDFLAGS="$LDFLAGS" +liboconfig_CPPFLAGS="$CPPFLAGS" +AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + if test -d "$withval/lib" + then + liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib" + fi + if test -d "$withval/include" + then + liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include" + fi + fi + if test "x$withval" = "xno" + then + AC_MSG_ERROR("liboconfig is required") + fi +], +[ + with_liboconfig="yes" +]) + +save_LDFLAGS="$LDFLAGS" +save_CPPFLAGS="$CPPFLAGS" +LDFLAGS="$liboconfig_LDFLAGS" +CPPFLAGS="$liboconfig_CPPFLAGS" +AC_CHECK_LIB(oconfig, oconfig_parse_fh, +[ + with_liboconfig="yes" + with_own_liboconfig="no" +], +[ + with_liboconfig="yes" + with_own_liboconfig="yes" + LDFLAGS="$save_LDFLAGS" + CPPFLAGS="$save_CPPFLAGS" +]) + +AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes") +if test "x$with_own_liboconfig" = "xyes" +then + with_liboconfig="yes (shipped version)" +fi +# }}} + +# --with-liboping {{{ +with_own_liboping="no" +liboping_LDFLAGS="$LDFLAGS" +liboping_CPPFLAGS="$CPPFLAGS" +AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + if test -d "$withval/lib" + then + liboping_LDFLAGS="$LDFLAGS -L$withval/lib" + fi + if test -d "$withval/include" + then + liboping_CPPFLAGS="$CPPFLAGS -I$withval/include" + fi + fi + if test "x$withval" = "xno" + then + with_liboping="no" + with_own_liboping="no" else if test "x$withval" = "xyes" then with_liboping="yes" @@ -1502,7 +1564,78 @@ then fi AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes") AM_CONDITIONAL(BUILD_WITH_OWN_LIBOPING, test "x$with_own_liboping" = "xyes") +# }}} + +# --with-oracle {{{ +with_oracle_cppflags="" +with_oracle_libs="" +AC_ARG_WITH(oracle, [AS_HELP_STRING([--with-oracle@<:@=ORACLE_HOME@:>@], [Path to Oracle.])], +[ + if test "x$withval" = "xyes" + then + if test "x$ORACLE_HOME" = "x" + then + AC_MSG_WARN([Use of the Oracle library has been forced, but the environment variable ORACLE_HOME is not set.]) + fi + with_oracle="yes" + else if test "x$withval" = "xno" + then + with_oracle="no" + else + with_oracle="yes" + ORACLE_HOME="$withval" + fi; fi +], +[ + if test "x$ORACLE_HOME" = "x" + then + with_oracle="no (ORACLE_HOME is not set)" + else + with_oracle="yes" + fi +]) +if test "x$ORACLE_HOME" != "x" +then + with_oracle_cppflags="-I$ORACLE_HOME/rdbms/public" + + if test -e "$ORACLE_HOME/lib/ldflags" + then + with_oracle_libs=`cat "$ORACLE_HOME/lib/ldflags"` + fi + #with_oracle_libs="-L$ORACLE_HOME/lib $with_oracle_libs -lclntsh" + with_oracle_libs="-L$ORACLE_HOME/lib -lclntsh" +fi +if test "x$with_oracle" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_oracle_cppflags" + + AC_CHECK_HEADERS(oci.h, [with_oracle="yes"], [with_oracle="no (oci.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_oracle" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_oracle_cppflags" + LDFLAGS="$LDFLAGS $with_oracle_libs" + AC_CHECK_FUNC(OCIEnvCreate, [with_oracle="yes"], [with_oracle="no (Symbol 'OCIEnvCreate' not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_oracle" = "xyes" +then + BUILD_WITH_ORACLE_CFLAGS="$with_oracle_cppflags" + BUILD_WITH_ORACLE_LIBS="$with_oracle_libs" + AC_SUBST(BUILD_WITH_ORACLE_CFLAGS) + AC_SUBST(BUILD_WITH_ORACLE_LIBS) +fi +# }}} + +# --with-libowcapi {{{ with_libowcapi_cppflags="" with_libowcapi_libs="-lowcapi" AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])], @@ -1547,8 +1680,9 @@ then AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS) AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS) fi +# }}} - +# --with-libpcap {{{ AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])], [ if test "x$withval" != "xno" && test "x$withval" != "xyes" @@ -1586,45 +1720,81 @@ fi AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap], [Wether or not to use the pcap library]) AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes") +# }}} + +# --with-libpcre {{{ +with_pcre_config="pcre-config" +with_pcre_cflags="" +with_pcre_libs="" +AC_ARG_WITH(libpcre, [AS_HELP_STRING([--with-libpcre@<:@=PREFIX@:>@], + [Path to libpcre.])], + [ + if test "x$withval" = "xno" + then + with_libpcre="no" + else if test "x$withval" = "xyes" + then + with_libpcre="yes" + else + if test -f "$withval" && test -x "$withval" + then + with_pcre_config="$withval" + else if test -x "$withval/bin/pcre-config" + then + with_pcre_config="$withval/bin/pcre-config" + fi; fi + with_libpcre="yes" + fi; fi + ], + [ + with_libpcre="yes" + ]) -AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])], -[ - if test "x$withval" != "xno" && test "x$withval" != "xyes" +if test "x$with_libpcre" = "xyes" +then + with_pcre_cflags=`$with_pcre_config --cflags 2>/dev/null` + pcre_config_status=$? + + if test $pcre_config_status -ne 0 then - LDFLAGS="$LDFLAGS -L$withval/lib" - CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE" - with_libesmtp="yes" + with_libpcre="no ($with_pcre_config failed)" else - with_libesmtp="$withval" + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_pcre_cflags" + + AC_CHECK_HEADERS(pcre.h, [], [with_libpcre="no (pcre.h not found)"], []) + + CPPFLAGS="$SAVE_CPPFLAGS" fi -], -[ - with_libesmtp="yes" -]) -if test "x$with_libesmtp" = "xyes" -then - AC_CHECK_LIB(esmtp, smtp_create_session, - [ - AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).]) - ], [with_libesmtp="no (libesmtp not found)"]) fi -if test "x$with_libesmtp" = "xyes" + +if test "x$with_libpcre" = "xyes" then - AC_CHECK_HEADERS(libesmtp.h, - [ - AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the header file.]) - ], [with_libesmtp="no (libesmtp.h not found)"]) + with_pcre_libs=`$with_pcre_config --libs 2>/dev/null` + pcre_config_status=$? + + if test $pcre_config_status -ne 0 + then + with_libpcre="no ($with_pcre_config failed)" + else + AC_CHECK_LIB(pcre, pcre_compile, + [with_libpcre="yes"], + [with_libpcre="no (symbol 'pcre_compile' not found)"], + [$with_pcre_libs]) + fi fi -if test "x$with_libesmtp" = "xyes" + +if test "x$with_libpcre" = "xyes" then - collect_libesmtp=1 -else - collect_libesmtp=0 + BUILD_WITH_LIBPCRE_CFLAGS="$with_pcre_cflags" + BUILD_WITH_LIBPCRE_LIBS="$with_pcre_libs" + AC_SUBST(BUILD_WITH_LIBPCRE_CFLAGS) + AC_SUBST(BUILD_WITH_LIBPCRE_LIBS) fi -AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp], - [Wether or not to use the esmtp library]) -AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes") +AM_CONDITIONAL(BUILD_WITH_LIBPCRE, test "x$with_libpcre" = "xyes") +# }}} +# --with-libperl {{{ perl_interpreter="perl" AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])], [ @@ -1740,551 +1910,526 @@ then CFLAGS=$SAVE_CFLAGS LDFLAGS=$SAVE_LDFLAGS fi +# }}} -with_own_libiptc="no" -AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])], +# --with-libpq {{{ +with_pg_config="pg_config" +with_libpq_includedir="" +with_libpq_libdir="" +with_libpq_cppflags="" +with_libpq_ldflags="" +AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@], + [Path to libpq.])], [ - if test "x$withval" != "xno" && test "x$withval" != "xyes" + if test "x$withval" = "xno" then - LDFLAGS="$LDFLAGS -L$withval/lib" - CPPFLAGS="$CPPFLAGS -I$withval/include" - with_libiptc="yes" + with_libpq="no" + else if test "x$withval" = "xyes" + then + with_libpq="yes" else - with_libiptc="$withval" - fi + if test -f "$withval" && test -x "$withval"; + then + with_pg_config="$withval" + else if test -x "$withval/bin/pg_config" + then + with_pg_config="$withval/bin/pg_config" + fi; fi + with_libpq="yes" + fi; fi ], [ - if test "x$ac_system" = "xLinux" + with_libpq="yes" +]) +if test "x$with_libpq" = "xyes" +then + with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null` + pg_config_status=$? + + if test $pg_config_status -eq 0 then - with_libiptc="yes" + if test -n "$with_libpq_includedir"; then + for dir in $with_libpq_includedir; do + with_libpq_cppflags="$with_libpq_cppflags -I$dir" + done + fi else - with_libiptc="no (Linux only)" + AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) fi -]) -if test "x$with_libiptc" = "xyes" -then - AC_CHECK_LIB(iptc, iptc_init, - [ - AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).]) - ], - [ - with_libiptc="yes" - with_own_libiptc="yes" - ]) -fi -if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" != "xyes" -then - AC_CHECK_HEADERS(libiptc/libiptc.h, - [ - AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the header file.]) - ], - [ - with_libiptc="yes" - with_own_libiptc="yes" - ]) + + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libpq_cppflags" + + AC_CHECK_HEADERS(libpq-fe.h, [], + [with_libpq="no (libpq-fe.h not found)"], []) + + CPPFLAGS="$SAVE_CPPFLAGS" fi -if test "x$with_libiptc" = "xyes" +if test "x$with_libpq" = "xyes" then - SAVE_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS $KERNEL_CFLAGS" + with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null` + pg_config_status=$? - AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [], - [ - with_libiptc="no (Linux iptables headers not found - check KERNEL_DIR)" - with_own_libiptc="no" - ], - [ -#include "$srcdir/src/libiptc/ipt_kernel_headers.h" - ]) + if test $pg_config_status -eq 0 + then + if test -n "$with_libpq_libdir"; then + for dir in $with_libpq_libdir; do + with_libpq_ldflags="$with_libpq_ldflags -L$dir" + done + fi + else + AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) + fi - CFLAGS=$SAVE_CFLAGS + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $with_libpq_ldflags" + + AC_CHECK_LIB(pq, PQconnectdb, + [with_libpq="yes"], + [with_libpq="no (symbol 'PQconnectdb' not found)"]) + + LDFLAGS="$SAVE_LDFLAGS" fi -AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes") -AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_own_libiptc" = "xyes") -if test "x$with_own_libiptc" = "xyes" +if test "x$with_libpq" = "xyes" then - AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.]) + BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags" + BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags" + AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS) fi +AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes") +# }}} -with_snmp_config="net-snmp-config" -with_snmp_cflags="" -with_snmp_libs="" -AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])], -[ - if test "x$withval" = "xno" - then - with_libnetsnmp="no" - else if test "x$withval" = "xyes" +# --with-libpthread {{{ +AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])], +[ if test "x$withval" != "xno" \ + && test "x$withval" != "xyes" then - with_libnetsnmp="yes" + LDFLAGS="$LDFLAGS -L$withval/lib" + CPPFLAGS="$CPPFLAGS -I$withval/include" + with_libpthread="yes" else - if test -x "$withval" + if test "x$withval" = "xno" then - with_snmp_config="$withval" - with_libnetsnmp="yes" - else - with_snmp_config="$withval/bin/net-snmp-config" - with_libnetsnmp="yes" + with_libpthread="no (disabled)" fi - fi; fi -], -[with_libnetsnmp="yes"]) -if test "x$with_libnetsnmp" = "xyes" -then - with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null` - snmp_config_status=$? - - if test $snmp_config_status -ne 0 - then - with_libnetsnmp="no ($with_snmp_config failed)" - else - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_snmp_cflags" - - AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"]) - - CPPFLAGS="$SAVE_CPPFLAGS" fi +], [with_libpthread="yes"]) +if test "x$with_libpthread" = "xyes" +then + AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], []) fi -if test "x$with_libnetsnmp" = "xyes" +if test "x$with_libpthread" = "xyes" then - with_snmp_libs=`$with_snmp_config --libs 2>/dev/null` - snmp_config_status=$? + AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"]) +fi +if test "x$with_libpthread" = "xyes" +then + collect_pthread=1 +else + collect_pthread=0 +fi +AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread], + [Wether or not to use pthread (POSIX threads) library]) +AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes") +# }}} - if test $snmp_config_status -ne 0 +# --with-librrd {{{ +# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given]) +librrd_cflags="" +librrd_ldflags="" +librrd_threadsafe="yes" +librrd_rrdc_update="no" +AC_ARG_WITH(librrd, [AS_HELP_STRING([--with-librrd@<:@=PREFIX@:>@], [Path to rrdtool.])], +[ if test "x$withval" != "xno" && test "x$withval" != "xyes" then - with_libnetsnmp="no ($with_snmp_config failed)" + librrd_cflags="-I$withval/include" + librrd_ldflags="-L$withval/lib" + with_librrd="yes" else - AC_CHECK_LIB(netsnmp, init_snmp, - [with_libnetsnmp="yes"], - [with_libnetsnmp="no (libnetsnmp not found)"], - [$with_snmp_libs]) + with_librrd="$withval" fi -fi -if test "x$with_libnetsnmp" = "xyes" +], [with_librrd="yes"]) +if test "x$with_librrd" = "xyes" then - BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags" - BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs" - AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS) - AC_SUBST(BUILD_WITH_LIBSNMP_LIBS) -fi -AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes") + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" -PKG_CHECK_MODULES([LIBNOTIFY], [libnotify], - [with_libnotify="yes"], - [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"]) + CPPFLAGS="$CPPFLAGS $librrd_cflags" + LDFLAGS="$LDFLAGS $librrd_ldflags" -with_libupsclient="no (pkg-config isn't available)" -with_libupsclient_cflags="" -with_libupsclient_libs="" -if test "x$PKG_CONFIG" != "x" + AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_librrd" = "xyes" then - pkg-config --exists 'libupsclient' 2>/dev/null - if test "$?" = "0" + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + + CPPFLAGS="$CPPFLAGS $librrd_cflags" + LDFLAGS="$LDFLAGS $librrd_ldflags" + + AC_CHECK_LIB(rrd_th, rrd_update_r, + [with_librrd="yes" + librrd_ldflags="$librrd_ldflags -lrrd_th -lm" + ], + [librrd_threadsafe="no" + AC_CHECK_LIB(rrd, rrd_update, + [with_librrd="yes" + librrd_ldflags="$librrd_ldflags -lrrd -lm" + ], + [with_librrd="no (symbol 'rrd_update' not found)"], + [-lm]) + ], + [-lm]) + + if test "x$librrd_threadsafe" = "xyes" then - with_libupsclient="yes" + AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"]) else - with_libupsclient="no (pkg-config doesn't know library)" + AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"]) fi + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" fi -if test "x$with_libupsclient" = "xyes" +if test "x$with_librrd" = "xyes" then - with_libupsclient_cflags="`pkg-config --cflags 'libupsclient'`" - if test $? -ne 0 + BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags" + BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags" + AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS) + AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS) +fi +if test "x$librrd_threadsafe" = "xyes" +then + AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).]) +fi +# }}} + +# --with-libsensors {{{ +with_sensors_cflags="" +with_sensors_ldflags="" +AC_ARG_WITH(libsensors, [AS_HELP_STRING([--with-libsensors@<:@=PREFIX@:>@], [Path to lm_sensors.])], +[ + if test "x$withval" = "xno" then - with_libupsclient="no" + with_libsensors="no" + else + with_libsensors="yes" + if test "x$withval" != "xyes" + then + with_sensors_cflags="-I$withval/include" + with_sensors_ldflags="-L$withval/lib" + with_libsensors="yes" + fi fi - with_libupsclient_libs="`pkg-config --libs 'libupsclient'`" - if test $? -ne 0 +], +[ + if test "x$ac_system" = "xLinux" then - with_libupsclient="no" + with_libsensors="yes" + else + with_libsensors="no (Linux only library)" fi -fi -if test "x$with_libupsclient" = "xyes" +]) +if test "x$with_libsensors" = "xyes" then SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + CPPFLAGS="$CPPFLAGS $with_sensors_cflags" - AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"]) +# AC_CHECK_HEADERS(sensors/sensors.h, +# [ +# AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the header file.]) +# ], +# [with_libsensors="no (sensors/sensors.h not found)"]) + AC_CHECK_HEADERS(sensors/sensors.h, [], [with_libsensors="no (sensors/sensors.h not found)"]) CPPFLAGS="$SAVE_CPPFLAGS" fi -if test "x$with_libupsclient" = "xyes" +if test "x$with_libsensors" = "xyes" then SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_sensors_cflags" + LDFLAGS="$LDFLAGS $with_sensors_ldflags" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" - LDFLAGS="$LDFLAGS $with_libupsclient_libs" - - AC_CHECK_LIB(upsclient, upscli_connect, - [with_libupsclient="yes"], - [with_libupsclient="no (symbol upscli_connect not found)"]) + AC_CHECK_LIB(sensors, sensors_init, + [ + AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).]) + ], + [with_libsensors="no (libsensors not found)"]) CPPFLAGS="$SAVE_CPPFLAGS" LDFLAGS="$SAVE_LDFLAGS" fi -if test "x$with_libupsclient" = "xyes" +if test "x$with_libsensors" = "xyes" then - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags" + BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags" + AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS) + AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS) +fi +AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes") +# }}} - AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [], -[#include -#include -#include ]) - - CPPFLAGS="$SAVE_CPPFLAGS" -fi -if test "x$with_libupsclient" = "xyes" -then - BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags" - BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs" - AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS) - AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS) -fi - -### BEGIN of check for libxmms ### -with_xmms_config="xmms-config" -with_xmms_cflags="" -with_xmms_libs="" -AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])], +# --with-libstatgrab {{{ +with_libstatgrab_cflags="" +with_libstatgrab_ldflags="" +AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])], [ if test "x$withval" != "xno" \ && test "x$withval" != "xyes" then - if test -f "$withval" && test -x "$withval"; - then - with_xmms_config="$withval" - else if test -x "$withval/bin/xmms-config" - then - with_xmms_config="$withval/bin/xmms-config" - fi; fi - with_libxmms="yes" - else if test "x$withval" = "xno" - then - with_libxmms="no" + with_libstatgrab_cflags="-I$withval/include" + with_libstatgrab_ldflags="-L$withval/lib" + with_libstatgrab="yes" else - with_libxmms="yes" - fi; fi + with_libstatgrab="$withval" + fi ], [ - with_libxmms="yes" -]) -if test "x$with_libxmms" = "xyes" -then - with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null` - xmms_config_status=$? - - if test $xmms_config_status -ne 0 - then - with_libxmms="no" - fi -fi -if test "x$with_libxmms" = "xyes" -then - with_xmms_libs=`$with_xmms_config --libs 2>/dev/null` - xmms_config_status=$? - - if test $xmms_config_status -ne 0 + if test "x$ac_system" = "xunknown" then - with_libxmms="no" + with_libstatgrab="yes" + else + with_libstatgrab="no" fi -fi -if test "x$with_libxmms" = "xyes" -then - AC_CHECK_LIB(xmms, xmms_remote_get_info, - [ - BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags" - BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs" - AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS) - AC_SUBST(BUILD_WITH_LIBXMMS_LIBS) - ], - [ - with_libxmms="no" - ], - [$with_xmms_libs]) -fi -with_libxmms_numeric=0 -if test "x$with_libxmms" = "xyes" -then - with_libxmms_numeric=1 -fi -AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).]) -AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes") -### END of check for libxmms ### - -with_libnetlink_cflags="" -with_libnetlink_libs="-lnetlink" -AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])], -[ - echo "libnetlink: withval = $withval" - if test "x$withval" = "xyes" - then - with_libnetlink="yes" - else if test "x$withval" = "xno" - then - with_libnetlink="no" - else - if test -d "$withval/include" - then - with_libnetlink_cflags="-I$withval/include" - with_libnetlink_libs="-L$withval/lib -lnetlink" - with_libnetlink="yes" - else - AC_MSG_ERROR("no such directory: $withval/include") - fi - fi; fi -], -[ - if test "x$ac_system" = "xLinux" - then - with_libnetlink="yes" - else - with_libnetlink="no (Linux only library)" - fi ]) -if test "x$with_libnetlink" = "xyes" -then - SAVE_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS $with_libnetlink_cflags" - - with_libnetlink="no (libnetlink.h not found)" - - AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h, - [ - with_libnetlink="yes" - break - ], [], -[#include -#include -#include -#include -#include -#include ]) - AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [], -[#include -#include -#include -#include ]) - - AC_COMPILE_IFELSE( -[#include -#include -#include -#include -#include -#include - -int main (void) -{ - int retval = TCA_STATS2; - return (retval); -}], - [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])] - []); - - AC_COMPILE_IFELSE( -[#include -#include -#include -#include -#include -#include - -int main (void) -{ - int retval = TCA_STATS; - return (retval); -}], - [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])] - []); - - CFLAGS="$SAVE_CFLAGS" -fi -if test "x$with_libnetlink" = "xyes" -then - AC_CHECK_LIB(netlink, rtnl_open, - [with_libnetlink="yes"], - [with_libnetlink="no (symbol 'rtnl_open' not found)"], - [$with_libnetlink_libs]) -fi -if test "x$with_libnetlink" = "xyes" -then - BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags" - BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs" - AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS) - AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS) -fi -AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes") - -with_libopenipmipthread="yes" -with_libopenipmipthread_cflags="" -with_libopenipmipthread_libs="" - -AC_MSG_CHECKING([for pkg-config]) -temp_result="no" -if test "x$PKG_CONFIG" = "x" +with_libstatgrab_pkg_config="yes" +if test "x$with_libstatgrab" = "xyes" \ + && test "x$PKG_CONFIG" != "x" then - with_libopenipmipthread="no" - temp_result="no" + AC_MSG_CHECKING([pkg-config for libstatgrab]) + temp_result="found" + $PKG_CONFIG --exists libstatgrab 2>/dev/null + if test "$?" != "0" + then + with_libstatgrab_pkg_config="no" + temp_result="not found" + fi + AC_MSG_RESULT([$temp_result]) else - temp_result="$PKG_CONFIG" + AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.]) + with_libstatgrab_pkg_config="no" + with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab" fi -AC_MSG_RESULT([$temp_result]) -if test "x$with_libopenipmipthread" = "xyes" +if test "x$with_libstatgrab" = "xyes" \ + && test "x$with_libstatgrab_pkg_config" = "xyes" \ + && test "x$with_libstatgrab_cflags" = "x" then - AC_MSG_CHECKING([for libOpenIPMIpthread]) - $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null - if test "$?" != "0" - then - with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)" - fi - AC_MSG_RESULT([$with_libopenipmipthread]) + AC_MSG_CHECKING([for libstatgrab CFLAGS]) + temp_result="`$PKG_CONFIG --cflags libstatgrab`" + if test "$?" = "0" + then + with_libstatgrab_cflags="$temp_result" + else + with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)" + temp_result="$PKG_CONFIG --cflags libstatgrab failed" + fi + AC_MSG_RESULT([$temp_result]) fi -if test "x$with_libopenipmipthread" = "xyes" +if test "x$with_libstatgrab" = "xyes" \ + && test "x$with_libstatgrab_pkg_config" = "xyes" \ + && test "x$with_libstatgrab_ldflags" = "x" then - AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS]) - temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`" - if test "$?" = "0" - then - with_libopenipmipthread_cflags="$temp_result" - else - with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)" - temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed" - fi - AC_MSG_RESULT([$temp_result]) + AC_MSG_CHECKING([for libstatgrab LDFLAGS]) + temp_result="`$PKG_CONFIG --libs libstatgrab`" + if test "$?" = "0" + then + with_libstatgrab_ldflags="$temp_result" + else + with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)" + temp_result="$PKG_CONFIG --libs libstatgrab failed" + fi + AC_MSG_RESULT([$temp_result]) fi -if test "x$with_libopenipmipthread" = "xyes" +if test "x$with_libstatgrab" = "xyes" then - AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS]) - temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`" - if test "$?" = "0" - then - with_libopenipmipthread_ldflags="$temp_result" - else - with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)" - temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed" - fi - AC_MSG_RESULT([$temp_result]) + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags" + + AC_CHECK_HEADERS(statgrab.h, + [with_libstatgrab="yes"], + [with_libstatgrab="no (statgrab.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" fi -if test "x$with_libopenipmipthread" = "xyes" +if test "x$with_libstatgrab" = "xyes" then - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags" + SAVE_CFLAGS="$CFLAGS" + SAVE_LDFLAGS="$LDFLAGS" - AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h, - [with_libopenipmipthread="yes"], - [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"], -[#include -#include -#include -#include -]) + CFLAGS="$CFLAGS $with_libstatgrab_cflags" + LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags" - CPPFLAGS="$SAVE_CPPFLAGS" + AC_CHECK_LIB(statgrab, sg_init, + [with_libstatgrab="yes"], + [with_libstatgrab="no (symbol sg_init not found)"]) + + CFLAGS="$SAVE_CFLAGS" + LDFLAGS="$SAVE_LDFLAGS" fi -if test "x$with_libopenipmipthread" = "xyes" +AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes") +if test "x$with_libstatgrab" = "xyes" then - BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags" - BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags" - AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS) - AC_SUBST(BUILD_WITH_OPENIPMI_LIBS) + AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)]) + BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags" + BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags" + AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS) + AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS) fi +# }}} -dnl Check for libpq. -with_pg_config="pg_config" -with_libpq_includedir="" -with_libpq_libdir="" -with_libpq_cppflags="" -with_libpq_ldflags="" -AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@], - [Path to libpq.])], +# --with-libxmms {{{ +with_xmms_config="xmms-config" +with_xmms_cflags="" +with_xmms_libs="" +AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])], [ - if test "x$withval" = "xno" - then - with_libpq="no" - else if test "x$withval" = "xyes" + if test "x$withval" != "xno" \ + && test "x$withval" != "xyes" then - with_libpq="yes" - else if test -f "$withval" && test -x "$withval"; then - with_pg_config="$withval" - else if test -x "$withval/bin/pg_config" + with_xmms_config="$withval" + else if test -x "$withval/bin/xmms-config" then - with_pg_config="$withval/bin/pg_config" + with_xmms_config="$withval/bin/xmms-config" fi; fi - with_libpq="yes" + with_libxmms="yes" + else if test "x$withval" = "xno" + then + with_libxmms="no" + else + with_libxmms="yes" fi; fi ], [ - with_libpq="yes" + with_libxmms="yes" ]) -if test "x$with_libpq" = "xyes" +if test "x$with_libxmms" = "xyes" then - with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null` - pg_config_status=$? + with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null` + xmms_config_status=$? - if test $pg_config_status -eq 0 + if test $xmms_config_status -ne 0 then - if test -n "$with_libpq_includedir"; then - for dir in $with_libpq_includedir; do - with_libpq_cppflags="$with_libpq_cppflags -I$dir" - done - fi - else - AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) + with_libxmms="no" fi +fi +if test "x$with_libxmms" = "xyes" +then + with_xmms_libs=`$with_xmms_config --libs 2>/dev/null` + xmms_config_status=$? - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libpq_cppflags" - - AC_CHECK_HEADERS(libpq-fe.h, [], - [with_libpq="no (libpq-fe.h not found)"], []) - - CPPFLAGS="$SAVE_CPPFLAGS" + if test $xmms_config_status -ne 0 + then + with_libxmms="no" + fi fi -if test "x$with_libpq" = "xyes" +if test "x$with_libxmms" = "xyes" then - with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null` - pg_config_status=$? + AC_CHECK_LIB(xmms, xmms_remote_get_info, + [ + BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags" + BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs" + AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS) + AC_SUBST(BUILD_WITH_LIBXMMS_LIBS) + ], + [ + with_libxmms="no" + ], + [$with_xmms_libs]) +fi +with_libxmms_numeric=0 +if test "x$with_libxmms" = "xyes" +then + with_libxmms_numeric=1 +fi +AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).]) +AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes") +# }}} - if test $pg_config_status -eq 0 +# pkg-config --exists 'libupsclient' {{{ +with_libupsclient="no (pkg-config isn't available)" +with_libupsclient_cflags="" +with_libupsclient_libs="" +if test "x$PKG_CONFIG" != "x" +then + pkg-config --exists 'libupsclient' 2>/dev/null + if test "$?" = "0" then - if test -n "$with_libpq_libdir"; then - for dir in $with_libpq_libdir; do - with_libpq_ldflags="$with_libpq_ldflags -L$dir" - done - fi + with_libupsclient="yes" else - AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) + with_libupsclient="no (pkg-config doesn't know library)" + fi +fi +if test "x$with_libupsclient" = "xyes" +then + with_libupsclient_cflags="`pkg-config --cflags 'libupsclient'`" + if test $? -ne 0 + then + with_libupsclient="no" + fi + with_libupsclient_libs="`pkg-config --libs 'libupsclient'`" + if test $? -ne 0 + then + with_libupsclient="no" fi +fi +if test "x$with_libupsclient" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libupsclient" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $with_libpq_ldflags" - AC_CHECK_LIB(pq, PQconnectdb, - [with_libpq="yes"], - [with_libpq="no (symbol 'PQconnectdb' not found)"]) + CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + LDFLAGS="$LDFLAGS $with_libupsclient_libs" + + AC_CHECK_LIB(upsclient, upscli_connect, + [with_libupsclient="yes"], + [with_libupsclient="no (symbol upscli_connect not found)"]) + CPPFLAGS="$SAVE_CPPFLAGS" LDFLAGS="$SAVE_LDFLAGS" fi -if test "x$with_libpq" = "xyes" +if test "x$with_libupsclient" = "xyes" then - BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags" - BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags" - AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS) - AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS) + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + + AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [], +[#include +#include +#include ]) + + CPPFLAGS="$SAVE_CPPFLAGS" fi -AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes") +if test "x$with_libupsclient" = "xyes" +then + BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags" + BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs" + AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS) + AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS) +fi +# }}} -dnl Check for libvirt and libxml2 libraries. +# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{ with_libxml2="no (pkg-config isn't available)" with_libxml2_cflags="" with_libxml2_ldflags="" @@ -2399,8 +2544,92 @@ if test "x$with_libvirt" = "xyes"; then AC_SUBST(BUILD_WITH_LIBVIRT_CFLAGS) AC_SUBST(BUILD_WITH_LIBVIRT_LIBS) fi +# }}} + +# $PKG_CONFIG --exists OpenIPMIpthread {{{ +with_libopenipmipthread="yes" +with_libopenipmipthread_cflags="" +with_libopenipmipthread_libs="" + +AC_MSG_CHECKING([for pkg-config]) +temp_result="no" +if test "x$PKG_CONFIG" = "x" +then + with_libopenipmipthread="no" + temp_result="no" +else + temp_result="$PKG_CONFIG" +fi +AC_MSG_RESULT([$temp_result]) + +if test "x$with_libopenipmipthread" = "xyes" +then + AC_MSG_CHECKING([for libOpenIPMIpthread]) + $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null + if test "$?" != "0" + then + with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)" + fi + AC_MSG_RESULT([$with_libopenipmipthread]) +fi + +if test "x$with_libopenipmipthread" = "xyes" +then + AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS]) + temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`" + if test "$?" = "0" + then + with_libopenipmipthread_cflags="$temp_result" + else + with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)" + temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed" + fi + AC_MSG_RESULT([$temp_result]) +fi + +if test "x$with_libopenipmipthread" = "xyes" +then + AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS]) + temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`" + if test "$?" = "0" + then + with_libopenipmipthread_ldflags="$temp_result" + else + with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)" + temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed" + fi + AC_MSG_RESULT([$temp_result]) +fi + +if test "x$with_libopenipmipthread" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags" + + AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h, + [with_libopenipmipthread="yes"], + [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"], +[#include +#include +#include +#include +]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi + +if test "x$with_libopenipmipthread" = "xyes" +then + BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags" + BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags" + AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS) + AC_SUBST(BUILD_WITH_OPENIPMI_LIBS) +fi +# }}} -dnl End of check for libvirt and libxml2 libraries. +PKG_CHECK_MODULES([LIBNOTIFY], [libnotify], + [with_libnotify="yes"], + [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"]) # Check for enabled/disabled features # @@ -2707,6 +2936,7 @@ AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics]) AC_PLUGIN([cpufreq], [$plugin_cpufreq], [CPU frequency statistics]) AC_PLUGIN([csv], [yes], [CSV output plugin]) AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications]) +AC_PLUGIN([dbi], [$with_libdbi], [General database statistics]) AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics]) AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics]) AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis]) @@ -2714,6 +2944,8 @@ AC_PLUGIN([email], [yes], [EMail statistics]) AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics]) AC_PLUGIN([exec], [yes], [Execution of external programs]) AC_PLUGIN([filecount], [yes], [Count files in directories]) +AC_PLUGIN([filter_ignore], [yes], [Ignore specific values]) +AC_PLUGIN([filter_pcre], [$with_libpcre], [Filter based on PCRE]) AC_PLUGIN([hddtemp], [yes], [Query hddtempd]) AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters]) @@ -2736,13 +2968,15 @@ AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier]) AC_PLUGIN([ntpd], [yes], [NTPd statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics]) +AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) AC_PLUGIN([ping], [$with_liboping], [Network latency statistics]) AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) AC_PLUGIN([processes], [$plugin_processes], [Process statistics]) -AC_PLUGIN([rrdtool], [$with_rrdtool], [RRDTool output plugin]) -AC_PLUGIN([sensors], [$with_lm_sensors], [lm_sensors statistics]) +AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin]) +AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin]) +AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics]) AC_PLUGIN([serial], [$plugin_serial], [serial port traffic]) AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin]) AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics]) @@ -2799,10 +3033,10 @@ AC_SUBST(PERL_BINDINGS_OPTIONS) AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile) -if test "x$with_rrdtool" = "xyes" \ +if test "x$with_librrd" = "xyes" \ && test "x$librrd_threadsafe" != "xyes" then - with_rrdtool="yes (warning: librrd is not thread-safe)" + with_librrd="yes (warning: librrd is not thread-safe)" fi if test "x$with_liboping" = "xyes" \ @@ -2834,6 +3068,7 @@ cat < r_tbl.owner, segment_name => r_tbl.TABLE_NAME, segment_type => 'TABLE', total_blocks => l_total_blocks, total_bytes => l_total_bytes, unused_blocks => l_unused_blocks, unused_bytes => l_unused_bytes, last_used_extent_file_id => l_lastusedextfileid, last_used_extent_block_id => l_lastusedextblockid, last_used_block => l_last_used_block); + + EXCEPTION + WHEN others THEN + DBMS_OUTPUT.PUT_LINE('tbl_name: ' || r_tbl.TABLE_NAME); + END; + INSERT + INTO c_tbl_size + VALUES(r_tbl.owner, r_tbl.TABLE_NAME, l_total_bytes -l_unused_bytes); + END LOOP; + + COMMIT; + + FOR r_idx IN cur_idx + LOOP + BEGIN + dbms_space.unused_space(segment_owner => r_idx.owner, segment_name => r_idx.index_name, segment_type => 'INDEX', total_blocks => l_total_blocks, total_bytes => l_total_bytes, unused_blocks => l_unused_blocks, unused_bytes => l_unused_bytes, last_used_extent_file_id => l_lastusedextfileid, last_used_extent_block_id => l_lastusedextblockid, last_used_block => l_last_used_block); + + EXCEPTION + WHEN others THEN + DBMS_OUTPUT.PUT_LINE('idx_name: ' || r_idx.index_name); + END; + + UPDATE c_tbl_size + SET bytes = bytes + l_total_bytes -l_unused_bytes + WHERE owner = r_idx.owner + AND TABLE_NAME = r_idx.TABLE_NAME; + END LOOP; + + COMMIT; + + FOR r_lob IN cur_lob + LOOP + BEGIN + dbms_space.unused_space(segment_owner => r_lob.owner, segment_name => r_lob.segment_name, segment_type => 'LOB', total_blocks => l_total_blocks, total_bytes => l_total_bytes, unused_blocks => l_unused_blocks, unused_bytes => l_unused_bytes, last_used_extent_file_id => l_lastusedextfileid, last_used_extent_block_id => l_lastusedextblockid, last_used_block => l_last_used_block); + + EXCEPTION + WHEN others THEN + DBMS_OUTPUT.PUT_LINE('lob_name: ' || r_lob.segment_name); + END; + + UPDATE c_tbl_size + SET bytes = bytes + l_total_bytes -l_unused_bytes + WHERE owner = r_lob.owner + AND TABLE_NAME = r_lob.TABLE_NAME; + END LOOP; + + COMMIT; + +END get_object_size; +/ + +create or replace PROCEDURE get_tbs_size AS +BEGIN + +execute immediate 'truncate table c_tbs_usage'; + +insert into c_tbs_usage ( +select df.tablespace_name as tablespace_name, + decode(df.maxbytes, + 0, + sum(fs.bytes), + (df.maxbytes-(df.bytes-sum(fs.bytes)))) as bytes_free, + decode(df.maxbytes, + 0, + round((df.bytes-sum(fs.bytes))), + round(df.maxbytes-(df.maxbytes-(df.bytes-sum(fs.bytes))))) as bytes_used +from dba_free_space fs inner join + (select + tablespace_name, + sum(bytes) bytes, + sum(decode(maxbytes,0,bytes,maxbytes)) maxbytes + from dba_data_files + group by tablespace_name ) df +on fs.tablespace_name = df.tablespace_name +group by df.tablespace_name,df.maxbytes,df.bytes); + +COMMIT; + +END get_tbs_size; +/ + +BEGIN +sys.dbms_scheduler.create_job( +job_name => '"COLLECTDU"."C_TBSSIZE_JOB"', +job_type => 'PLSQL_BLOCK', +job_action => 'begin + get_tbs_size(); +end;', +repeat_interval => 'FREQ=MINUTELY;INTERVAL=5', +start_date => systimestamp at time zone 'Europe/Berlin', +job_class => '"DEFAULT_JOB_CLASS"', +auto_drop => FALSE, +enabled => TRUE); +END; +/ + +BEGIN +sys.dbms_scheduler.create_job( +job_name => '"COLLECTDU"."C_TBLSIZE_COLLECTDU_JOB"', +job_type => 'PLSQL_BLOCK', +job_action => 'begin + get_object_size( owner => ''COLLECTDU'' ); +end;', +repeat_interval => 'FREQ=HOURLY;INTERVAL=12', +start_date => systimestamp at time zone 'Europe/Berlin', +job_class => '"DEFAULT_JOB_CLASS"', +auto_drop => FALSE, +enabled => TRUE); +END; +/ + +spool off +quit diff --git a/contrib/oracle/db_systat.sql b/contrib/oracle/db_systat.sql new file mode 100644 index 00000000..cf2a3c4a --- /dev/null +++ b/contrib/oracle/db_systat.sql @@ -0,0 +1,55 @@ +-- Table sizes +SELECT owner, + TABLE_NAME, + bytes +FROM collectdu.c_tbl_size; + +-- Tablespace sizes +SELECT tablespace_name, + bytes_free, + bytes_used +FROM collectdu.c_tbs_usage; + +-- IO per Tablespace +SELECT SUM(vf.phyblkrd) *8192 AS +phy_blk_r, + SUM(vf.phyblkwrt) *8192 AS +phy_blk_w, + 'tablespace' AS +i_prefix, + dt.tablespace_name +FROM((dba_data_files dd JOIN v$filestat vf ON dd.file_id = vf.file#) JOIN dba_tablespaces dt ON dd.tablespace_name = dt.tablespace_name) +GROUP BY dt.tablespace_name; + +-- Buffer Pool Hit Ratio: +SELECT DISTINCT 100 *ROUND(1 -((MAX(decode(name, 'physical reads cache', VALUE))) /(MAX(decode(name, 'db block gets from cache', VALUE)) + MAX(decode(name, 'consistent gets from cache', VALUE)))), 4) AS +VALUE, + 'BUFFER_CACHE_HIT_RATIO' AS +buffer_cache_hit_ratio +FROM v$sysstat; + +-- Shared Pool Hit Ratio: +SELECT + 100.0 * sum(PINHITS) / sum(pins) as VALUE, + 'SHAREDPOOL_HIT_RATIO' AS SHAREDPOOL_HIT_RATIO +FROM V$LIBRARYCACHE; + +-- PGA Hit Ratio: +SELECT VALUE, + 'PGA_HIT_RATIO' AS +pga_hit_ratio +FROM v$pgastat +WHERE name = 'cache hit percentage'; + +-- DB Efficiency +SELECT ROUND(SUM(decode(metric_name, 'Database Wait Time Ratio', VALUE)), 2) AS +database_wait_time_ratio, + ROUND(SUM(decode(metric_name, 'Database CPU Time Ratio', VALUE)), 2) AS +database_cpu_time_ratio, + 'DB_EFFICIENCY' AS +db_efficiency +FROM sys.v_$sysmetric +WHERE metric_name IN('Database CPU Time Ratio', 'Database Wait Time Ratio') + AND intsize_csec = + (SELECT MAX(intsize_csec) + FROM sys.v_$sysmetric); diff --git a/src/Makefile.am b/src/Makefile.am index 47268f51..cf89c9a1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,7 @@ collectd_SOURCES = collectd.c collectd.h \ utils_tail_match.c utils_tail_match.h \ utils_match.c utils_match.h \ utils_mount.c utils_mount.h \ + utils_subst.c utils_subst.h \ utils_tail.c utils_tail.h \ utils_threshold.c utils_threshold.h \ types_list.c types_list.h @@ -184,6 +185,16 @@ collectd_LDADD += "-dlopen" csv.la collectd_DEPENDENCIES += csv.la endif +if BUILD_PLUGIN_DBI +pkglib_LTLIBRARIES += dbi.la +dbi_la_SOURCES = dbi.c +dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS) +dbi_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBDBI_LDFLAGS) +dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS) +collectd_LDADD += "-dlopen" dbi.la +collectd_DEPENDENCIES += dbi.la +endif + if BUILD_PLUGIN_DF pkglib_LTLIBRARIES += df.la df_la_SOURCES = df.c @@ -264,6 +275,24 @@ collectd_LDADD += "-dlopen" filecount.la collectd_DEPENDENCIES += filecount.la endif +if BUILD_PLUGIN_FILTER_IGNORE +pkglib_LTLIBRARIES += filter_ignore.la +filter_ignore_la_SOURCES = filter_ignore.c +filter_ignore_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" filter_ignore.la +collectd_DEPENDENCIES += filter_ignore.la +endif + +if BUILD_PLUGIN_FILTER_PCRE +pkglib_LTLIBRARIES += filter_pcre.la +filter_pcre_la_SOURCES = filter_pcre.c +filter_pcre_la_CPPFLAGS = $(BUILD_WITH_LIBPCRE_CFLAGS) +filter_pcre_la_LDFLAGS = -module -avoid-version \ + $(BUILD_WITH_LIBPCRE_LIBS) +collectd_LDADD += "-dlopen" filter_pcre.la +collectd_DEPENDENCIES += filter_pcre.la +endif + if BUILD_PLUGIN_HDDTEMP pkglib_LTLIBRARIES += hddtemp.la hddtemp_la_SOURCES = hddtemp.c @@ -529,6 +558,17 @@ collectd_LDADD += "-dlopen" onewire.la collectd_DEPENDENCIES += onewire.la endif +if BUILD_PLUGIN_ORACLE +pkglib_LTLIBRARIES += oracle.la +oracle_la_SOURCES = oracle.c +oracle_la_CFLAGS = $(AM_CFLAGS) +oracle_la_CPPFLAGS = $(BUILD_WITH_ORACLE_CFLAGS) +oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS) +oracle_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" oracle.la +collectd_DEPENDENCIES += oracle.la +endif + if BUILD_PLUGIN_PERL pkglib_LTLIBRARIES += perl.la perl_la_SOURCES = perl.c @@ -588,9 +628,19 @@ processes_la_LIBADD += -lkvm endif endif +if BUILD_PLUGIN_RRDCACHED +pkglib_LTLIBRARIES += rrdcached.la +rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h +rrdcached_la_LDFLAGS = -module -avoid-version +rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS) +rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS) +collectd_LDADD += "-dlopen" rrdcached.la +collectd_DEPENDENCIES += rrdcached.la +endif + if BUILD_PLUGIN_RRDTOOL pkglib_LTLIBRARIES += rrdtool.la -rrdtool_la_SOURCES = rrdtool.c +rrdtool_la_SOURCES = rrdtool.c utils_rrdcreate.c utils_rrdcreate.h rrdtool_la_LDFLAGS = -module -avoid-version rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS) rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS) diff --git a/src/collectd.conf.in b/src/collectd.conf.in index eaafff13..28004afa 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -34,6 +34,7 @@ FQDNLookup true @BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu @BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq @BUILD_PLUGIN_CSV_TRUE@LoadPlugin csv +@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi @BUILD_PLUGIN_DF_TRUE@LoadPlugin df @BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk @BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns @@ -41,6 +42,7 @@ FQDNLookup true @BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy @BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec @BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount +@BUILD_PLUGIN_FILTER_PCRE_TRUE@LoadPlugin filter_pcre @BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp @BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface @BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables @@ -68,6 +70,7 @@ FQDNLookup true @BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql @BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns @BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes +@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached @BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool @BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors @BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial @@ -110,6 +113,25 @@ FQDNLookup true # StoreRates false # +# +# +# Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl" +# Type "gauge" +# InstancesFrom "c_key" +# ValuesFrom "c_value" +# +# +# Driver "mysql" +# DriverOption "host" "localhost" +# DriverOption "username" "collectd" +# DriverOption "password" "AeXohy0O" +# DriverOption "dbname" "custdb0" +# #SelectDB "custdb0" +# Query "num_of_customers" +# #Query "..." +# +# + # # Device "/dev/hda1" # Device "192.168.0.2:/mnt/nfs" @@ -146,9 +168,27 @@ FQDNLookup true # Name "*.conf" # MTime "-5m" # Size "+10k" +# Recursive true # # +# +# +# Host "^mail\d+$" +# Plugin "^tcpconns$" +# TypeInstance "^SYN_" +# +# Action NoWrite +# +# +# +# Plugin "^sensors$" +# PluginInstance "^Some Weird Sensor Chip Name Prefix" +# +# SubstitutePluginInstance "foo" +# +# + @BUILD_PLUGIN_HDDTEMP_TRUE@ # Host "127.0.0.1" # Port "7634" @@ -318,6 +358,13 @@ FQDNLookup true # Process "name" # +# +# DaemonAddress "unix:/tmp/rrdcached.sock" +# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd" +# CreateFiles true +# CollectStatistics true +# + # # DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd" # CacheTimeout 120 diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 0df5c3b7..c6fc5a3f 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -282,6 +282,161 @@ number. =back +=head2 Plugin C + +This plugin uses the "B" library (L) to +connect to various databases, execute SQL statements and read back the results. +You can configure how each column is to be interpreted and the plugin will +generate one data set from each row returned according to these rules. + +Because the plugin is very generic, the configuration is a little more complex +than those of other plugins. It usually looks something like this: + + + + Statement "SELECT category, COUNT(*) AS value FROM products WHERE in_stock = 0 GROUP BY category" + Type "gauge" + InstancesFrom "category" + ValuesFrom "value" + + + Driver "mysql" + DriverOption "host" "localhost" + DriverOption "username" "collectd" + DriverOption "password" "aZo6daiw" + DriverOption "dbname" "prod_info" + SelectDB "prod_info" + Query "out_of_stock" + + + +The configuration above defines one query and one database. The query is then +linked to the database with the B option I the +BDatabaseE> block. You can have any number of queries and databases +and you can also use the B statement to split up the configuration +file in multiple, smaller files. However, the BQueryE> block I +precede the BDatabaseE> blocks, because the file is interpreted from +top to bottom! + +The following is a complete list of options: + +=head3 B blocks + +Query blocks define SQL statements and how the returned data should be +interpreted. They are identified by the name that is given in the opening line +of the block. Thus the name needs to be unique. Other than that, the name is +not used in collectd. + +=over 4 + +=item B I + +Sets the statement that should be executed on the server. This is B +interpreted by collectd, but simply passed to the database server. Therefore, +the SQL dialect that's used depends on the server collectd is connected to. + +The query has to return at least two columns, one for the instance and one +value. You cannot omit the instance, even if the statement is guaranteed to +always return exactly one line. In that case, you can usually specify something +like this: + + Statement "SELECT \"instance\", COUNT(*) AS value FROM table" + +(That works with MySQL but may not be valid SQL according to the spec. If you +use a more strict database server, you may have to select from a dummy table or +something.) + +=item B I + +The B that's used for each line returned. See L for more +details on how types are defined. In short: A type is a predefined layout of +data and the number of values and type of values has to match the type +definition. + +If you specify "temperature" here, you need exactly one gauge column. If you +specify "if_octets", you will need two counter columns. See the B +setting below. + +=item B I [I ...] + +Specifies the columns whose values will be used to create the "TypeInstance" +for each row. You need to specify at least one column for each query. If you +specify more than one column, the value of all columns will be join together +with the hyphen as separation character. + +The plugin itself does not check whether or not all built instances are +different. It's your responsibility to assure that each is unique. + +=item B I [I ...] + +Names the columns whose content is used as the actual data for the data sets +that are dispatched to the daemon. How many such columns you need is determined +by the B setting above. If you specify too many or not enough columns, +the plugin will complain about that and no data will be submitted to the +daemon. + +The actual data type in the columns is not that important. The plugin will +automatically cast the values to the right type if it know how to do that. So +it should be able to handle integer an floating point types, as well as strings +(if they include a number at the beginning). + +=back + +=head3 B blocks + +Database blocks define a connection to a database and which queries should be +sent to that database. Since the used "dbi" library can handle a wide variety +of databases, the configuration is very generic. If in doubt, refer to libdbi's +documentationE- we stick as close to the terminology used there. + +Each database needs a "name" as string argument in the starting tag of the +block. This name will be used as "PluginInstance" in the values submitted to +the daemon. Other than that, that name is not used. + +=over 4 + +=item B I + +Specifies the driver to use to connect to the database. In many cases those +drivers are named after the database they can connect to, but this is not a +technical necessity. These drivers are sometimes referred to as "DBD", +BataBase Briver, and some distributions ship them in separate +packages. Drivers for the "dbi" library are developed by the B +project at L. + +You need to give the driver name as expected by the "dbi" library here. You +should be able to find that in the documentation for each driver. If you +mistype the driver name, the plugin will dump a list of all known driver names +to the log. + +=item B I I + +Sets driver-specific options. What option a driver supports can be found in the +documentation for each driver, somewhere at +L. However, the options "host", +"username", "password", and "dbname" seem to be deEfacto standards. + +Unfortunately, drivers are not too keen to report errors when an unknown option +is passed to them, so invalid settings here may go unnoticed. This is not the +plugin's fault, it will report errors if it gets them from the libraryE/ +the driver. If a driver complains about an option, the plugin will dump a +complete list of all options understood by that driver to the log. + +=item B I + +In some cases, the database name you connect with is not the database name you +want to use for querying data. If this option is set, the plugin will "select" +(switch to) that database after the connection is established. + +=item B I + +Associates the query named I with this database connection. The +query needs to be defined I this statement, i.Ee. all query +blocks you want to refer to must be placed above the database block you want to +refer to them from. + +=back + =head2 Plugin C =over 4 @@ -491,6 +646,101 @@ description see above. Valid multipliers here are C (byte), C (kilobyte), C (megabyte), C (gigabyte), C (terabyte), and C

(petabyte). Please note that there are 1000 bytes in a kilobyte, not 1024. +=item B I|I + +Controls whether or not to recurse into subdirectories. Enabled by default. + +=back + +=head2 Plugin C + +This plugin allows you to filter and rewrite value lists based on +Perl-compatible regular expressions whose syntax and semantics are as close as +possible to those of the Perl 5 language. See L for details. + + + + Host "^mail\d+$" + Plugin "^tcpconns$" + TypeInstance "^SYN_" + + Action NoWrite + + + + Plugin "^sensors$" + PluginInstance "^Some Weird Sensor Chip Name Prefix" + + SubstitutePluginInstance "foo" + + + +The configuration consists of one or more C blocks, each of which +specifies a regular expression identifying a set of value lists and how to +handle successful matches. A value list keeps the values of a single data-set +and is identified by the tuple (host, plugin, plugin instance, type, type +instance). The plugin and type instances are optional components. If they are +missing they are treated as empty strings. Within those blocks, the following +options are recognized: + +=over 4 + +=item B I + +=item B I + +=item B I + +=item B I + +=item B I + +Specifies the regular expression for each component of the identifier. If any +of these options is missing it is interpreted as a pattern which matches any +string. All five components of a value list have to match the appropriate +regular expression to trigger the specified action. + +=item B I|I|I + +Specify how to handle successful matches: + +=over 4 + +=item B + +Do not send the value list to any output (a.k.a. write) plugins. + +=item B + +Skip threshold checking for this value list. + +=item B + +Completely ignore this value list. + +=back + +Two or more actions may be combined by specifying multiple B options. + +=item B I + +=item B I + +=item B I + +=item B I + +=item B I + +Upon a successful match, the matching substring will be replaced by the +specified I text. These options require that an appropriate regex +has been specified before, e.Eg. B requires that the +B option has been specified before. + +B: It is not recommended to modify the type unless you really know what +you are doing. The type is used to identify the data-set definition of the +dispatched values. + =back =head2 Plugin C @@ -1099,6 +1349,65 @@ short: If it works for you: Great! But kaap in mind that the config I change, though this is unlikely. Oh, and if you want to help improving this plugin, just send a short notice to the mailing list. ThanksE:) +=head2 Plugin C + +The "oracle" plugin uses the Oracle® Call Interface (OCI) to connect to an +Oracle® Database and lets you execute SQL statements there. It is very similar +to the "dbi" plugin, because it was written around the same time. See the "dbi" +plugin's documentation above for details. + + + + Statement "SELECT category, COUNT(*) AS value FROM products WHERE in_stock = 0 GROUP BY category" + Type "gauge" + InstancesFrom "category" + ValuesFrom "value" + + + ConnectID "db01" + Username "oracle" + Password "secret" + Query "out_of_stock" + + + +=head3 B blocks + +The Query blocks are handled identically to the Query blocks of the "dbi" +plugin. Please see its documentation above for details on how to specify +queries. + +=head3 B blocks + +Database blocks define a connection to a database and which queries should be +sent to that database. Each database needs a "name" as string argument in the +starting tag of the block. This name will be used as "PluginInstance" in the +values submitted to the daemon. Other than that, that name is not used. + +=over 4 + +=item B I + +Defines the "database alias" or "service name" to connect to. Usually, these +names are defined in the file named C<$ORACLE_HOME/network/admin/tnsnames.ora>. + +=item B I + +Username used for authentication. + +=item B I + +Password used for authentication. + +=item B I + +Associates the query named I with this database connection. The +query needs to be defined I this statement, i.Ee. all query +blocks you want to refer to must be placed above the database block you want to +refer to them from. + +=back + =head2 Plugin C This plugin embeds a Perl-interpreter into collectd and provides an interface @@ -1500,6 +1809,56 @@ and minor and major pagefaults. =back +=head2 Plugin C + +The C plugin uses the RRDTool accelerator daemon, L, +to store values to RRD files in an efficient manner. The combination of the +C B and the C B is very similar to the +way the C plugin works (see below). The added abstraction layer +provides a number of benefits, though: Because the cache is not within +C anymore, it does not need to be flushed when C is to be +restarted. This results in much shorter (if any) gaps in graphs, especially +under heavy load. Also, the C command line utility is aware of the +daemon so that it can flush values to disk automatically when needed. This +allows to integrate automated flushing of values into graphing solutions much +more easily. + +There are disadvantages, though: The daemon may reside on a different host, so +it may not be possible for C to create the appropriate RRD files +anymore. And even if C runs on the same host, it may run in a +different base directory, so relative paths may do weird stuff if you're not +careful. + +So the B is to let C and C run +on the same host, communicating via a UNIX domain socket. The B +setting should be set to an absolute path, so that a changed base directory +does not result in RRD files being createdE/ expected in the wrong place. + +=over 4 + +=item B I

+ +Address of the daemon as understood by the C function of the RRD +library. See L for details. Example: + + + DaemonAddress "unix:/var/run/rrdcached.sock" + + +=item B I + +Set the base directory in which the RRD files reside. If this is a relative +path, it is relative to the working base directory of the C daemon! +Use of an absolute path is recommended. + +=item B B|B + +Enables or disables the creation of RRD files. If the daemon is not running +locally, or B is set to a relative path, this will not work as +expected. Default is B. + +=back + =head2 Plugin C You can use the settings B, B, B, and B to @@ -2085,6 +2444,7 @@ L, L, L, L, +L, L, L, L diff --git a/src/dbi.c b/src/dbi.c new file mode 100644 index 00000000..484c604c --- /dev/null +++ b/src/dbi.c @@ -0,0 +1,1059 @@ +/** + * collectd - src/dbi.c + * Copyright (C) 2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#include + +/* + * Data types + */ +struct cdbi_driver_option_s +{ + char *key; + char *value; +}; +typedef struct cdbi_driver_option_s cdbi_driver_option_t; + +struct cdbi_query_s +{ + char *name; + char *statement; + char *type; + char **instances; + size_t instances_num; + char **values; + size_t values_num; +}; +typedef struct cdbi_query_s cdbi_query_t; + +struct cdbi_database_s +{ + char *name; + char *select_db; + + char *driver; + cdbi_driver_option_t *driver_options; + size_t driver_options_num; + + cdbi_query_t **queries; + size_t queries_num; + + dbi_conn connection; +}; +typedef struct cdbi_database_s cdbi_database_t; + +/* + * Global variables + */ +static cdbi_query_t **queries = NULL; +static size_t queries_num = 0; +static cdbi_database_t **databases = NULL; +static size_t databases_num = 0; + +/* + * Functions + */ +static const char *cdbi_strerror (dbi_conn conn, /* {{{ */ + char *buffer, size_t buffer_size) +{ + const char *msg; + int status; + + if (conn == NULL) + { + sstrncpy (buffer, "connection is NULL", buffer_size); + return (buffer); + } + + msg = NULL; + status = dbi_conn_error (conn, &msg); + if ((status >= 0) && (msg != NULL)) + ssnprintf (buffer, buffer_size, "%s (status %i)", msg, status); + else + ssnprintf (buffer, buffer_size, "dbi_conn_error failed with status %i", + status); + + return (buffer); +} /* }}} const char *cdbi_conn_error */ + +static int cdbi_result_get_field (dbi_result res, /* {{{ */ + const char *name, int dst_type, value_t *ret_value) +{ + value_t value; + unsigned int index; + unsigned short src_type; + dbi_conn connection; + + index = dbi_result_get_field_idx (res, name); + if (index < 1) + { + ERROR ("dbi plugin: cdbi_result_get: No such column: %s.", name); + return (-1); + } + + src_type = dbi_result_get_field_type_idx (res, index); + if (src_type == DBI_TYPE_ERROR) + { + ERROR ("dbi plugin: cdbi_result_get: " + "dbi_result_get_field_type_idx failed."); + return (-1); + } + + if ((dst_type != DS_TYPE_COUNTER) && (dst_type != DS_TYPE_GAUGE)) + { + ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle " + "destination type %i.", dst_type); + return (-1); + } + + if (src_type == DBI_TYPE_INTEGER) + { + if (dst_type == DS_TYPE_COUNTER) + value.counter = dbi_result_get_ulonglong_idx (res, index); + else + value.gauge = (gauge_t) dbi_result_get_longlong_idx (res, index); + } + else if (src_type == DBI_TYPE_DECIMAL) + { + value.gauge = dbi_result_get_double_idx (res, index); + if (dst_type == DS_TYPE_COUNTER) + value.counter = (counter_t) round (value.gauge); + } + else if (src_type == DBI_TYPE_STRING) + { + const char *string = dbi_result_get_string_idx (res, index); + char *endptr = NULL; + + if (string == NULL) + value.gauge = NAN; + else if (dst_type == DS_TYPE_COUNTER) + value.counter = (counter_t) strtoll (string, &endptr, 0); + else + value.gauge = (gauge_t) strtod (string, &endptr); + + if (string == endptr) + { + ERROR ("dbi plugin: cdbi_result_get: Can't parse string as number: %s.", + string); + return (-1); + } + } + else + { + ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle " + "source type %hu.", src_type); + return (-1); + } + + connection = dbi_result_get_conn (res); + if (dbi_conn_error_flag (connection) != 0) + { + char errbuf[1024]; + ERROR ("dbi plugin: cdbi_result_get: dbi_result_get_*_idx failed: %s.", + cdbi_strerror (connection, errbuf, sizeof (errbuf))); + return (-1); + } + + *ret_value = value; + return (0); +} /* }}} int cdbi_result_get_field */ + +static void cdbi_query_free (cdbi_query_t *q) /* {{{ */ +{ + size_t i; + + if (q == NULL) + return; + + sfree (q->name); + sfree (q->statement); + sfree (q->type); + + for (i = 0; i < q->instances_num; i++) + sfree (q->instances[i]); + sfree (q->instances); + + for (i = 0; i < q->values_num; i++) + sfree (q->values[i]); + sfree (q->values); + + sfree (q); +} /* }}} void cdbi_query_free */ + +static void cdbi_database_free (cdbi_database_t *db) /* {{{ */ +{ + size_t i; + + if (db == NULL) + return; + + sfree (db->name); + sfree (db->driver); + + for (i = 0; i < db->driver_options_num; i++) + { + sfree (db->driver_options[i].key); + sfree (db->driver_options[i].value); + } + sfree (db->driver_options); + + sfree (db); +} /* }}} void cdbi_database_free */ + +static void cdbi_submit (cdbi_database_t *db, cdbi_query_t *q, /* {{{ */ + char **instances, value_t *values) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = values; + vl.values_len = (int) q->values_num; + vl.time = time (NULL); + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "dbi", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance)); + sstrncpy (vl.type, q->type, sizeof (vl.type)); + strjoin (vl.type_instance, sizeof (vl.type_instance), + instances, q->instances_num, "-"); + vl.type_instance[sizeof (vl.type_instance) - 1] = 0; + + plugin_dispatch_values (&vl); +} /* }}} void cdbi_submit */ + +/* Configuration handling functions {{{ + * + * + * + * Statement "SELECT name, value FROM table" + * Type "gauge" + * InstancesFrom "name" + * ValuesFrom "value" + * + * + * + * Driver "mysql" + * DriverOption "hostname" "localhost" + * ... + * Query "plugin_instance0" + * + * + */ + +static int cdbi_config_set_string (char **ret_string, /* {{{ */ + oconfig_item_t *ci) +{ + char *string; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("dbi plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + string = strdup (ci->values[0].value.string); + if (string == NULL) + { + ERROR ("dbi plugin: strdup failed."); + return (-1); + } + + if (*ret_string != NULL) + free (*ret_string); + *ret_string = string; + + return (0); +} /* }}} int cdbi_config_set_string */ + +static int cdbi_config_add_string (char ***ret_array, /* {{{ */ + size_t *ret_array_len, oconfig_item_t *ci) +{ + char **array; + size_t array_len; + int i; + + if (ci->values_num < 1) + { + WARNING ("dbi plugin: The `%s' config option " + "needs at least one argument.", ci->key); + return (-1); + } + + for (i = 0; i < ci->values_num; i++) + { + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + WARNING ("dbi plugin: Argument %i to the `%s' option " + "is not a string.", i + 1, ci->key); + return (-1); + } + } + + array_len = *ret_array_len; + array = (char **) realloc (*ret_array, + sizeof (char *) * (array_len + ci->values_num)); + if (array == NULL) + { + ERROR ("dbi plugin: realloc failed."); + return (-1); + } + *ret_array = array; + + for (i = 0; i < ci->values_num; i++) + { + array[array_len] = strdup (ci->values[i].value.string); + if (array[array_len] == NULL) + { + ERROR ("dbi plugin: strdup failed."); + *ret_array_len = array_len; + return (-1); + } + array_len++; + } + + *ret_array_len = array_len; + return (0); +} /* }}} int cdbi_config_add_string */ + +static int cdbi_config_add_query (oconfig_item_t *ci) /* {{{ */ +{ + cdbi_query_t *q; + int status; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("dbi plugin: The `Query' block " + "needs exactly one string argument."); + return (-1); + } + + q = (cdbi_query_t *) malloc (sizeof (*q)); + if (q == NULL) + { + ERROR ("dbi plugin: malloc failed."); + return (-1); + } + memset (q, 0, sizeof (*q)); + + status = cdbi_config_set_string (&q->name, ci); + if (status != 0) + { + sfree (q); + return (status); + } + + /* Fill the `cdbi_query_t' structure.. */ + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Statement", child->key) == 0) + status = cdbi_config_set_string (&q->statement, child); + else if (strcasecmp ("Type", child->key) == 0) + status = cdbi_config_set_string (&q->type, child); + else if (strcasecmp ("InstancesFrom", child->key) == 0) + status = cdbi_config_add_string (&q->instances, &q->instances_num, child); + else if (strcasecmp ("ValuesFrom", child->key) == 0) + status = cdbi_config_add_string (&q->values, &q->values_num, child); + else + { + WARNING ("dbi plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + while (status == 0) + { + if (q->statement == NULL) + { + WARNING ("dbi plugin: `Statement' not given for query `%s'", q->name); + status = -1; + } + if (q->type == NULL) + { + WARNING ("dbi plugin: `Type' not given for query `%s'", q->name); + status = -1; + } + if (q->instances == NULL) + { + WARNING ("dbi plugin: `InstancesFrom' not given for query `%s'", q->name); + status = -1; + } + if (q->values == NULL) + { + WARNING ("dbi plugin: `ValuesFrom' not given for query `%s'", q->name); + status = -1; + } + + break; + } /* while (status == 0) */ + + /* If all went well, add this query to the list of queries within the + * database structure. */ + if (status == 0) + { + cdbi_query_t **temp; + + temp = (cdbi_query_t **) realloc (queries, + sizeof (*queries) * (queries_num + 1)); + if (temp == NULL) + { + ERROR ("dbi plugin: realloc failed"); + status = -1; + } + else + { + queries = temp; + queries[queries_num] = q; + queries_num++; + } + } + + if (status != 0) + { + cdbi_query_free (q); + return (-1); + } + + return (0); +} /* }}} int cdbi_config_add_query */ + +static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ */ + oconfig_item_t *ci) +{ + cdbi_driver_option_t *option; + + if ((ci->values_num != 2) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || (ci->values[1].type != OCONFIG_TYPE_STRING)) + { + WARNING ("dbi plugin: The `DriverOption' config option " + "needs exactly two string arguments."); + return (-1); + } + + option = (cdbi_driver_option_t *) realloc (db->driver_options, + sizeof (*option) * (db->driver_options_num + 1)); + if (option == NULL) + { + ERROR ("dbi plugin: realloc failed"); + return (-1); + } + + db->driver_options = option; + option = db->driver_options + db->driver_options_num; + + option->key = strdup (ci->values[0].value.string); + if (option->key == NULL) + { + ERROR ("dbi plugin: strdup failed."); + return (-1); + } + + option->value = strdup (ci->values[1].value.string); + if (option->value == NULL) + { + ERROR ("dbi plugin: strdup failed."); + sfree (option->key); + return (-1); + } + + db->driver_options_num++; + return (0); +} /* }}} int cdbi_config_add_database_driver_option */ + +static int cdbi_config_add_database_query (cdbi_database_t *db, /* {{{ */ + oconfig_item_t *ci) +{ + cdbi_query_t *q; + cdbi_query_t **temp; + size_t i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("dbi plugin: The `Query' config option " + "needs exactly one string argument."); + return (-1); + } + + q = NULL; + for (i = 0; i < queries_num; i++) + { + if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0) + { + q = queries[i]; + break; + } + } + + if (q == NULL) + { + WARNING ("dbi plugin: Database `%s': Unknown query `%s'. " + "Please make sure that the block comes before " + "the block.", + db->name, ci->values[0].value.string, + ci->values[0].value.string, db->name); + return (-1); + } + + temp = (cdbi_query_t **) realloc (db->queries, + sizeof (*db->queries) * (db->queries_num + 1)); + if (temp == NULL) + { + ERROR ("dbi plugin: realloc failed"); + return (-1); + } + else + { + db->queries = temp; + db->queries[db->queries_num] = q; + db->queries_num++; + } + + return (0); +} /* }}} int cdbi_config_add_database_query */ + +static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */ +{ + cdbi_database_t *db; + int status; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("dbi plugin: The `Database' block " + "needs exactly one string argument."); + return (-1); + } + + db = (cdbi_database_t *) malloc (sizeof (*db)); + if (db == NULL) + { + ERROR ("dbi plugin: malloc failed."); + return (-1); + } + memset (db, 0, sizeof (*db)); + + status = cdbi_config_set_string (&db->name, ci); + if (status != 0) + { + sfree (db); + return (status); + } + + /* Fill the `cdbi_database_t' structure.. */ + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Driver", child->key) == 0) + status = cdbi_config_set_string (&db->driver, child); + else if (strcasecmp ("DriverOption", child->key) == 0) + status = cdbi_config_add_database_driver_option (db, child); + else if (strcasecmp ("SelectDB", child->key) == 0) + status = cdbi_config_set_string (&db->select_db, child); + else if (strcasecmp ("Query", child->key) == 0) + status = cdbi_config_add_database_query (db, child); + else + { + WARNING ("dbi plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + while (status == 0) + { + if (db->driver == NULL) + { + WARNING ("dbi plugin: `Driver' not given for database `%s'", db->name); + status = -1; + } + if (db->driver_options_num == 0) + { + WARNING ("dbi plugin: No `DriverOption' given for database `%s'. " + "This will likely not work.", db->name); + } + + break; + } /* while (status == 0) */ + + /* If all went well, add this database to the global list of databases. */ + if (status == 0) + { + cdbi_database_t **temp; + + temp = (cdbi_database_t **) realloc (databases, + sizeof (*databases) * (databases_num + 1)); + if (temp == NULL) + { + ERROR ("dbi plugin: realloc failed"); + status = -1; + } + else + { + databases = temp; + databases[databases_num] = db; + databases_num++; + } + } + + if (status != 0) + { + cdbi_database_free (db); + return (-1); + } + + return (0); +} /* }}} int cdbi_config_add_database */ + +static int cdbi_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + if (strcasecmp ("Query", child->key) == 0) + cdbi_config_add_query (child); + else if (strcasecmp ("Database", child->key) == 0) + cdbi_config_add_database (child); + else + { + WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key); + } + } /* for (ci->children) */ + + return (0); +} /* }}} int cdbi_config */ + +/* }}} End of configuration handling functions */ + +static int cdbi_init (void) /* {{{ */ +{ + static int did_init = 0; + int status; + + if (did_init != 0) + return (0); + + if (queries_num == 0) + { + ERROR ("dbi plugin: No blocks have been found. Without them, " + "this plugin can't do anything useful, so we will returns an error."); + return (-1); + } + + if (databases_num == 0) + { + ERROR ("dbi plugin: No blocks have been found. Without them, " + "this plugin can't do anything useful, so we will returns an error."); + return (-1); + } + + status = dbi_initialize (NULL); + if (status < 0) + { + ERROR ("dbi plugin: cdbi_init: dbi_initialize failed with status %i.", + status); + return (-1); + } + else if (status == 0) + { + ERROR ("dbi plugin: `dbi_initialize' could not load any drivers. Please " + "install at least one `DBD' or check your installation."); + return (-1); + } + DEBUG ("dbi plugin: cdbi_init: dbi_initialize reports %i driver%s.", + status, (status == 1) ? "" : "s"); + + return (0); +} /* }}} int cdbi_init */ + +static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */ + cdbi_query_t *q) +{ + dbi_result res; + char **instances; + value_t *values; + const data_set_t *ds; + size_t i; + int status; + + res = NULL; + instances = NULL; + values = NULL; + + /* Macro that cleans up dynamically allocated memory and returns the + * specified status. */ +#define BAIL_OUT(status) \ + if (res != NULL) { dbi_result_free (res); res = NULL; } \ + if (instances != NULL) { sfree (instances[0]); sfree (instances); } \ + sfree (values); \ + return (status) + + ds = plugin_get_ds (q->type); + if (ds == NULL) + { + ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': Type `%s' is not " + "known by the daemon. See types.db(5) for details.", + q->name, q->type); + BAIL_OUT (-1); + } + + if (((size_t) ds->ds_num) != q->values_num) + { + ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': The type `%s' " + "requires exactly %i value%s, but the configuration specifies %zu.", + q->name, q->type, + ds->ds_num, (ds->ds_num == 1) ? "" : "s", + q->values_num); + BAIL_OUT (-1); + } + + /* Allocate `instances' and `values' {{{ */ + instances = (char **) malloc (sizeof (*instances) * q->instances_num); + if (instances == NULL) + { + ERROR ("dbi plugin: malloc failed."); + BAIL_OUT (-1); + } + + instances[0] = (char *) malloc (q->instances_num * DATA_MAX_NAME_LEN); + if (instances[0] == NULL) + { + ERROR ("dbi plugin: malloc failed."); + BAIL_OUT (-1); + } + for (i = 1; i < q->instances_num; i++) + instances[i] = instances[i - 1] + DATA_MAX_NAME_LEN; + + values = (value_t *) malloc (sizeof (*values) * q->values_num); + if (values == NULL) + { + ERROR ("dbi plugin: malloc failed."); + BAIL_OUT (-1); + } + /* }}} */ + + res = dbi_conn_query (db->connection, q->statement); + if (res == NULL) + { + char errbuf[1024]; + ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " + "dbi_conn_query failed: %s", + db->name, q->name, + cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); + BAIL_OUT (-1); + } + + status = dbi_result_first_row (res); + if (status != 1) + { + char errbuf[1024]; + ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " + "dbi_result_first_row failed: %s. Maybe the statement didn't " + "return any rows?", + db->name, q->name, + cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); + BAIL_OUT (-1); + } + + while (42) + { + /* Get instance names and values from the result: */ + for (i = 0; i < q->instances_num; i++) /* {{{ */ + { + const char *inst; + + inst = dbi_result_get_string (res, q->instances[i]); + if (dbi_conn_error_flag (db->connection) != 0) + { + char errbuf[1024]; + ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " + "dbi_result_get_string (%s) failed: %s", + db->name, q->name, q->instances[i], + cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); + BAIL_OUT (-1); + } + + sstrncpy (instances[i], (inst == NULL) ? "" : inst, DATA_MAX_NAME_LEN); + DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): " + "instances[%zu] = %s;", + db->name, q->name, i, instances[i]); + } /* }}} for (i = 0; i < q->instances_num; i++) */ + + for (i = 0; i < q->values_num; i++) /* {{{ */ + { + status = cdbi_result_get_field (res, q->values[i], ds->ds[i].type, + values + i); + if (status != 0) + { + BAIL_OUT (-1); + } + + if (ds->ds[i].type == DS_TYPE_COUNTER) + { + DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %llu;", + db->name, q->name, i, values[i].counter); + } + else + { + DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %g;", + db->name, q->name, i, values[i].gauge); + } + } /* }}} for (i = 0; i < q->values_num; i++) */ + + /* Dispatch this row to the daemon. */ + cdbi_submit (db, q, instances, values); + + /* Get the next row from the database. */ + status = dbi_result_next_row (res); + if (status != 1) + { + if (dbi_conn_error_flag (db->connection) != 0) + { + char errbuf[1024]; + WARNING ("dbi plugin: cdbi_read_database_query (%s, %s): " + "dbi_result_next_row failed: %s.", + db->name, q->name, + cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); + } + break; + } + } /* while (42) */ + + BAIL_OUT (0); +#undef BAIL_OUT +} /* }}} int cdbi_read_database_query */ + +static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */ +{ + dbi_driver driver; + dbi_conn connection; + size_t i; + int status; + + if (db->connection != NULL) + { + status = dbi_conn_ping (db->connection); + if (status != 0) /* connection is alive */ + return (0); + + dbi_conn_close (db->connection); + db->connection = NULL; + } + + driver = dbi_driver_open (db->driver); + if (driver == NULL) + { + ERROR ("dbi plugin: cdbi_connect_database: dbi_driver_open (%s) failed.", + db->driver); + INFO ("dbi plugin: Maybe the driver isn't installed? " + "Known drivers are:"); + for (driver = dbi_driver_list (NULL); + driver != NULL; + driver = dbi_driver_list (driver)) + { + INFO ("dbi plugin: * %s", dbi_driver_get_name (driver)); + } + return (-1); + } + + connection = dbi_conn_open (driver); + if (connection == NULL) + { + ERROR ("dbi plugin: cdbi_connect_database: dbi_conn_open (%s) failed.", + db->driver); + return (-1); + } + + /* Set all the driver options. Because this is a very very very generic + * interface, the error handling is kind of long. If an invalid option is + * encountered, it will get a list of options understood by the driver and + * report that as `INFO'. This way, users hopefully don't have too much + * trouble finding out how to configure the plugin correctly.. */ + for (i = 0; i < db->driver_options_num; i++) + { + DEBUG ("dbi plugin: cdbi_connect_database (%s): " + "key = %s; value = %s;", + db->name, + db->driver_options[i].key, + db->driver_options[i].value); + + status = dbi_conn_set_option (connection, + db->driver_options[i].key, db->driver_options[i].value); + if (status != 0) + { + char errbuf[1024]; + const char *opt; + + ERROR ("dbi plugin: cdbi_connect_database (%s): " + "dbi_conn_set_option (%s, %s) failed: %s.", + db->name, + db->driver_options[i].key, db->driver_options[i].value, + cdbi_strerror (connection, errbuf, sizeof (errbuf))); + + INFO ("dbi plugin: This is a list of all options understood " + "by the `%s' driver:", db->driver); + for (opt = dbi_conn_get_option_list (connection, NULL); + opt != NULL; + opt = dbi_conn_get_option_list (connection, opt)) + { + INFO ("dbi plugin: * %s", opt); + } + + dbi_conn_close (connection); + return (-1); + } + } /* for (i = 0; i < db->driver_options_num; i++) */ + + status = dbi_conn_connect (connection); + if (status != 0) + { + char errbuf[1024]; + ERROR ("dbi plugin: cdbi_connect_database (%s): " + "dbi_conn_connect failed: %s", + db->name, cdbi_strerror (connection, errbuf, sizeof (errbuf))); + dbi_conn_close (connection); + return (-1); + } + + if (db->select_db != NULL) + { + status = dbi_conn_select_db (connection, db->select_db); + if (status != 0) + { + char errbuf[1024]; + WARNING ("dbi plugin: cdbi_connect_database (%s): " + "dbi_conn_select_db (%s) failed: %s. Check the `SelectDB' option.", + db->name, db->select_db, + cdbi_strerror (connection, errbuf, sizeof (errbuf))); + dbi_conn_close (connection); + return (-1); + } + } + + db->connection = connection; + return (0); +} /* }}} int cdbi_connect_database */ + +static int cdbi_read_database (cdbi_database_t *db) /* {{{ */ +{ + size_t i; + int success; + int status; + + status = cdbi_connect_database (db); + if (status != 0) + return (status); + assert (db->connection != NULL); + + success = 0; + for (i = 0; i < db->queries_num; i++) + { + status = cdbi_read_database_query (db, db->queries[i]); + if (status == 0) + success++; + } + + if (success == 0) + { + ERROR ("dbi plugin: All queries failed for database `%s'.", db->name); + return (-1); + } + + return (0); +} /* }}} int cdbi_read_database */ + +static int cdbi_read (void) /* {{{ */ +{ + size_t i; + int success = 0; + int status; + + for (i = 0; i < databases_num; i++) + { + status = cdbi_read_database (databases[i]); + if (status == 0) + success++; + } + + if (success == 0) + { + ERROR ("dbi plugin: No database could be read. Will return an error so " + "the plugin will be delayed."); + return (-1); + } + + return (0); +} /* }}} int cdbi_read */ + +static int cdbi_shutdown (void) /* {{{ */ +{ + size_t i; + + for (i = 0; i < databases_num; i++) + { + if (databases[i]->connection != NULL) + { + dbi_conn_close (databases[i]->connection); + databases[i]->connection = NULL; + } + cdbi_database_free (databases[i]); + } + sfree (databases); + databases_num = 0; + + for (i = 0; i < queries_num; i++) + cdbi_query_free (queries[i]); + sfree (queries); + queries_num = 0; + + return (0); +} /* }}} int cdbi_shutdown */ + +void module_register (void) /* {{{ */ +{ + plugin_register_complex_config ("dbi", cdbi_config); + plugin_register_init ("dbi", cdbi_init); + plugin_register_read ("dbi", cdbi_read); + plugin_register_shutdown ("dbi", cdbi_shutdown); +} /* }}} void module_register */ + +/* + * vim: shiftwidth=2 softtabstop=2 et fdm=marker + */ diff --git a/src/filecount.c b/src/filecount.c index f0713795..e2b00207 100644 --- a/src/filecount.c +++ b/src/filecount.c @@ -31,11 +31,15 @@ #include #include +#define FC_RECURSIVE 1 + struct fc_directory_conf_s { char *path; char *instance; + int options; + /* Data counters */ uint64_t files_num; uint64_t files_size; @@ -307,6 +311,25 @@ static int fc_config_add_dir_size (fc_directory_conf_t *dir, return (0); } /* int fc_config_add_dir_size */ +static int fc_config_add_dir_recursive (fc_directory_conf_t *dir, + oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) + { + WARNING ("filecount plugin: The `Recursive' config options needs exactly " + "one boolean argument."); + return (-1); + } + + if (ci->values[0].value.boolean) + dir->options |= FC_RECURSIVE; + else + dir->options &= ~FC_RECURSIVE; + + return (0); +} /* int fc_config_add_dir_recursive */ + static int fc_config_add_dir (oconfig_item_t *ci) { fc_directory_conf_t *dir; @@ -338,6 +361,8 @@ static int fc_config_add_dir (oconfig_item_t *ci) fc_config_set_instance (dir, dir->path); + dir->options = FC_RECURSIVE; + dir->name = NULL; dir->mtime = 0; dir->size = 0; @@ -355,6 +380,8 @@ static int fc_config_add_dir (oconfig_item_t *ci) status = fc_config_add_dir_mtime (dir, option); else if (strcasecmp ("Size", option->key) == 0) status = fc_config_add_dir_size (dir, option); + else if (strcasecmp ("Recursive", option->key) == 0) + status = fc_config_add_dir_recursive (dir, option); else { WARNING ("filecount plugin: fc_config_add_dir: " @@ -447,7 +474,7 @@ static int fc_read_dir_callback (const char *dirname, const char *filename, return (-1); } - if (S_ISDIR (statbuf.st_mode)) + if (S_ISDIR (statbuf.st_mode) && (dir->options & FC_RECURSIVE)) { status = walk_directory (abs_path, fc_read_dir_callback, dir); return (status); diff --git a/src/filter_ignore.c b/src/filter_ignore.c new file mode 100644 index 00000000..19588842 --- /dev/null +++ b/src/filter_ignore.c @@ -0,0 +1,147 @@ +/** + * collectd - src/entropy.c + * Copyright (C) 2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_ignorelist.h" + +/* + * Variables + */ +static ignorelist_t *il_host = NULL; +static ignorelist_t *il_plugin = NULL; +static ignorelist_t *il_type = NULL; + +static const char *config_keys[] = +{ + "IgnoreHost", + "IgnorePlugin", + "IgnoreType" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +/* + * Functions + */ +static int ignorelist_add_create (ignorelist_t **il_ptr, const char *entry) +{ + ignorelist_t *il; + int status; + + il = *il_ptr; + + if (il == NULL) + { + il = ignorelist_create (/* ignore = */ 0); + if (il == NULL) + { + ERROR ("filter_ignore plugin: ignorelist_create failed."); + return (-1); + } + *il_ptr = il; + } + + status = ignorelist_add (il, entry); + if (status != 0) + { + ERROR ("filter_ignore plugin: ignorelist_add failed with error %i.", + status); + return (status); + } + + return (0); +} /* int ignorelist_add_create */ + +static int fi_config (const char *key, const char *value) +{ + int status; + + status = 0; + + if (strcasecmp ("IgnoreHost", key) == 0) + status = ignorelist_add_create (&il_host, value); + else if (strcasecmp ("IgnorePlugin", key) == 0) + status = ignorelist_add_create (&il_plugin, value); + else if (strcasecmp ("IgnoreType", key) == 0) + status = ignorelist_add_create (&il_type, value); + else + return (-1); + + if (status < 0) + status = status * (-1); + + return (status); +} /* int fi_config */ + +static int fi_filter (const data_set_t *ds, value_list_t *vl) +{ + int status; + + if (il_host != NULL) + { + status = ignorelist_match (il_host, vl->host); + if (status != 0) + return (FILTER_IGNORE); + } + + if (il_plugin != NULL) + { + char buffer[2 * DATA_MAX_NAME_LEN]; + + if (vl->plugin_instance[0] == 0) + sstrncpy (buffer, vl->plugin, sizeof (buffer)); + else + ssnprintf (buffer, sizeof (buffer), "%s-%s", + vl->plugin, vl->plugin_instance); + + status = ignorelist_match (il_plugin, buffer); + if (status != 0) + return (FILTER_IGNORE); + } + + if (il_type != NULL) + { + char buffer[2 * DATA_MAX_NAME_LEN]; + + if (vl->type_instance[0] == 0) + sstrncpy (buffer, vl->type, sizeof (buffer)); + else + ssnprintf (buffer, sizeof (buffer), "%s-%s", + vl->type, vl->type_instance); + + status = ignorelist_match (il_type, buffer); + if (status != 0) + return (FILTER_IGNORE); + } + + return (0); +} /* int fi_filter */ + +void module_register (void) +{ + plugin_register_config ("filter_ignore", fi_config, + config_keys, config_keys_num); + plugin_register_filter ("filter_ignore", fi_filter); +} /* void module_register */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/filter_pcre.c b/src/filter_pcre.c new file mode 100644 index 00000000..7b223e7c --- /dev/null +++ b/src/filter_pcre.c @@ -0,0 +1,426 @@ +/** + * collectd - src/filter_pcre.c + * Copyright (C) 2008 Sebastian Harl + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Sebastian Harl + **/ + +/* + * This module allows to filter and rewrite value lists based on + * Perl-compatible regular expressions. + */ + +#include "collectd.h" +#include "configfile.h" +#include "plugin.h" +#include "common.h" + +#include "utils_subst.h" + +#include + +#define log_err(...) ERROR ("filter_pcre: " __VA_ARGS__) +#define log_warn(...) WARNING ("filter_pcre: " __VA_ARGS__) + +/* + * private data types + */ + +typedef struct { + /* regular expression */ + pcre *re; + const char *re_str; + + /* extra information from studying the pattern */ + pcre_extra *extra; + + /* replacment text for string substitution */ + const char *replacement; +} c_pcre_t; + +#define C_PCRE_INIT(regex) do { \ + (regex).re = NULL; \ + (regex).re_str = NULL; \ + (regex).extra = NULL; \ + (regex).replacement = NULL; \ + } while (0) + +#define C_PCRE_FREE(regex) do { \ + pcre_free ((regex).re); \ + free ((void *)(regex).re_str); \ + pcre_free ((regex).extra); \ + free ((void *)(regex).replacement); \ + C_PCRE_INIT (regex); \ + } while (0) + +typedef struct { + c_pcre_t host; + c_pcre_t plugin; + c_pcre_t plugin_instance; + c_pcre_t type; + c_pcre_t type_instance; + + int action; +} regex_t; + +typedef struct { + int vec[30]; + int status; +} ovec_t; + +typedef struct { + ovec_t host; + ovec_t plugin; + ovec_t plugin_instance; + ovec_t type; + ovec_t type_instance; +} ovectors_t; + +/* + * private variables + */ + +static regex_t *regexes = NULL; +static int regexes_num = 0; + +/* + * internal helper functions + */ + +/* returns true if string matches the regular expression */ +static int c_pcre_match (c_pcre_t *re, const char *string, ovec_t *ovec) +{ + if ((NULL == re) || (NULL == re->re)) + return 1; + + if (NULL == string) + string = ""; + + ovec->status = pcre_exec (re->re, + /* extra = */ re->extra, + /* subject = */ string, + /* length = */ strlen (string), + /* startoffset = */ 0, + /* options = */ 0, + /* ovector = */ ovec->vec, + /* ovecsize = */ STATIC_ARRAY_SIZE (ovec->vec)); + + if (0 <= ovec->status) + return 1; + + if (PCRE_ERROR_NOMATCH != ovec->status) + log_err ("PCRE matching of string \"%s\" failed with status %d", + string, ovec->status); + return 0; +} /* c_pcre_match */ + +static int c_pcre_subst (c_pcre_t *re, char *string, size_t strlen, + ovec_t *ovec) +{ + char buffer[strlen]; + + if ((NULL == re) || (NULL == re->replacement)) + return 0; + + assert (0 <= ovec->status); + + if (NULL == subst (buffer, sizeof (buffer), string, + ovec->vec[0], ovec->vec[1], re->replacement)) { + log_err ("Substitution in string \"%s\" (using regex \"%s\" and " + "replacement string \"%s\") failed.", + string, re->re_str, re->replacement); + return -1; + } + + sstrncpy (string, buffer, strlen); + return 0; +} /* c_pcre_subst */ + +static regex_t *regex_new (void) +{ + regex_t *re; + + ++regexes_num; + regexes = (regex_t *)realloc (regexes, regexes_num * sizeof (*regexes)); + if (NULL == regexes) { + log_err ("Out of memory."); + exit (5); + } + + re = regexes + (regexes_num - 1); + + C_PCRE_INIT (re->host); + C_PCRE_INIT (re->plugin); + C_PCRE_INIT (re->plugin_instance); + C_PCRE_INIT (re->type); + C_PCRE_INIT (re->type_instance); + + re->action = 0; + return re; +} /* regex_new */ + +static void regex_delete (regex_t *re) +{ + if (NULL == re) + return; + + C_PCRE_FREE (re->host); + C_PCRE_FREE (re->plugin); + C_PCRE_FREE (re->plugin_instance); + C_PCRE_FREE (re->type); + C_PCRE_FREE (re->type_instance); + + re->action = 0; +} /* regex_delete */ + +/* returns true if the value list matches the regular expression */ +static int regex_match (regex_t *re, value_list_t *vl, ovectors_t *ovectors) +{ + int matches = 0; + + if (NULL == re) + return 1; + + if (c_pcre_match (&re->host, vl->host, &ovectors->host)) + ++matches; + + if (c_pcre_match (&re->plugin, vl->plugin, &ovectors->plugin)) + ++matches; + + if (c_pcre_match (&re->plugin_instance, vl->plugin_instance, + &ovectors->plugin_instance)) + ++matches; + + if (c_pcre_match (&re->type, vl->type, &ovectors->type)) + ++matches; + + if (c_pcre_match (&re->type_instance, vl->type_instance, + &ovectors->type_instance)) + ++matches; + + if (5 == matches) + return 1; + return 0; +} /* regex_match */ + +static int regex_subst (regex_t *re, value_list_t *vl, ovectors_t *ovectors) +{ + if (NULL == re) + return 0; + + c_pcre_subst (&re->host, vl->host, sizeof (vl->host), + &ovectors->host); + c_pcre_subst (&re->plugin, vl->plugin, sizeof (vl->plugin), + &ovectors->plugin); + c_pcre_subst (&re->plugin_instance, vl->plugin_instance, + sizeof (vl->plugin_instance), &ovectors->plugin_instance); + c_pcre_subst (&re->type, vl->type, sizeof (vl->type), + &ovectors->type); + c_pcre_subst (&re->type_instance, vl->type_instance, + sizeof (vl->type_instance), &ovectors->type_instance); + return 0; +} /* regex_subst */ + +/* + * interface to collectd + */ + +static int c_pcre_filter (const data_set_t *ds, value_list_t *vl) +{ + int i; + + ovectors_t ovectors; + + for (i = 0; i < regexes_num; ++i) + if (regex_match (regexes + i, vl, &ovectors)) { + regex_subst (regexes + i, vl, &ovectors); + return regexes[i].action; + } + return 0; +} /* c_pcre_filter */ + +static int c_pcre_shutdown (void) +{ + int i; + + plugin_unregister_filter ("filter_pcre"); + plugin_unregister_shutdown ("filter_pcre"); + + for (i = 0; i < regexes_num; ++i) + regex_delete (regexes + i); + + sfree (regexes); + regexes_num = 0; + return 0; +} /* c_pcre_shutdown */ + +static int config_set_regex (c_pcre_t *re, oconfig_item_t *ci) +{ + const char *pattern; + const char *errptr; + int erroffset; + + if ((0 != ci->children_num) || (1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err (": %s expects a single string argument.", ci->key); + return 1; + } + + pattern = ci->values[0].value.string; + + re->re = pcre_compile (pattern, + /* options = */ 0, + /* errptr = */ &errptr, + /* erroffset = */ &erroffset, + /* tableptr = */ NULL); + + if (NULL == re->re) { + log_err (": PCRE compilation of pattern \"%s\" failed " + "at offset %d: %s", pattern, erroffset, errptr); + return 1; + } + + re->re_str = sstrdup (pattern); + + re->extra = pcre_study (re->re, + /* options = */ 0, + /* errptr = */ &errptr); + + if (NULL != errptr) { + log_err (": PCRE studying of pattern \"%s\" failed: %s", + pattern, errptr); + return 1; + } + return 0; +} /* config_set_regex */ + +static int config_set_replacement (c_pcre_t *re, oconfig_item_t *ci) +{ + if ((0 != ci->children_num) || (1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err (": %s expects a single string argument.", ci->key); + return 1; + } + + if (NULL == re->re) { + log_err (": %s without an appropriate regex (%s) " + "is not allowed.", ci->key, ci->key + strlen ("Substitute")); + return 1; + } + + re->replacement = sstrdup (ci->values[0].value.string); + return 0; +} /* config_set_replacement */ + +static int config_set_action (int *action, oconfig_item_t *ci) +{ + const char *action_str; + + if ((0 != ci->children_num) || (1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err (": Action expects a single string argument."); + return 1; + } + + action_str = ci->values[0].value.string; + + if (0 == strcasecmp (action_str, "NoWrite")) + *action |= FILTER_NOWRITE; + else if (0 == strcasecmp (action_str, "NoThresholdCheck")) + *action |= FILTER_NOTHRESHOLD_CHECK; + else if (0 == strcasecmp (action_str, "Ignore")) + *action |= FILTER_IGNORE; + else + log_warn (": Ignoring unknown action \"%s\".", action_str); + return 0; +} /* config_set_action */ + +static int c_pcre_config_regex (oconfig_item_t *ci) +{ + regex_t *re; + int i; + + if (0 != ci->values_num) { + log_err (" expects no arguments."); + return 1; + } + + re = regex_new (); + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + int status = 0; + + if (0 == strcasecmp (c->key, "Host")) + status = config_set_regex (&re->host, c); + else if (0 == strcasecmp (c->key, "Plugin")) + status = config_set_regex (&re->plugin, c); + else if (0 == strcasecmp (c->key, "PluginInstance")) + status = config_set_regex (&re->plugin_instance, c); + else if (0 == strcasecmp (c->key, "Type")) + status = config_set_regex (&re->type, c); + else if (0 == strcasecmp (c->key, "TypeInstance")) + status = config_set_regex (&re->type_instance, c); + else if (0 == strcasecmp (c->key, "Action")) + status = config_set_action (&re->action, c); + else if (0 == strcasecmp (c->key, "SubstituteHost")) + status = config_set_replacement (&re->host, c); + else if (0 == strcasecmp (c->key, "SubstitutePlugin")) + status = config_set_replacement (&re->plugin, c); + else if (0 == strcasecmp (c->key, "SubstitutePluginInstance")) + status = config_set_replacement (&re->plugin_instance, c); + else if (0 == strcasecmp (c->key, "SubstituteType")) + status = config_set_replacement (&re->type, c); + else if (0 == strcasecmp (c->key, "SubstituteTypeInstance")) + status = config_set_replacement (&re->type_instance, c); + else + log_warn (": Ignoring unknown config key \"%s\".", c->key); + + if (0 != status) { + log_err ("Ignoring regular expression definition."); + regex_delete (re); + --regexes_num; + } + } + return 0; +} /* c_pcre_config_regex */ + +static int c_pcre_config (oconfig_item_t *ci) +{ + int i; + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "RegEx")) + c_pcre_config_regex (c); + else + log_warn ("Ignoring unknown config key \"%s\".", c->key); + } + + plugin_register_filter ("filter_pcre", c_pcre_filter); + plugin_register_shutdown ("filter_pcre", c_pcre_shutdown); + return 0; +} /* c_pcre_config */ + +void module_register (void) +{ + plugin_register_complex_config ("filter_pcre", c_pcre_config); +} /* module_register */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ + diff --git a/src/interface.c b/src/interface.c index 806a336d..7512c4ee 100644 --- a/src/interface.c +++ b/src/interface.c @@ -24,6 +24,7 @@ #include "common.h" #include "plugin.h" #include "configfile.h" +#include "utils_ignorelist.h" #if HAVE_SYS_TYPES_H # include @@ -77,14 +78,7 @@ static const char *config_keys[] = }; static int config_keys_num = 2; -static char **if_list = NULL; -static int if_list_num = 0; -/* - * if_list_action: - * 0 => default is to collect selected interface - * 1 => ignore selcted interfaces - */ -static int if_list_action = 0; +static ignorelist_t *ignorelist = NULL; #ifdef HAVE_LIBKSTAT #define MAX_NUMIF 256 @@ -95,33 +89,21 @@ static int numif = 0; static int interface_config (const char *key, const char *value) { - char **temp; + if (ignorelist == NULL) + ignorelist = ignorelist_create (/* invert = */ 1); if (strcasecmp (key, "Interface") == 0) { - temp = (char **) realloc (if_list, (if_list_num + 1) * sizeof (char *)); - if (temp == NULL) - { - ERROR ("Cannot allocate more memory."); - return (1); - } - if_list = temp; - - if ((if_list[if_list_num] = strdup (value)) == NULL) - { - ERROR ("Cannot allocate memory."); - return (1); - } - if_list_num++; + ignorelist_add (ignorelist, value); } else if (strcasecmp (key, "IgnoreSelected") == 0) { + int invert = 1; if ((strcasecmp (value, "True") == 0) || (strcasecmp (value, "Yes") == 0) || (strcasecmp (value, "On") == 0)) - if_list_action = 1; - else - if_list_action = 0; + invert = 0; + ignorelist_set_invert (ignorelist, invert); } else { @@ -161,26 +143,6 @@ static int interface_init (void) } /* int interface_init */ #endif /* HAVE_LIBKSTAT */ -/* - * Check if this interface/instance should be ignored. This is called from - * both, `submit' and `write' to give client and server the ability to - * ignore certain stuff.. - */ -static int check_ignore_if (const char *interface) -{ - int i; - - /* If no interfaces are given collect all interfaces. Mostly to be - * backwards compatible, but also because this is much easier. */ - if (if_list_num < 1) - return (0); - - for (i = 0; i < if_list_num; i++) - if (strcasecmp (interface, if_list[i]) == 0) - return (if_list_action); - return (1 - if_list_action); -} /* int check_ignore_if */ - static void if_submit (const char *dev, const char *type, unsigned long long rx, unsigned long long tx) @@ -188,7 +150,7 @@ static void if_submit (const char *dev, const char *type, value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - if (check_ignore_if (dev)) + if (ignorelist_match (ignorelist, dev) != 0) return; values[0].counter = rx; diff --git a/src/libiptc/Makefile.am b/src/libiptc/Makefile.am index ffa98878..a8ed93cc 100644 --- a/src/libiptc/Makefile.am +++ b/src/libiptc/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = foreign no-dependencies -EXTRA_DIST = libiptc.c +EXTRA_DIST = libiptc.c README.collectd if COMPILER_IS_GCC AM_CFLAGS = -Wall -Werror @@ -8,7 +8,7 @@ endif noinst_LTLIBRARIES = libiptc.la -libiptc_la_CFLAGS = -DIPTABLES_VERSION=\"1.4.0\" -I$(KERNEL_DIR)/include +libiptc_la_CFLAGS = -I$(KERNEL_DIR)/include libiptc_la_SOURCES = libip4tc.c libip6tc.c \ ipt_kernel_headers.h libip6tc.h libiptc.h linux_list.h \ xtables.h libxtc.h diff --git a/src/libiptc/README.collectd b/src/libiptc/README.collectd new file mode 100644 index 00000000..adb53b0b --- /dev/null +++ b/src/libiptc/README.collectd @@ -0,0 +1,25 @@ + libiptc (IPTables Chains) in collectd +======================================= +http://netfilter.org/ +http://collectd.org/ + +About +----- + + This is libiptc taken from the iptables source distribution. As it is not + meant to be a public interface by upstream it is not shipped in some binary + distributions. Thus, collectd ships its own copy as a fall-back. + + The presently available version was imported from iptables 1.4.1.1. + +Changes to the iptables upstream sources: +----------------------------------------- + + * Added copyright headers mentioning the "Netfilter Core Team" as copyright + holder. + + * Changed "libiptc/*" includes to "*". + + * Use the shipped copy of "xtables.h" instead of the one possibly available + on the system. + diff --git a/src/libiptc/libip6tc.h b/src/libiptc/libip6tc.h index 103267b7..9253e11f 100644 --- a/src/libiptc/libip6tc.h +++ b/src/libiptc/libip6tc.h @@ -162,7 +162,7 @@ int ip6tc_set_counter(const ip6t_chainlabel chain, int ip6tc_commit(ip6tc_handle_t *handle); /* Get raw socket. */ -int ip6tc_get_raw_socket(); +int ip6tc_get_raw_socket(void); /* Translates errno numbers into more human-readable form than strerror. */ const char *ip6tc_strerror(int err); @@ -170,4 +170,6 @@ const char *ip6tc_strerror(int err); /* Return prefix length, or -1 if not contiguous */ int ipv6_prefix_length(const struct in6_addr *a); +extern void dump_entries6(const ip6tc_handle_t); + #endif /* _LIBIP6TC_H */ diff --git a/src/libiptc/libiptc.c b/src/libiptc/libiptc.c index f8e88b28..f7a6640f 100644 --- a/src/libiptc/libiptc.c +++ b/src/libiptc/libiptc.c @@ -44,6 +44,7 @@ */ #include #include +#include "xtables.h" #include "linux_list.h" @@ -58,8 +59,10 @@ #define DEBUGP_C(x, args...) #endif -#ifndef IPT_LIB_DIR -#define IPT_LIB_DIR "/usr/local/lib/iptables" +#ifdef DEBUG +#define debug(x, args...) fprintf(stderr, x, ## args) +#else +#define debug(x, args...) #endif static int sockfd = -1; @@ -150,6 +153,11 @@ STRUCT_TC_HANDLE struct chain_head *chain_iterator_cur; struct rule_head *rule_iterator_cur; + unsigned int num_chains; /* number of user defined chains */ + + struct chain_head **chain_index; /* array for fast chain list access*/ + unsigned int chain_index_sz;/* size of chain index array */ + STRUCT_GETINFO info; STRUCT_GET_ENTRIES *entries; }; @@ -184,7 +192,7 @@ static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int siz } /* notify us that the ruleset has been modified by the user */ -static void +static inline void set_changed(TC_HANDLE_T h) { h->changed = 1; @@ -282,11 +290,307 @@ iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h) /********************************************************************** + * Chain index (cache utility) functions + ********************************************************************** + * The chain index is an array with pointers into the chain list, with + * CHAIN_INDEX_BUCKET_LEN spacing. This facilitates the ability to + * speedup chain list searching, by find a more optimal starting + * points when searching the linked list. + * + * The starting point can be found fast by using a binary search of + * the chain index. Thus, reducing the previous search complexity of + * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN. + * + * A nice property of the chain index, is that the "bucket" list + * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will + * change this). Oppose to hashing, where the "bucket" list length can + * vary a lot. + */ +#ifndef CHAIN_INDEX_BUCKET_LEN +#define CHAIN_INDEX_BUCKET_LEN 40 +#endif + +/* Another nice property of the chain index is that inserting/creating + * chains in chain list don't change the correctness of the chain + * index, it only causes longer lists in the buckets. + * + * To mitigate the performance penalty of longer bucket lists and the + * penalty of rebuilding, the chain index is rebuild only when + * CHAIN_INDEX_INSERT_MAX chains has been added. + */ +#ifndef CHAIN_INDEX_INSERT_MAX +#define CHAIN_INDEX_INSERT_MAX 355 +#endif + +static inline unsigned int iptcc_is_builtin(struct chain_head *c); + + +/* Use binary search in the chain index array, to find a chain_head + * pointer closest to the place of the searched name element. + * + * Notes that, binary search (obviously) requires that the chain list + * is sorted by name. + */ +static struct list_head * +iptcc_bsearch_chain_index(const char *name, unsigned int *idx, TC_HANDLE_T handle) +{ + unsigned int pos, end; + int res; + + struct list_head *list_pos; + list_pos=&handle->chains; + + /* Check for empty array, e.g. no user defined chains */ + if (handle->chain_index_sz == 0) { + debug("WARNING: handle->chain_index_sz == 0\n"); + return list_pos; + } + + /* Init */ + end = handle->chain_index_sz; + pos = end / 2; + + debug("bsearch Find chain:%s (pos:%d end:%d)\n", name, pos, end); + + /* Loop */ + loop: + if (!handle->chain_index[pos]) { + fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos); + return &handle->chains; /* Be safe, return orig start pos */ + } + + res = strcmp(name, handle->chain_index[pos]->name); + list_pos = &handle->chain_index[pos]->list; + *idx = pos; + + debug("bsearch Index[%d] name:%s res:%d ", + pos, handle->chain_index[pos]->name, res); + + if (res == 0) { /* Found element, by direct hit */ + debug("[found] Direct hit pos:%d end:%d\n", pos, end); + return list_pos; + } else if (res < 0) { /* Too far, jump back */ + end = pos; + pos = pos / 2; + + /* Exit case: First element of array */ + if (end == 0) { + debug("[found] Reached first array elem (end%d)\n",end); + return list_pos; + } + debug("jump back to pos:%d (end:%d)\n", pos, end); + goto loop; + } else if (res > 0 ){ /* Not far enough, jump forward */ + + /* Exit case: Last element of array */ + if (pos == handle->chain_index_sz-1) { + debug("[found] Last array elem (end:%d)\n", end); + return list_pos; + } + + /* Exit case: Next index less, thus elem in this list section */ + res = strcmp(name, handle->chain_index[pos+1]->name); + if (res < 0) { + debug("[found] closest list (end:%d)\n", end); + return list_pos; + } + + pos = (pos+end)/2; + debug("jump forward to pos:%d (end:%d)\n", pos, end); + goto loop; + } + + return list_pos; +} + +#ifdef DEBUG +/* Trivial linear search of chain index. Function used for verifying + the output of bsearch function */ +static struct list_head * +iptcc_linearly_search_chain_index(const char *name, TC_HANDLE_T handle) +{ + unsigned int i=0; + int res=0; + + struct list_head *list_pos; + list_pos = &handle->chains; + + if (handle->chain_index_sz) + list_pos = &handle->chain_index[0]->list; + + /* Linearly walk of chain index array */ + + for (i=0; i < handle->chain_index_sz; i++) { + if (handle->chain_index[i]) { + res = strcmp(handle->chain_index[i]->name, name); + if (res > 0) + break; // One step too far + list_pos = &handle->chain_index[i]->list; + if (res == 0) + break; // Direct hit + } + } + + return list_pos; +} +#endif + +static int iptcc_chain_index_alloc(TC_HANDLE_T h) +{ + unsigned int list_length = CHAIN_INDEX_BUCKET_LEN; + unsigned int array_elems; + unsigned int array_mem; + + /* Allocate memory for the chain index array */ + array_elems = (h->num_chains / list_length) + + (h->num_chains % list_length ? 1 : 0); + array_mem = sizeof(h->chain_index) * array_elems; + + debug("Alloc Chain index, elems:%d mem:%d bytes\n", + array_elems, array_mem); + + h->chain_index = malloc(array_mem); + if (!h->chain_index) { + h->chain_index_sz = 0; + return -ENOMEM; + } + memset(h->chain_index, 0, array_mem); + h->chain_index_sz = array_elems; + + return 1; +} + +static void iptcc_chain_index_free(TC_HANDLE_T h) +{ + h->chain_index_sz = 0; + free(h->chain_index); +} + + +#ifdef DEBUG +static void iptcc_chain_index_dump(TC_HANDLE_T h) +{ + unsigned int i = 0; + + /* Dump: contents of chain index array */ + for (i=0; i < h->chain_index_sz; i++) { + if (h->chain_index[i]) { + fprintf(stderr, "Chain index[%d].name: %s\n", + i, h->chain_index[i]->name); + } + } +} +#endif + +/* Build the chain index */ +static int iptcc_chain_index_build(TC_HANDLE_T h) +{ + unsigned int list_length = CHAIN_INDEX_BUCKET_LEN; + unsigned int chains = 0; + unsigned int cindex = 0; + struct chain_head *c; + + /* Build up the chain index array here */ + debug("Building chain index\n"); + + debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n", + h->num_chains, list_length, h->chain_index_sz); + + if (h->chain_index_sz == 0) + return 0; + + list_for_each_entry(c, &h->chains, list) { + + /* Issue: The index array needs to start after the + * builtin chains, as they are not sorted */ + if (!iptcc_is_builtin(c)) { + cindex=chains / list_length; + + /* Safe guard, break out on array limit, this + * is useful if chains are added and array is + * rebuild, without realloc of memory. */ + if (cindex >= h->chain_index_sz) + break; + + if ((chains % list_length)== 0) { + debug("\nIndex[%d] Chains:", cindex); + h->chain_index[cindex] = c; + } + chains++; + } + debug("%s, ", c->name); + } + debug("\n"); + + return 1; +} + +static int iptcc_chain_index_rebuild(TC_HANDLE_T h) +{ + debug("REBUILD chain index array\n"); + iptcc_chain_index_free(h); + if ((iptcc_chain_index_alloc(h)) < 0) + return -ENOMEM; + iptcc_chain_index_build(h); + return 1; +} + +/* Delete chain (pointer) from index array. Removing an element from + * the chain list only affects the chain index array, if the chain + * index points-to/uses that list pointer. + * + * There are different strategies, the simple and safe is to rebuild + * the chain index every time. The more advanced is to update the + * array index to point to the next element, but that requires some + * house keeping and boundry checks. The advanced is implemented, as + * the simple approach behaves badly when all chains are deleted + * because list_for_each processing will always hit the first chain + * index, thus causing a rebuild for every chain. + */ +static int iptcc_chain_index_delete_chain(struct chain_head *c, TC_HANDLE_T h) +{ + struct list_head *index_ptr, *index_ptr2, *next; + struct chain_head *c2; + unsigned int idx, idx2; + + index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h); + + debug("Del chain[%s] c->list:%p index_ptr:%p\n", + c->name, &c->list, index_ptr); + + /* Save the next pointer */ + next = c->list.next; + list_del(&c->list); + + if (index_ptr == &c->list) { /* Chain used as index ptr */ + + /* See if its possible to avoid a rebuild, by shifting + * to next pointer. Its possible if the next pointer + * is located in the same index bucket. + */ + c2 = list_entry(next, struct chain_head, list); + index_ptr2 = iptcc_bsearch_chain_index(c2->name, &idx2, h); + if (idx != idx2) { + /* Rebuild needed */ + return iptcc_chain_index_rebuild(h); + } else { + /* Avoiding rebuild */ + debug("Update cindex[%d] with next ptr name:[%s]\n", + idx, c2->name); + h->chain_index[idx]=c2; + return 0; + } + } + return 0; +} + + +/********************************************************************** * iptc cache utility functions (iptcc_*) **********************************************************************/ /* Is the given chain builtin (1) or user-defined (0) */ -static unsigned int iptcc_is_builtin(struct chain_head *c) +static inline unsigned int iptcc_is_builtin(struct chain_head *c) { return (c->hooknum ? 1 : 0); } @@ -338,21 +642,81 @@ iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset) return NULL; } + /* Returns chain head if found, otherwise NULL. */ static struct chain_head * iptcc_find_label(const char *name, TC_HANDLE_T handle) { struct list_head *pos; + struct list_head *list_start_pos; + unsigned int i=0; + int res; if (list_empty(&handle->chains)) return NULL; + /* First look at builtin chains */ list_for_each(pos, &handle->chains) { struct chain_head *c = list_entry(pos, struct chain_head, list); + if (!iptcc_is_builtin(c)) + break; if (!strcmp(c->name, name)) return c; } + /* Find a smart place to start the search via chain index */ + //list_start_pos = iptcc_linearly_search_chain_index(name, handle); + list_start_pos = iptcc_bsearch_chain_index(name, &i, handle); + + /* Handel if bsearch bails out early */ + if (list_start_pos == &handle->chains) { + list_start_pos = pos; + } +#ifdef DEBUG + else { + /* Verify result of bsearch against linearly index search */ + struct list_head *test_pos; + struct chain_head *test_c, *tmp_c; + test_pos = iptcc_linearly_search_chain_index(name, handle); + if (list_start_pos != test_pos) { + debug("BUG in chain_index search\n"); + test_c=list_entry(test_pos, struct chain_head,list); + tmp_c =list_entry(list_start_pos,struct chain_head,list); + debug("Verify search found:\n"); + debug(" Chain:%s\n", test_c->name); + debug("BSearch found:\n"); + debug(" Chain:%s\n", tmp_c->name); + exit(42); + } + } +#endif + + /* Initial/special case, no user defined chains */ + if (handle->num_chains == 0) + return NULL; + + /* Start searching through the chain list */ + list_for_each(pos, list_start_pos->prev) { + struct chain_head *c = list_entry(pos, struct chain_head, list); + res = strcmp(c->name, name); + debug("List search name:%s == %s res:%d\n", name, c->name, res); + if (res==0) + return c; + + /* We can stop earlier as we know list is sorted */ + if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/ + debug(" Not in list, walked too far, sorted list\n"); + return NULL; + } + + /* Stop on wrap around, if list head is reached */ + if (pos == &handle->chains) { + debug("Stop, list head reached\n"); + return NULL; + } + } + + debug("List search NOT found name:%s\n", name); return NULL; } @@ -413,14 +777,37 @@ static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num) static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c) { struct chain_head *tmp; + struct list_head *list_start_pos; + unsigned int i=1; + + /* Find a smart place to start the insert search */ + list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h); + + /* Handle the case, where chain.name is smaller than index[0] */ + if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) { + h->chain_index[0] = c; /* Update chain index head */ + list_start_pos = h->chains.next; + debug("Update chain_index[0] with %s\n", c->name); + } + + /* Handel if bsearch bails out early */ + if (list_start_pos == &h->chains) { + list_start_pos = h->chains.next; + } /* sort only user defined chains */ if (!c->hooknum) { - list_for_each_entry(tmp, &h->chains, list) { + list_for_each_entry(tmp, list_start_pos->prev, list) { if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) { list_add(&c->list, tmp->list.prev); return; } + + /* Stop if list head is reached */ + if (&tmp->list == &h->chains) { + debug("Insert, list head reached add to tail\n"); + break; + } } } @@ -493,6 +880,7 @@ static int cache_add_entry(STRUCT_ENTRY *e, errno = -ENOMEM; return -1; } + h->num_chains++; /* New user defined chain */ __iptcc_p_add_chain(h, c, offset, num); @@ -580,22 +968,27 @@ static int parse_table(TC_HANDLE_T h) ENTRY_ITERATE(h->entries->entrytable, h->entries->size, cache_add_entry, h, &prev, &num); + /* Build the chain index, used for chain list search speedup */ + if ((iptcc_chain_index_alloc(h)) < 0) + return -ENOMEM; + iptcc_chain_index_build(h); + /* Second pass: fixup parsed data from first pass */ list_for_each_entry(c, &h->chains, list) { struct rule_head *r; list_for_each_entry(r, &c->rules, list) { - struct chain_head *c; + struct chain_head *lc; STRUCT_STANDARD_TARGET *t; if (r->type != IPTCC_R_JUMP) continue; t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry); - c = iptcc_find_chain_by_offset(h, t->verdict); - if (!c) + lc = iptcc_find_chain_by_offset(h, t->verdict); + if (!lc) return -1; - r->jump = c; - c->references++; + r->jump = lc; + lc->references++; } } @@ -848,7 +1241,7 @@ TC_INIT(const char *tablename) return NULL; } sockfd_use++; - +retry: s = sizeof(info); strcpy(info.name, tablename); @@ -901,6 +1294,9 @@ TC_INIT(const char *tablename) return h; error: TC_FREE(&h); + /* A different process changed the ruleset size, retry */ + if (errno == EAGAIN) + goto retry; return NULL; } @@ -925,6 +1321,8 @@ TC_FREE(TC_HANDLE_T *h) free(c); } + iptcc_chain_index_free(*h); + free((*h)->entries); free(*h); @@ -947,7 +1345,7 @@ TC_DUMP_ENTRIES(const TC_HANDLE_T handle) CHECK(handle); printf("libiptc v%s. %u bytes.\n", - IPTABLES_VERSION, handle->entries->size); + XTABLES_VERSION, handle->entries->size); printf("Table `%s'\n", handle->info.name); printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n", handle->info.hook_entry[HOOK_PRE_ROUTING], @@ -1091,7 +1489,7 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle) } /* How many rules in this chain? */ -unsigned int +static unsigned int TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle) { struct chain_head *c; @@ -1107,9 +1505,8 @@ TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle) return c->num_rules; } -const STRUCT_ENTRY *TC_GET_RULE(const char *chain, - unsigned int n, - TC_HANDLE_T *handle) +static const STRUCT_ENTRY * +TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle) { struct chain_head *c; struct rule_head *r; @@ -1791,6 +2188,8 @@ int TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) { static struct chain_head *c; + int capacity; + int exceeded; iptc_fn = TC_CREATE_CHAIN; @@ -1819,10 +2218,25 @@ TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) return 0; } + (*handle)->num_chains++; /* New user defined chain */ DEBUGP("Creating chain `%s'\n", chain); iptc_insert_chain(*handle, c); /* Insert sorted */ + /* Inserting chains don't change the correctness of the chain + * index (except if its smaller than index[0], but that + * handled by iptc_insert_chain). It only causes longer lists + * in the buckets. Thus, only rebuild chain index when the + * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains. + */ + capacity = (*handle)->chain_index_sz * CHAIN_INDEX_BUCKET_LEN; + exceeded = ((((*handle)->num_chains)-capacity)); + if (exceeded > CHAIN_INDEX_INSERT_MAX) { + debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n", + capacity, exceeded, (*handle)->num_chains); + iptcc_chain_index_rebuild(*handle); + } + set_changed(*handle); return 1; @@ -1885,11 +2299,14 @@ TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) } /* If we are about to delete the chain that is the current - * iterator, move chain iterator firward. */ + * iterator, move chain iterator forward. */ if (c == (*handle)->chain_iterator_cur) iptcc_chain_iterator_advance(*handle); - list_del(&c->list); + (*handle)->num_chains--; /* One user defined chain deleted */ + + //list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */ + iptcc_chain_index_delete_chain(c, *handle); free(c); DEBUGP("chain `%s' deleted\n", chain); @@ -1997,16 +2414,14 @@ subtract_counters(STRUCT_COUNTERS *answer, } -static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, - unsigned int index) +static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx) { - newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0}); + newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0}); DEBUGP_C("NOMAP => zero\n"); } static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters, - STRUCT_REPLACE *repl, - unsigned int index, + STRUCT_REPLACE *repl, unsigned int idx, unsigned int mappos) { /* Original read: X. @@ -2016,15 +2431,13 @@ static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters, * => Add in X + Y * => Add in replacement read. */ - newcounters->counters[index] = repl->counters[mappos]; + newcounters->counters[idx] = repl->counters[mappos]; DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos); } static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters, - STRUCT_REPLACE *repl, - unsigned int index, - unsigned int mappos, - STRUCT_COUNTERS *counters) + STRUCT_REPLACE *repl, unsigned int idx, + unsigned int mappos, STRUCT_COUNTERS *counters) { /* Original read: X. * Atomic read on replacement: X + Y. @@ -2033,19 +2446,18 @@ static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters, * => Add in Y. * => Add in (replacement read - original read). */ - subtract_counters(&newcounters->counters[index], + subtract_counters(&newcounters->counters[idx], &repl->counters[mappos], counters); DEBUGP_C("ZEROED => mappos %u\n", mappos); } static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters, - unsigned int index, - STRUCT_COUNTERS *counters) + unsigned int idx, STRUCT_COUNTERS *counters) { /* Want to set counter (iptables-restore) */ - memcpy(&newcounters->counters[index], counters, + memcpy(&newcounters->counters[idx], counters, sizeof(STRUCT_COUNTERS)); DEBUGP_C("SET\n"); diff --git a/src/libiptc/libiptc.h b/src/libiptc/libiptc.h index d9f7423c..3fc25b6f 100644 --- a/src/libiptc/libiptc.h +++ b/src/libiptc/libiptc.h @@ -172,11 +172,13 @@ int iptc_set_counter(const ipt_chainlabel chain, int iptc_commit(iptc_handle_t *handle); /* Get raw socket. */ -int iptc_get_raw_socket(); +int iptc_get_raw_socket(void); /* Translates errno numbers into more human-readable form than strerror. */ const char *iptc_strerror(int err); +extern void dump_entries(const iptc_handle_t); + #ifdef __cplusplus } #endif diff --git a/src/libiptc/libxtc.h b/src/libiptc/libxtc.h new file mode 100644 index 00000000..2ed03f4c --- /dev/null +++ b/src/libiptc/libxtc.h @@ -0,0 +1,53 @@ +/** + * This file was imported from the iptables sources. + * Copyright (C) 1999-2008 Netfilter Core Team + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LIBXTC_H +#define _LIBXTC_H +/* Library which manipulates filtering rules. */ + +#include "ipt_kernel_headers.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XT_MIN_ALIGN +/* xt_entry has pointers and u_int64_t's in it, so if you align to + it, you'll also align to any crazy matches and targets someone + might write */ +#define XT_MIN_ALIGN (__alignof__(struct xt_entry)) +#endif + +#ifndef XT_ALIGN +#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1)) +#endif + +typedef char xt_chainlabel[32]; + +#define XTC_LABEL_ACCEPT "ACCEPT" +#define XTC_LABEL_DROP "DROP" +#define XTC_LABEL_QUEUE "QUEUE" +#define XTC_LABEL_RETURN "RETURN" + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBXTC_H */ diff --git a/src/libiptc/xtables.h b/src/libiptc/xtables.h new file mode 100644 index 00000000..47e47dc7 --- /dev/null +++ b/src/libiptc/xtables.h @@ -0,0 +1,239 @@ +/** + * This file was imported from the iptables sources. + * Copyright (C) 1999-2008 Netfilter Core Team + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _XTABLES_H +#define _XTABLES_H + +#include +#include +#include +#include "libxtc.h" +#include + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#define XTABLES_VERSION "1.4.1.1" +#define XTABLES_VERSION_CODE (0x10000 * 1 + 0x100 * 4 + 1) + +#define XTABLES_API_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) + +/* Include file for additions: new matches and targets. */ +struct xtables_match +{ + struct xtables_match *next; + + xt_chainlabel name; + + /* Revision of match (0 by default). */ + u_int8_t revision; + + u_int16_t family; + + const char *version; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct xt_entry_match *m); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + /* ip is struct ipt_ip * for example */ + void (*print)(const void *ip, + const struct xt_entry_match *match, int numeric); + + /* Saves the match info in parsable form to stdout. */ + /* ip is struct ipt_ip * for example */ + void (*save)(const void *ip, const struct xt_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct xt_entry_match *m; + unsigned int mflags; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +struct xtables_target +{ + struct xtables_target *next; + + xt_chainlabel name; + + /* Revision of target (0 by default). */ + u_int8_t revision; + + u_int16_t family; + + const char *version; + + /* Size of target data. */ + size_t size; + + /* Size of target data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct xt_entry_target *t); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_target **targetinfo); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const void *ip, + const struct xt_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const void *ip, + const struct xt_entry_target *target); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct xt_entry_target *t; + unsigned int tflags; + unsigned int used; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +/* Your shared library should call one of these. */ +extern void xtables_register_match(struct xtables_match *me); +extern void xtables_register_target(struct xtables_target *me); + +extern int string_to_number_ll(const char *s, + unsigned long long min, + unsigned long long max, + unsigned long long *ret); +extern int string_to_number_l(const char *s, + unsigned long min, + unsigned long max, + unsigned long *ret); +extern int string_to_number(const char *s, + unsigned int min, + unsigned int max, + unsigned int *ret); +extern bool strtonuml(const char *, char **, unsigned long *, + unsigned long, unsigned long); +extern bool strtonum(const char *, char **, unsigned int *, + unsigned int, unsigned int); +extern int service_to_port(const char *name, const char *proto); +extern u_int16_t parse_port(const char *port, const char *proto); +extern void +parse_interface(const char *arg, char *vianame, unsigned char *mask); + +enum exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM, + RESOURCE_PROBLEM, + P_ONLY_ONCE, + P_NO_INVERT, + P_BAD_VALUE, + P_ONE_ACTION, +}; + +/* this is a special 64bit data type that is 8-byte aligned */ +#define aligned_u64 u_int64_t __attribute__((aligned(8))) + +int check_inverse(const char option[], int *invert, int *my_optind, int argc); +void exit_error(enum exittype, const char *, ...)__attribute__((noreturn, + format(printf,2,3))); +extern void param_act(unsigned int, const char *, ...); +extern const char *program_name, *program_version; + +extern const char *ipaddr_to_numeric(const struct in_addr *); +extern const char *ipaddr_to_anyname(const struct in_addr *); +extern const char *ipmask_to_numeric(const struct in_addr *); +extern struct in_addr *numeric_to_ipaddr(const char *); +extern struct in_addr *numeric_to_ipmask(const char *); +extern void ipparse_hostnetworkmask(const char *, struct in_addr **, + struct in_addr *, unsigned int *); + +extern struct in6_addr *numeric_to_ip6addr(const char *); +extern const char *ip6addr_to_numeric(const struct in6_addr *); +extern const char *ip6addr_to_anyname(const struct in6_addr *); +extern const char *ip6mask_to_numeric(const struct in6_addr *); +extern void ip6parse_hostnetworkmask(const char *, struct in6_addr **, + struct in6_addr *, unsigned int *); + +/** + * Print the specified value to standard output, quoting dangerous + * characters if required. + */ +extern void save_string(const char *value); + +#ifdef NO_SHARED_LIBS +# ifdef _INIT +# undef _init +# define _init _INIT +# endif + extern void init_extensions(void); +#else +# define _init __attribute__((constructor)) _INIT +#endif + +/* Present in both iptables.c and ip6tables.c */ +extern u_int16_t parse_protocol(const char *s); + +#ifdef XTABLES_INTERNAL +# include +#endif + +#endif /* _XTABLES_H */ diff --git a/src/oracle.c b/src/oracle.c new file mode 100644 index 00000000..fa7e72fa --- /dev/null +++ b/src/oracle.c @@ -0,0 +1,1027 @@ +/** + * collectd - src/oracle.c + * Copyright (C) 2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linking src/oracle.c ("the oracle plugin") statically or dynamically with + * other modules is making a combined work based on the oracle plugin. Thus, + * the terms and conditions of the GNU General Public License cover the whole + * combination. + * + * In addition, as a special exception, the copyright holders of the oracle + * plugin give you permission to combine the oracle plugin with free software + * programs or libraries that are released under the GNU LGPL and with code + * included in the standard release of the Oracle® Call Interface (OCI) under + * the Oracle® Technology Network (OTN) License (or modified versions of such + * code, with unchanged license). You may copy and distribute such a system + * following the terms of the GNU GPL for the oracle plugin and the licenses of + * the other code concerned. + * + * Note that people who make modified versions of the oracle plugin are not + * obligated to grant this special exception for their modified versions; it is + * their choice whether to do so. The GNU General Public License gives + * permission to release a modified version without this exception; this + * exception also makes it possible to release a modified version which carries + * forward this exception. However, without this exception the OTN License does + * not allow linking with code licensed under the GNU General Public License. + * + * Oracle® is a registered trademark of Oracle Corporation and/or its + * affiliates. Other names may be trademarks of their respective owners. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include + +/* + * Data types + */ +struct o_query_s +{ + char *name; + char *statement; + char *type; + char **instances; + size_t instances_num; + char **values; + size_t values_num; + + OCIStmt *oci_statement; +}; +typedef struct o_query_s o_query_t; + +struct o_database_s +{ + char *name; + char *connect_id; + char *username; + char *password; + + o_query_t **queries; + size_t queries_num; + + OCISvcCtx *oci_service_context; +}; +typedef struct o_database_s o_database_t; + +/* + * Global variables + */ +static o_query_t **queries = NULL; +static size_t queries_num = 0; +static o_database_t **databases = NULL; +static size_t databases_num = 0; + +OCIEnv *oci_env = NULL; +OCIError *oci_error = NULL; + +/* + * Functions + */ +static void o_report_error (const char *where, /* {{{ */ + const char *what, OCIError *eh) +{ + char buffer[2048]; + sb4 error_code; + int status; + + status = OCIErrorGet (eh, /* record number = */ 1, + /* sqlstate = */ NULL, + &error_code, + (text *) &buffer[0], + (ub4) sizeof (buffer), + OCI_HTYPE_ERROR); + buffer[sizeof (buffer) - 1] = 0; + + if (status == OCI_SUCCESS) + { + size_t buffer_length; + + buffer_length = strlen (buffer); + while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32)) + { + buffer_length--; + buffer[buffer_length] = 0; + } + + ERROR ("oracle plugin: %s: %s failed: %s", + where, what, buffer); + } + else + { + ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.", + where, what, status); + } +} /* }}} void o_report_error */ + +static void o_query_free (o_query_t *q) /* {{{ */ +{ + size_t i; + + if (q == NULL) + return; + + sfree (q->name); + sfree (q->statement); + sfree (q->type); + + for (i = 0; i < q->instances_num; i++) + sfree (q->instances[i]); + sfree (q->instances); + + for (i = 0; i < q->values_num; i++) + sfree (q->values[i]); + sfree (q->values); + + sfree (q); +} /* }}} void o_query_free */ + +static void o_database_free (o_database_t *db) /* {{{ */ +{ + if (db == NULL) + return; + + sfree (db->name); + sfree (db->connect_id); + sfree (db->username); + sfree (db->password); + sfree (db->queries); + + sfree (db); +} /* }}} void o_database_free */ + +/* Configuration handling functions {{{ + * + * + * + * Statement "SELECT name, value FROM table" + * Type "gauge" + * InstancesFrom "name" + * ValuesFrom "value" + * + * + * + * ConnectID "db01" + * Username "oracle" + * Password "secret" + * Query "plugin_instance0" + * + * + */ + +static int o_config_set_string (char **ret_string, /* {{{ */ + oconfig_item_t *ci) +{ + char *string; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("oracle plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + string = strdup (ci->values[0].value.string); + if (string == NULL) + { + ERROR ("oracle plugin: strdup failed."); + return (-1); + } + + if (*ret_string != NULL) + free (*ret_string); + *ret_string = string; + + return (0); +} /* }}} int o_config_set_string */ + +static int o_config_add_string (char ***ret_array, /* {{{ */ + size_t *ret_array_len, oconfig_item_t *ci) +{ + char **array; + size_t array_len; + int i; + + if (ci->values_num < 1) + { + WARNING ("oracle plugin: The `%s' config option " + "needs at least one argument.", ci->key); + return (-1); + } + + for (i = 0; i < ci->values_num; i++) + { + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + WARNING ("oracle plugin: Argument %i to the `%s' option " + "is not a string.", i + 1, ci->key); + return (-1); + } + } + + array_len = *ret_array_len; + array = (char **) realloc (*ret_array, + sizeof (char *) * (array_len + ci->values_num)); + if (array == NULL) + { + ERROR ("oracle plugin: realloc failed."); + return (-1); + } + *ret_array = array; + + for (i = 0; i < ci->values_num; i++) + { + array[array_len] = strdup (ci->values[i].value.string); + if (array[array_len] == NULL) + { + ERROR ("oracle plugin: strdup failed."); + *ret_array_len = array_len; + return (-1); + } + array_len++; + } + + *ret_array_len = array_len; + return (0); +} /* }}} int o_config_add_string */ + +static int o_config_add_query (oconfig_item_t *ci) /* {{{ */ +{ + o_query_t *q; + int status; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("oracle plugin: The `Query' block " + "needs exactly one string argument."); + return (-1); + } + + q = (o_query_t *) malloc (sizeof (*q)); + if (q == NULL) + { + ERROR ("oracle plugin: malloc failed."); + return (-1); + } + memset (q, 0, sizeof (*q)); + + status = o_config_set_string (&q->name, ci); + if (status != 0) + { + sfree (q); + return (status); + } + + /* Fill the `o_query_t' structure.. */ + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Statement", child->key) == 0) + status = o_config_set_string (&q->statement, child); + else if (strcasecmp ("Type", child->key) == 0) + status = o_config_set_string (&q->type, child); + else if (strcasecmp ("InstancesFrom", child->key) == 0) + status = o_config_add_string (&q->instances, &q->instances_num, child); + else if (strcasecmp ("ValuesFrom", child->key) == 0) + status = o_config_add_string (&q->values, &q->values_num, child); + else + { + WARNING ("oracle plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + while (status == 0) + { + if (q->statement == NULL) + { + WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name); + status = -1; + } + if (q->type == NULL) + { + WARNING ("oracle plugin: `Type' not given for query `%s'", q->name); + status = -1; + } + if (q->instances == NULL) + { + WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name); + status = -1; + } + if (q->values == NULL) + { + WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name); + status = -1; + } + + break; + } /* while (status == 0) */ + + /* If all went well, add this query to the list of queries within the + * database structure. */ + if (status == 0) + { + o_query_t **temp; + + temp = (o_query_t **) realloc (queries, + sizeof (*queries) * (queries_num + 1)); + if (temp == NULL) + { + ERROR ("oracle plugin: realloc failed"); + status = -1; + } + else + { + queries = temp; + queries[queries_num] = q; + queries_num++; + } + } + + if (status != 0) + { + o_query_free (q); + return (-1); + } + + return (0); +} /* }}} int o_config_add_query */ + +static int o_config_add_database_query (o_database_t *db, /* {{{ */ + oconfig_item_t *ci) +{ + o_query_t *q; + o_query_t **temp; + size_t i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("oracle plugin: The `Query' config option " + "needs exactly one string argument."); + return (-1); + } + + q = NULL; + for (i = 0; i < queries_num; i++) + { + if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0) + { + q = queries[i]; + break; + } + } + + if (q == NULL) + { + WARNING ("oracle plugin: Database `%s': Unknown query `%s'. " + "Please make sure that the block comes before " + "the block.", + db->name, ci->values[0].value.string, + ci->values[0].value.string, db->name); + return (-1); + } + + temp = (o_query_t **) realloc (db->queries, + sizeof (*db->queries) * (db->queries_num + 1)); + if (temp == NULL) + { + ERROR ("oracle plugin: realloc failed"); + return (-1); + } + else + { + db->queries = temp; + db->queries[db->queries_num] = q; + db->queries_num++; + } + + return (0); +} /* }}} int o_config_add_database_query */ + +static int o_config_add_database (oconfig_item_t *ci) /* {{{ */ +{ + o_database_t *db; + int status; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("oracle plugin: The `Database' block " + "needs exactly one string argument."); + return (-1); + } + + db = (o_database_t *) malloc (sizeof (*db)); + if (db == NULL) + { + ERROR ("oracle plugin: malloc failed."); + return (-1); + } + memset (db, 0, sizeof (*db)); + + status = o_config_set_string (&db->name, ci); + if (status != 0) + { + sfree (db); + return (status); + } + + /* Fill the `o_database_t' structure.. */ + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("ConnectID", child->key) == 0) + status = o_config_set_string (&db->connect_id, child); + else if (strcasecmp ("Username", child->key) == 0) + status = o_config_set_string (&db->username, child); + else if (strcasecmp ("Password", child->key) == 0) + status = o_config_set_string (&db->password, child); + else if (strcasecmp ("Query", child->key) == 0) + status = o_config_add_database_query (db, child); + else + { + WARNING ("oracle plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + while (status == 0) + { + if (db->connect_id == NULL) + { + WARNING ("oracle plugin: `ConnectID' not given for query `%s'", db->name); + status = -1; + } + if (db->username == NULL) + { + WARNING ("oracle plugin: `Username' not given for query `%s'", db->name); + status = -1; + } + if (db->password == NULL) + { + WARNING ("oracle plugin: `Password' not given for query `%s'", db->name); + status = -1; + } + + break; + } /* while (status == 0) */ + + /* If all went well, add this query to the list of queries within the + * database structure. */ + if (status == 0) + { + o_database_t **temp; + + temp = (o_database_t **) realloc (databases, + sizeof (*databases) * (databases_num + 1)); + if (temp == NULL) + { + ERROR ("oracle plugin: realloc failed"); + status = -1; + } + else + { + databases = temp; + databases[databases_num] = db; + databases_num++; + } + } + + if (status != 0) + { + o_database_free (db); + return (-1); + } + + return (0); +} /* }}} int o_config_add_database */ + +static int o_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + if (strcasecmp ("Query", child->key) == 0) + o_config_add_query (child); + else if (strcasecmp ("Database", child->key) == 0) + o_config_add_database (child); + else + { + WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key); + } + } /* for (ci->children) */ + + return (0); +} /* }}} int o_config */ + +/* }}} End of configuration handling functions */ + +static int o_init (void) /* {{{ */ +{ + int status; + + if (oci_env != NULL) + return (0); + + status = OCIEnvCreate (&oci_env, + /* mode = */ OCI_THREADED, + /* context = */ NULL, + /* malloc = */ NULL, + /* realloc = */ NULL, + /* free = */ NULL, + /* user_data_size = */ 0, + /* user_data_ptr = */ NULL); + if (status != 0) + { + ERROR ("oracle plugin: OCIEnvCreate failed with status %i.", status); + return (-1); + } + + status = OCIHandleAlloc (oci_env, (void *) &oci_error, OCI_HTYPE_ERROR, + /* user_data_size = */ 0, /* user_data = */ NULL); + if (status != OCI_SUCCESS) + { + ERROR ("oracle plugin: OCIHandleAlloc (OCI_HTYPE_ERROR) failed " + "with status %i.", status); + return (-1); + } + + return (0); +} /* }}} int o_init */ + +static void o_submit (o_database_t *db, o_query_t *q, /* {{{ */ + const data_set_t *ds, char **buffer_instances, char **buffer_values) +{ + value_list_t vl = VALUE_LIST_INIT; + size_t i; + + assert (((size_t) ds->ds_num) == q->values_num); + + vl.values = (value_t *) malloc (sizeof (value_t) * q->values_num); + if (vl.values == NULL) + { + ERROR ("oracle plugin: malloc failed."); + return; + } + vl.values_len = ds->ds_num; + + for (i = 0; i < q->values_num; i++) + { + char *endptr; + + endptr = NULL; + errno = 0; + if (ds->ds[i].type == DS_TYPE_COUNTER) + vl.values[i].counter = (counter_t) strtoll (buffer_values[i], + &endptr, /* base = */ 0); + else if (ds->ds[i].type == DS_TYPE_GAUGE) + vl.values[i].gauge = (gauge_t) strtod (buffer_values[i], &endptr); + else + errno = EINVAL; + + if ((endptr == buffer_values[i]) || (errno != 0)) + { + WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.", + buffer_values[i], + (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge"); + vl.values[i].gauge = NAN; + } + } + + vl.time = time (NULL); + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "oracle", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance)); + sstrncpy (vl.type, q->type, sizeof (vl.type)); + strjoin (vl.type_instance, sizeof (vl.type_instance), + buffer_instances, q->instances_num, "-"); + vl.type_instance[sizeof (vl.type_instance) - 1] = 0; + + plugin_dispatch_values (&vl); +} /* }}} void o_submit */ + +static int o_read_database_query (o_database_t *db, /* {{{ */ + o_query_t *q) +{ + const data_set_t *ds; + ub4 param_counter; /* == number of columns */ + int status; + size_t i; + ub4 j; + + /* Scratch area for OCI to write values to */ + char **buffer_instances; + char **buffer_values; + + /* List of indizes of the instance and value columns. Only used for error + * checking. */ + size_t *index_instances; + size_t *index_values; + + /* List of `OCIDefine' pointers. These defines map columns to the buffer + * space declared above. */ + OCIDefine **oci_defines; + + ds = plugin_get_ds (q->type); /* {{{ */ + if (ds == NULL) + { + WARNING ("oracle plugin: o_read_database_query (%s, %s): " + "plugin_get_ds (%s) failed. Please check if the type exists, " + "see types.db(5).", + db->name, q->name, q->type); + return (-1); + } /* }}} */ + + if (((size_t) ds->ds_num) != q->values_num) + { + ERROR ("oracle plugin: o_read_database_query (%s, %s): " + "The query `%s' uses the type `%s' which requires exactly " + "%i value%s, but you specified %zu value-column%s. " + "See types.db(5) for details.", + db->name, q->name, + q->name, q->type, + ds->ds_num, (ds->ds_num == 1) ? "" : "s", + q->values_num, (q->values_num == 1) ? "" : "s"); + return (-1); + } + + /* Prepare the statement */ + if (q->oci_statement == NULL) /* {{{ */ + { + status = OCIHandleAlloc (oci_env, (void *) &q->oci_statement, + OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error); + q->oci_statement = NULL; + return (-1); + } + + status = OCIStmtPrepare (q->oci_statement, oci_error, + (text *) q->statement, (ub4) strlen (q->statement), + /* language = */ OCI_NTV_SYNTAX, + /* mode = */ OCI_DEFAULT); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error); + OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT); + q->oci_statement = NULL; + return (-1); + } + assert (q->oci_statement != NULL); + } /* }}} */ + + /* Execute the statement */ + status = OCIStmtExecute (db->oci_service_context, /* {{{ */ + q->oci_statement, + oci_error, + /* iters = */ 0, + /* rowoff = */ 0, + /* snap_in = */ NULL, /* snap_out = */ NULL, + /* mode = */ OCI_DEFAULT); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error); + ERROR ("oracle plugin: o_read_database_query: " + "Failing statement was: %s", q->statement); + return (-1); + } /* }}} */ + + /* Acquire the number of columns returned. */ + param_counter = 0; + status = OCIAttrGet (q->oci_statement, OCI_HTYPE_STMT, /* {{{ */ + ¶m_counter, /* size pointer = */ NULL, + OCI_ATTR_PARAM_COUNT, oci_error); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database_query", "OCIAttrGet", oci_error); + return (-1); + } /* }}} */ + + /* Allocate the following buffers: + * + * - buffer_instances q->instances_num x DATA_MAX_NAME_LEN + * - buffer_values q->values_num x NUMBER_BUFFER_SIZE + * - index_instances q->instances_num + * - index_values q->values_num + * - oci_defines q->instances_num+q->values_num + * {{{ */ +#define NUMBER_BUFFER_SIZE 64 + +#define FREE_ALL \ + if (buffer_instances != NULL) { \ + sfree (buffer_instances[0]); \ + sfree (buffer_instances); \ + } \ + if (buffer_values != NULL) { \ + sfree (buffer_values[0]); \ + sfree (buffer_values); \ + } \ + sfree (index_instances); \ + sfree (index_values); \ + sfree (oci_defines) + +#define ALLOC_OR_FAIL(ptr, ptr_size) \ + do { \ + size_t alloc_size = (size_t) ((ptr_size)); \ + (ptr) = malloc (alloc_size); \ + if ((ptr) == NULL) { \ + FREE_ALL; \ + ERROR ("oracle plugin: o_read_database_query: malloc failed."); \ + return (-1); \ + } \ + memset ((ptr), 0, alloc_size); \ + } while (0) + + /* Initialize everything to NULL so the above works. */ + buffer_instances = NULL; + buffer_values = NULL; + index_instances = NULL; + index_values = NULL; + oci_defines = NULL; + + ALLOC_OR_FAIL (buffer_instances, q->instances_num * sizeof (char *)); + ALLOC_OR_FAIL (buffer_instances[0], q->instances_num * DATA_MAX_NAME_LEN + * sizeof (char)); + for (i = 1; i < q->instances_num; i++) + buffer_instances[i] = buffer_instances[i - 1] + DATA_MAX_NAME_LEN; + + ALLOC_OR_FAIL (buffer_values, q->values_num * sizeof (char *)); + ALLOC_OR_FAIL (buffer_values[0], q->values_num * NUMBER_BUFFER_SIZE + * sizeof (char)); + for (i = 1; i < q->values_num; i++) + buffer_values[i] = buffer_values[i - 1] + NUMBER_BUFFER_SIZE; + + ALLOC_OR_FAIL (index_instances, q->instances_num * sizeof (size_t)); + ALLOC_OR_FAIL (index_values, q->values_num * sizeof (size_t)); + + ALLOC_OR_FAIL (oci_defines, (q->instances_num + q->values_num) + * sizeof (OCIDefine *)); + /* }}} End of buffer allocations. */ + + /* ``Define'' the returned data, i. e. bind the columns to the buffers + * returned above. */ + for (j = 1; j <= param_counter; j++) /* {{{ */ + { + char *column_name; + size_t column_name_length; + char column_name_copy[DATA_MAX_NAME_LEN]; + size_t i; + OCIParam *oci_param; + + oci_param = NULL; + + status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error, + (void *) &oci_param, j); + if (status != OCI_SUCCESS) + { + /* This is probably alright */ + DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status); + o_report_error ("o_read_database_query", "OCIParamGet", oci_error); + status = OCI_SUCCESS; + break; + } + + column_name = NULL; + column_name_length = 0; + status = OCIAttrGet (oci_param, OCI_DTYPE_PARAM, + &column_name, &column_name_length, OCI_ATTR_NAME, oci_error); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)", + oci_error); + continue; + } + + /* Ensure null-termination. */ + memset (column_name_copy, 0, sizeof (column_name_copy)); + if (column_name_length >= sizeof (column_name_copy)) + column_name_length = sizeof (column_name_copy) - 1; + memcpy (column_name_copy, column_name, column_name_length); + column_name_copy[column_name_length] = 0; + + DEBUG ("oracle plugin: o_read_database_query: column_name[%u] = %s; column_name_length = %zu;", + (unsigned int) j, column_name_copy, column_name_length); + + for (i = 0; i < q->instances_num; i++) + { + if (strcasecmp (q->instances[i], column_name_copy) != 0) + continue; + + status = OCIDefineByPos (q->oci_statement, + &oci_defines[i], oci_error, j, + buffer_instances[i], DATA_MAX_NAME_LEN, SQLT_STR, + NULL, NULL, NULL, OCI_DEFAULT); + index_instances[i] = j; + + DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> instances[%zu]", + (unsigned int) j, column_name_copy, i); + break; + } + + for (i = 0; i < q->values_num; i++) + { + if (strcasecmp (q->values[i], column_name_copy) != 0) + continue; + + status = OCIDefineByPos (q->oci_statement, + &oci_defines[q->instances_num + i], oci_error, j, + buffer_values[i], NUMBER_BUFFER_SIZE, SQLT_STR, + NULL, NULL, NULL, OCI_DEFAULT); + index_values[i] = j; + + DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]", + (unsigned int) j, column_name_copy, i); + break; + } + } /* for (j = 1; j <= param_counter; j++) */ + /* }}} End of the ``define'' stuff. */ + + /* Iterate over all indizes and check that all columns from which we're + * supposed to read instances or values have been found. */ + /* {{{ */ + status = 0; + for (i = 0; i < q->instances_num; i++) + { + if (index_instances[i] > 0) + continue; + + ERROR ("oracle plugin: o_read_database_query (%s, %s): " + "Instance %zu of the `%s' query should be read from the column `%s', " + "but that column wasn't returned by the SQL statement. Please check " + "your configuration.", + db->name, q->name, (i + 1), q->name, q->instances[i]); + status++; + } + + for (i = 0; i < q->values_num; i++) + { + if (index_values[i] > 0) + continue; + + ERROR ("oracle plugin: o_read_database_query (%s, %s): " + "Value %zu of the `%s' query should be read from the column `%s', " + "but that column wasn't returned by the SQL statement. Please check " + "your configuration.", + db->name, q->name, (i + 1), q->name, q->values[i]); + status++; + } + + if (status != 0) + { + FREE_ALL; + return (-1); + } + /* }}} */ + + /* Fetch and handle all the rows that matched the query. */ + while (42) /* {{{ */ + { + status = OCIStmtFetch2 (q->oci_statement, oci_error, + /* nrows = */ 1, /* orientation = */ OCI_FETCH_NEXT, + /* fetch offset = */ 0, /* mode = */ OCI_DEFAULT); + if (status == OCI_NO_DATA) + { + status = OCI_SUCCESS; + break; + } + else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO)) + { + o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error); + break; + } + + for (i = 0; i < q->instances_num; i++) + { + DEBUG ("oracle plugin: o_read_database_query: " + "buffer_instances[%zu] = %s;", + i, buffer_instances[i]); + } + + for (i = 0; i < q->values_num; i++) + { + DEBUG ("oracle plugin: o_read_database_query: " + "buffer_values[%zu] = %s;", + i, buffer_values[i]); + } + + o_submit (db, q, ds, buffer_instances, buffer_values); + } /* }}} while (42) */ + + /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded: %s", q->statement); */ + FREE_ALL; + + return (0); +#undef FREE_ALL +#undef ALLOC_OR_FAIL +} /* }}} int o_read_database_query */ + +static int o_read_database (o_database_t *db) /* {{{ */ +{ + size_t i; + int status; + + if (db->oci_service_context == NULL) + { + status = OCILogon (oci_env, oci_error, + &db->oci_service_context, + (OraText *) db->username, (ub4) strlen (db->username), + (OraText *) db->password, (ub4) strlen (db->password), + (OraText *) db->connect_id, (ub4) strlen (db->connect_id)); + if (status != OCI_SUCCESS) + { + o_report_error ("o_read_database", "OCILogon", oci_error); + DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;", + db->connect_id, db->oci_service_context); + db->oci_service_context = NULL; + return (-1); + } + assert (db->oci_service_context != NULL); + } + + DEBUG ("oracle plugin: o_read_database: db->connect_id = %s; db->oci_service_context = %p;", + db->connect_id, db->oci_service_context); + + for (i = 0; i < db->queries_num; i++) + o_read_database_query (db, db->queries[i]); + + return (0); +} /* }}} int o_read_database */ + +static int o_read (void) /* {{{ */ +{ + size_t i; + + for (i = 0; i < databases_num; i++) + o_read_database (databases[i]); + + return (0); +} /* }}} int o_read */ + +static int o_shutdown (void) /* {{{ */ +{ + size_t i; + + for (i = 0; i < databases_num; i++) + if (databases[i]->oci_service_context != NULL) + { + OCIHandleFree (databases[i]->oci_service_context, OCI_HTYPE_SVCCTX); + databases[i]->oci_service_context = NULL; + } + + for (i = 0; i < queries_num; i++) + if (queries[i]->oci_statement != NULL) + { + OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT); + queries[i]->oci_statement = NULL; + } + + OCIHandleFree (oci_env, OCI_HTYPE_ENV); + return (0); +} /* }}} int o_shutdown */ + +void module_register (void) /* {{{ */ +{ + plugin_register_complex_config ("oracle", o_config); + plugin_register_init ("oracle", o_init); + plugin_register_read ("oracle", o_read); + plugin_register_shutdown ("oracle", o_shutdown); +} /* }}} void module_register */ + +/* + * vim: shiftwidth=2 softtabstop=2 et fdm=marker + */ diff --git a/src/perl.c b/src/perl.c index 400057b7..cdfb8d5e 100644 --- a/src/perl.c +++ b/src/perl.c @@ -1806,7 +1806,10 @@ static int perl_config (oconfig_item_t *ci) else if (0 == strcasecmp (c->key, "Plugin")) current_status = perl_config_plugin (aTHX_ c); else + { log_warn ("Ignoring unknown config key \"%s\".", c->key); + current_status = 0; + } /* fatal error - it's up to perl_config_* to clean up */ if (0 > current_status) { diff --git a/src/plugin.c b/src/plugin.c index 1ec25441..4d503f7c 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -55,6 +55,7 @@ typedef struct read_func_s read_func_t; static llist_t *list_init; static llist_t *list_read; static llist_t *list_write; +static llist_t *list_filter; static llist_t *list_flush; static llist_t *list_shutdown; static llist_t *list_log; @@ -442,6 +443,12 @@ int plugin_register_write (const char *name, return (register_callback (&list_write, name, (void *) callback)); } /* int plugin_register_write */ +int plugin_register_filter (const char *name, + int (*callback) (const data_set_t *ds, value_list_t *vl)) +{ + return (register_callback (&list_filter, name, (void *) callback)); +} /* int plugin_register_filter */ + int plugin_register_flush (const char *name, int (*callback) (const int timeout, const char *identifier)) { @@ -542,6 +549,11 @@ int plugin_unregister_write (const char *name) return (plugin_unregister (list_write, name)); } +int plugin_unregister_filter (const char *name) +{ + return (plugin_unregister (list_filter, name)); +} + int plugin_unregister_flush (const char *name) { return (plugin_unregister (list_flush, name)); @@ -718,12 +730,13 @@ void plugin_shutdown_all (void) int plugin_dispatch_values (value_list_t *vl) { - static c_complain_t no_write_complaint = C_COMPLAIN_INIT; + static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC; - int (*callback) (const data_set_t *, const value_list_t *); data_set_t *ds; llentry_t *le; + int filter = 0; + if ((vl == NULL) || (*vl->type == '\0')) { ERROR ("plugin_dispatch_values: Invalid value list."); return (-1); @@ -785,15 +798,36 @@ int plugin_dispatch_values (value_list_t *vl) escape_slashes (vl->type, sizeof (vl->type)); escape_slashes (vl->type_instance, sizeof (vl->type_instance)); + le = llist_head (list_filter); + while (le != NULL) + { + int (*filter_callback) (const data_set_t *, value_list_t *) = + (int (*) (const data_set_t *, value_list_t *)) le->value; + + filter |= (*filter_callback) (ds, vl); + + if (filter == FILTER_IGNORE) + return (-1); + + le = le->next; + } + /* Update the value cache */ uc_update (ds, vl); - ut_check_threshold (ds, vl); + + if ((filter & FILTER_NOTHRESHOLD_CHECK) == 0) + ut_check_threshold (ds, vl); + + if (filter & FILTER_NOWRITE) + return (0); le = llist_head (list_write); while (le != NULL) { - callback = (int (*) (const data_set_t *, const value_list_t *)) le->value; - (*callback) (ds, vl); + int (*write_callback) (const data_set_t *, const value_list_t *) = + (int (*) (const data_set_t *, const value_list_t *)) le->value; + + (*write_callback) (ds, vl); le = le->next; } diff --git a/src/plugin.h b/src/plugin.h index 3ffde461..b90811cf 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -52,6 +52,11 @@ #define NOTIF_WARNING 2 #define NOTIF_OKAY 4 +#define FILTER_NOWRITE 1 +#define FILTER_NOTHRESHOLD_CHECK 2 +/* FILTER_IGNORE has to equal the bitwise or of all other filter flags */ +#define FILTER_IGNORE (FILTER_NOWRITE | FILTER_NOTHRESHOLD_CHECK) + /* * Public data types */ @@ -196,6 +201,8 @@ int plugin_register_read (const char *name, int (*callback) (void)); int plugin_register_write (const char *name, int (*callback) (const data_set_t *ds, const value_list_t *vl)); +int plugin_register_filter (const char *name, + int (*callback) (const data_set_t *ds, value_list_t *vl)); int plugin_register_flush (const char *name, int (*callback) (const int timeout, const char *identifier)); int plugin_register_shutdown (char *name, @@ -211,6 +218,7 @@ int plugin_unregister_complex_config (const char *name); int plugin_unregister_init (const char *name); int plugin_unregister_read (const char *name); int plugin_unregister_write (const char *name); +int plugin_unregister_filter (const char *name); int plugin_unregister_flush (const char *name); int plugin_unregister_shutdown (const char *name); int plugin_unregister_data_set (const char *name); diff --git a/src/postgresql.c b/src/postgresql.c index faad16cd..a5bacf49 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -217,8 +217,7 @@ static c_psql_database_t *c_psql_database_new (const char *name) db->conn = NULL; - db->conn_complaint.last = 0; - db->conn_complaint.interval = 0; + C_COMPLAIN_INIT (&db->conn_complaint); db->proto_version = 0; diff --git a/src/rrdcached.c b/src/rrdcached.c new file mode 100644 index 00000000..f9d65ca5 --- /dev/null +++ b/src/rrdcached.c @@ -0,0 +1,415 @@ +/** + * collectd - src/rrdcached.c + * Copyright (C) 2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "utils_rrdcreate.h" + +#include + +/* + * Private variables + */ +static const char *config_keys[] = +{ + "DaemonAddress", + "DataDir", + "CreateFiles", + "CollectStatistics" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static char *datadir = NULL; +static char *daemon_address = NULL; +static int config_create_files = 1; +static int config_collect_stats = 1; +static rrdcreate_config_t rrdcreate_config = +{ + /* stepsize = */ 0, + /* heartbeat = */ 0, + /* rrarows = */ 1200, + /* xff = */ 0.1, + + /* timespans = */ NULL, + /* timespans_num = */ 0, + + /* consolidation_functions = */ NULL, + /* consolidation_functions_num = */ 0 +}; + +static int value_list_to_string (char *buffer, int buffer_len, + const data_set_t *ds, const value_list_t *vl) +{ + int offset; + int status; + int i; + + assert (0 == strcmp (ds->type, vl->type)); + + memset (buffer, '\0', buffer_len); + + status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time); + if ((status < 1) || (status >= buffer_len)) + return (-1); + offset = status; + + for (i = 0; i < ds->ds_num; i++) + { + if ((ds->ds[i].type != DS_TYPE_COUNTER) + && (ds->ds[i].type != DS_TYPE_GAUGE)) + return (-1); + + if (ds->ds[i].type == DS_TYPE_COUNTER) + { + status = ssnprintf (buffer + offset, buffer_len - offset, + ":%llu", vl->values[i].counter); + } + else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */ + { + status = ssnprintf (buffer + offset, buffer_len - offset, + ":%lf", vl->values[i].gauge); + } + + if ((status < 1) || (status >= (buffer_len - offset))) + return (-1); + + offset += status; + } /* for ds->ds_num */ + + return (0); +} /* int value_list_to_string */ + +static int value_list_to_filename (char *buffer, int buffer_len, + const data_set_t *ds, const value_list_t *vl) +{ + int offset = 0; + int status; + + assert (0 == strcmp (ds->type, vl->type)); + + if (datadir != NULL) + { + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s/", datadir); + if ((status < 1) || (status >= buffer_len - offset)) + return (-1); + offset += status; + } + + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s/", vl->host); + if ((status < 1) || (status >= buffer_len - offset)) + return (-1); + offset += status; + + if (strlen (vl->plugin_instance) > 0) + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s-%s/", vl->plugin, vl->plugin_instance); + else + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s/", vl->plugin); + if ((status < 1) || (status >= buffer_len - offset)) + return (-1); + offset += status; + + if (strlen (vl->type_instance) > 0) + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s-%s", vl->type, vl->type_instance); + else + status = ssnprintf (buffer + offset, buffer_len - offset, + "%s", vl->type); + if ((status < 1) || (status >= buffer_len - offset)) + return (-1); + offset += status; + + strncpy (buffer + offset, ".rrd", buffer_len - offset); + buffer[buffer_len - 1] = 0; + + return (0); +} /* int value_list_to_filename */ + +static int rc_config (const char *key, const char *value) +{ + if (strcasecmp ("DataDir", key) == 0) + { + if (datadir != NULL) + free (datadir); + datadir = strdup (value); + if (datadir != NULL) + { + int len = strlen (datadir); + while ((len > 0) && (datadir[len - 1] == '/')) + { + len--; + datadir[len] = '\0'; + } + if (len <= 0) + { + free (datadir); + datadir = NULL; + } + } + } + else if (strcasecmp ("DaemonAddress", key) == 0) + { + sfree (daemon_address); + daemon_address = strdup (value); + if (daemon_address == NULL) + { + ERROR ("rrdcached plugin: strdup failed."); + return (1); + } + } + else if (strcasecmp ("CreateFiles", key) == 0) + { + if ((strcasecmp ("false", value) == 0) + || (strcasecmp ("no", value) == 0) + || (strcasecmp ("off", value) == 0)) + config_create_files = 0; + else + config_create_files = 1; + } + else if (strcasecmp ("CollectStatistics", key) == 0) + { + if ((strcasecmp ("false", value) == 0) + || (strcasecmp ("no", value) == 0) + || (strcasecmp ("off", value) == 0)) + config_collect_stats = 0; + else + config_collect_stats = 1; + } + else + { + return (-1); + } + return (0); +} /* int rc_config */ + +static int rc_read (void) +{ + int status; + rrdc_stats_t *head; + rrdc_stats_t *ptr; + + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + if (daemon_address == NULL) + return (-1); + + if (config_collect_stats == 0) + return (-1); + + vl.values = values; + vl.values_len = 1; + vl.time = time (NULL); + + if ((strncmp ("unix:", daemon_address, strlen ("unix:")) == 0) + || (daemon_address[0] == '/')) + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + else + sstrncpy (vl.host, daemon_address, sizeof (vl.host)); + sstrncpy (vl.plugin, "rrdcached", sizeof (vl.plugin)); + + head = NULL; + status = rrdc_stats_get (&head); + if (status != 0) + { + ERROR ("rrdcached plugin: rrdc_stats_get failed with status %i.", status); + return (-1); + } + + for (ptr = head; ptr != NULL; ptr = ptr->next) + { + if (ptr->type == RRDC_STATS_TYPE_GAUGE) + values[0].gauge = (gauge_t) ptr->value.gauge; + else if (ptr->type == RRDC_STATS_TYPE_COUNTER) + values[0].counter = (counter_t) ptr->value.counter; + else + continue; + + if (strcasecmp ("QueueLength", ptr->name) == 0) + { + sstrncpy (vl.type, "queue_length", sizeof (vl.type)); + sstrncpy (vl.type_instance, "", sizeof (vl.type_instance)); + } + else if (strcasecmp ("UpdatesWritten", ptr->name) == 0) + { + sstrncpy (vl.type, "operations", sizeof (vl.type)); + sstrncpy (vl.type_instance, "write-updates", sizeof (vl.type_instance)); + } + else if (strcasecmp ("DataSetsWritten", ptr->name) == 0) + { + sstrncpy (vl.type, "operations", sizeof (vl.type)); + sstrncpy (vl.type_instance, "write-data_sets", + sizeof (vl.type_instance)); + } + else if (strcasecmp ("TreeNodesNumber", ptr->name) == 0) + { + sstrncpy (vl.type, "gauge", sizeof (vl.type)); + sstrncpy (vl.type_instance, "tree_nodes", sizeof (vl.type_instance)); + } + else if (strcasecmp ("TreeDepth", ptr->name) == 0) + { + sstrncpy (vl.type, "gauge", sizeof (vl.type)); + sstrncpy (vl.type_instance, "tree_depth", sizeof (vl.type_instance)); + } + else if (strcasecmp ("FlushesReceived", ptr->name) == 0) + { + sstrncpy (vl.type, "operations", sizeof (vl.type)); + sstrncpy (vl.type_instance, "receive-flush", sizeof (vl.type_instance)); + } + else if (strcasecmp ("JournalBytes", ptr->name) == 0) + { + sstrncpy (vl.type, "counter", sizeof (vl.type)); + sstrncpy (vl.type_instance, "journal-bytes", sizeof (vl.type_instance)); + } + else if (strcasecmp ("JournalRotate", ptr->name) == 0) + { + sstrncpy (vl.type, "counter", sizeof (vl.type)); + sstrncpy (vl.type_instance, "journal-rotates", sizeof (vl.type_instance)); + } + else if (strcasecmp ("UpdatesReceived", ptr->name) == 0) + { + sstrncpy (vl.type, "operations", sizeof (vl.type)); + sstrncpy (vl.type_instance, "receive-update", sizeof (vl.type_instance)); + } + else + { + DEBUG ("rrdcached plugin: rc_read: Unknown statistic `%s'.", ptr->name); + continue; + } + + plugin_dispatch_values (&vl); + } /* for (ptr = head; ptr != NULL; ptr = ptr->next) */ + + rrdc_stats_free (head); + + return (0); +} /* int rc_read */ + +static int rc_init (void) +{ + if (config_collect_stats != 0) + plugin_register_read ("rrdcached", rc_read); + + return (0); +} /* int rc_init */ + +static int rc_write (const data_set_t *ds, const value_list_t *vl) +{ + char filename[512]; + char values[512]; + char *values_array[2]; + int status; + + if (daemon_address == NULL) + { + ERROR ("rrdcached plugin: daemon_address == NULL."); + plugin_unregister_write ("rrdcached"); + return (-1); + } + + if (strcmp (ds->type, vl->type) != 0) + { + ERROR ("rrdcached plugin: DS type does not match value list type"); + return (-1); + } + + if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0) + { + ERROR ("rrdcached plugin: value_list_to_filename failed."); + return (-1); + } + + if (value_list_to_string (values, sizeof (values), ds, vl) != 0) + { + ERROR ("rrdcached plugin: value_list_to_string failed."); + return (-1); + } + + values_array[0] = values; + values_array[1] = NULL; + + if (config_create_files != 0) + { + struct stat statbuf; + + status = stat (filename, &statbuf); + if (status != 0) + { + if (errno != ENOENT) + { + char errbuf[1024]; + ERROR ("rrdcached plugin: stat (%s) failed: %s", + filename, sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + status = cu_rrd_create_file (filename, ds, vl, &rrdcreate_config); + if (status != 0) + { + ERROR ("rrdcached plugin: cu_rrd_create_file (%s) failed.", + filename); + return (-1); + } + } + } + + status = rrdc_connect (daemon_address); + if (status != 0) + { + ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.", + daemon_address, status); + return (-1); + } + + status = rrdc_update (filename, /* values_num = */ 1, (void *) values_array); + if (status != 0) + { + ERROR ("rrdcached plugin: rrdc_update (%s, [%s], 1) failed with " + "status %i.", + filename, values_array[0], status); + return (-1); + } + + return (0); +} /* int rc_write */ + +static int rc_shutdown (void) +{ + rrdc_disconnect (); + return (0); +} /* int rc_shutdown */ + +void module_register (void) +{ + plugin_register_config ("rrdcached", rc_config, + config_keys, config_keys_num); + plugin_register_init ("rrdcached", rc_init); + plugin_register_write ("rrdcached", rc_write); + plugin_register_shutdown ("rrdcached", rc_shutdown); +} /* void module_register */ + +/* + * vim: set sw=2 sts=2 et : + */ diff --git a/src/rrdtool.c b/src/rrdtool.c index 2e1727a8..9b236cb8 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -23,6 +23,7 @@ #include "plugin.h" #include "common.h" #include "utils_avltree.h" +#include "utils_rrdcreate.h" #include @@ -65,27 +66,6 @@ typedef struct rrd_queue_s rrd_queue_t; /* * Private variables */ -static int rra_timespans[] = -{ - 3600, - 86400, - 604800, - 2678400, - 31622400 -}; -static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans); - -static int *rra_timespans_custom = NULL; -static int rra_timespans_custom_num = 0; - -static char *rra_types[] = -{ - "AVERAGE", - "MIN", - "MAX" -}; -static int rra_types_num = STATIC_ARRAY_SIZE (rra_types); - static const char *config_keys[] = { "CacheTimeout", @@ -103,12 +83,21 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); /* If datadir is zero, the daemon's basedir is used. If stepsize or heartbeat * is zero a default, depending on the `interval' member of the value list is * being used. */ -static char *datadir = NULL; -static int stepsize = 0; -static int heartbeat = 0; -static int rrarows = 1200; -static double xff = 0.1; -static double write_rate = 0.0; +static char *datadir = NULL; +static double write_rate = 0.0; +static rrdcreate_config_t rrdcreate_config = +{ + /* stepsize = */ 0, + /* heartbeat = */ 0, + /* rrarows = */ 1200, + /* xff = */ 0.1, + + /* timespans = */ NULL, + /* timespans_num = */ 0, + + /* consolidation_functions = */ NULL, + /* consolidation_functions_num = */ 0 +}; /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time, * ALWAYS lock `cache_lock' first! */ @@ -132,233 +121,7 @@ static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER; static int do_shutdown = 0; -/* * * * * * * * * * - * WARNING: Magic * - * * * * * * * * * */ - -static void rra_free (int rra_num, char **rra_def) -{ - int i; - - for (i = 0; i < rra_num; i++) - { - sfree (rra_def[i]); - } - sfree (rra_def); -} /* void rra_free */ - -static int rra_get (char ***ret, const value_list_t *vl) -{ - char **rra_def; - int rra_num; - - int *rts; - int rts_num; - - int rra_max; - - int span; - - int cdp_num; - int cdp_len; - int i, j; - - char buffer[64]; - - /* The stepsize we use here: If it is user-set, use it. If not, use the - * interval of the value-list. */ - int ss; - - if (rrarows <= 0) - { - *ret = NULL; - return (-1); - } - - ss = (stepsize > 0) ? stepsize : vl->interval; - if (ss <= 0) - { - *ret = NULL; - return (-1); - } - - /* Use the configured timespans or fall back to the built-in defaults */ - if (rra_timespans_custom_num != 0) - { - rts = rra_timespans_custom; - rts_num = rra_timespans_custom_num; - } - else - { - rts = rra_timespans; - rts_num = rra_timespans_num; - } - - rra_max = rts_num * rra_types_num; - - if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL) - return (-1); - memset (rra_def, '\0', (rra_max + 1) * sizeof (char *)); - rra_num = 0; - - cdp_len = 0; - for (i = 0; i < rts_num; i++) - { - span = rts[i]; - - if ((span / ss) < rrarows) - span = ss * rrarows; - - if (cdp_len == 0) - cdp_len = 1; - else - cdp_len = (int) floor (((double) span) - / ((double) (rrarows * ss))); - - cdp_num = (int) ceil (((double) span) - / ((double) (cdp_len * ss))); - - for (j = 0; j < rra_types_num; j++) - { - if (rra_num >= rra_max) - break; - - if (ssnprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u", - rra_types[j], xff, - cdp_len, cdp_num) >= sizeof (buffer)) - { - ERROR ("rra_get: Buffer would have been truncated."); - continue; - } - - rra_def[rra_num++] = sstrdup (buffer); - } - } - -#if COLLECT_DEBUG - DEBUG ("rra_num = %i", rra_num); - for (i = 0; i < rra_num; i++) - DEBUG (" %s", rra_def[i]); -#endif - - *ret = rra_def; - return (rra_num); -} /* int rra_get */ - -static void ds_free (int ds_num, char **ds_def) -{ - int i; - - for (i = 0; i < ds_num; i++) - if (ds_def[i] != NULL) - free (ds_def[i]); - free (ds_def); -} - -static int ds_get (char ***ret, const data_set_t *ds, const value_list_t *vl) -{ - char **ds_def; - int ds_num; - - char min[32]; - char max[32]; - char buffer[128]; - - DEBUG ("ds->ds_num = %i", ds->ds_num); - - ds_def = (char **) malloc (ds->ds_num * sizeof (char *)); - if (ds_def == NULL) - { - char errbuf[1024]; - ERROR ("rrdtool plugin: malloc failed: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); - } - memset (ds_def, '\0', ds->ds_num * sizeof (char *)); - - for (ds_num = 0; ds_num < ds->ds_num; ds_num++) - { - data_source_t *d = ds->ds + ds_num; - char *type; - int status; - - ds_def[ds_num] = NULL; - - if (d->type == DS_TYPE_COUNTER) - type = "COUNTER"; - else if (d->type == DS_TYPE_GAUGE) - type = "GAUGE"; - else - { - ERROR ("rrdtool plugin: Unknown DS type: %i", - d->type); - break; - } - - if (isnan (d->min)) - { - sstrncpy (min, "U", sizeof (min)); - } - else - ssnprintf (min, sizeof (min), "%lf", d->min); - - if (isnan (d->max)) - { - sstrncpy (max, "U", sizeof (max)); - } - else - ssnprintf (max, sizeof (max), "%lf", d->max); - - status = ssnprintf (buffer, sizeof (buffer), - "DS:%s:%s:%i:%s:%s", - d->name, type, - (heartbeat > 0) ? heartbeat : (2 * vl->interval), - min, max); - if ((status < 1) || (status >= sizeof (buffer))) - break; - - ds_def[ds_num] = sstrdup (buffer); - } /* for ds_num = 0 .. ds->ds_num */ - -#if COLLECT_DEBUG -{ - int i; - DEBUG ("ds_num = %i", ds_num); - for (i = 0; i < ds_num; i++) - DEBUG (" %s", ds_def[i]); -} -#endif - - if (ds_num != ds->ds_num) - { - ds_free (ds_num, ds_def); - return (-1); - } - - *ret = ds_def; - return (ds_num); -} - #if HAVE_THREADSAFE_LIBRRD -static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up, - int argc, const char **argv) -{ - int status; - - optind = 0; /* bug in librrd? */ - rrd_clear_error (); - - status = rrd_create_r (filename, pdp_step, last_up, argc, (void *) argv); - - if (status != 0) - { - WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s", - filename, rrd_get_error ()); - } - - return (status); -} /* int srrd_create */ - static int srrd_update (char *filename, char *template, int argc, const char **argv) { @@ -380,59 +143,6 @@ static int srrd_update (char *filename, char *template, /* #endif HAVE_THREADSAFE_LIBRRD */ #else /* !HAVE_THREADSAFE_LIBRRD */ -static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up, - int argc, const char **argv) -{ - int status; - - int new_argc; - char **new_argv; - - char pdp_step_str[16]; - char last_up_str[16]; - - new_argc = 6 + argc; - new_argv = (char **) malloc ((new_argc + 1) * sizeof (char *)); - if (new_argv == NULL) - { - ERROR ("rrdtool plugin: malloc failed."); - return (-1); - } - - if (last_up == 0) - last_up = time (NULL) - 10; - - ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step); - ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up); - - new_argv[0] = "create"; - new_argv[1] = filename; - new_argv[2] = "-s"; - new_argv[3] = pdp_step_str; - new_argv[4] = "-b"; - new_argv[5] = last_up_str; - - memcpy (new_argv + 6, argv, argc * sizeof (char *)); - new_argv[new_argc] = NULL; - - pthread_mutex_lock (&librrd_lock); - optind = 0; /* bug in librrd? */ - rrd_clear_error (); - - status = rrd_create (new_argc, new_argv); - pthread_mutex_unlock (&librrd_lock); - - if (status != 0) - { - WARNING ("rrdtool plugin: rrd_create (%s) failed: %s", - filename, rrd_get_error ()); - } - - sfree (new_argv); - - return (status); -} /* int srrd_create */ - static int srrd_update (char *filename, char *template, int argc, const char **argv) { @@ -476,58 +186,6 @@ static int srrd_update (char *filename, char *template, } /* int srrd_update */ #endif /* !HAVE_THREADSAFE_LIBRRD */ -static int rrd_create_file (char *filename, const data_set_t *ds, const value_list_t *vl) -{ - char **argv; - int argc; - char **rra_def; - int rra_num; - char **ds_def; - int ds_num; - int status = 0; - - if (check_create_dir (filename)) - return (-1); - - if ((rra_num = rra_get (&rra_def, vl)) < 1) - { - ERROR ("rrd_create_file failed: Could not calculate RRAs"); - return (-1); - } - - if ((ds_num = ds_get (&ds_def, ds, vl)) < 1) - { - ERROR ("rrd_create_file failed: Could not calculate DSes"); - return (-1); - } - - argc = ds_num + rra_num; - - if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL) - { - char errbuf[1024]; - ERROR ("rrd_create failed: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); - } - - memcpy (argv, ds_def, ds_num * sizeof (char *)); - memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *)); - argv[ds_num + rra_num] = NULL; - - assert (vl->time > 10); - status = srrd_create (filename, - (stepsize > 0) ? stepsize : vl->interval, - vl->time - 10, - argc, (const char **)argv); - - free (argv); - ds_free (ds_num, ds_def); - rra_free (rra_num, rra_def); - - return (status); -} - static int value_list_to_string (char *buffer, int buffer_len, const data_set_t *ds, const value_list_t *vl) { @@ -1111,7 +769,9 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl) { if (errno == ENOENT) { - if (rrd_create_file (filename, ds, vl)) + status = cu_rrd_create_file (filename, + ds, vl, &rrdcreate_config); + if (status != 0) return (-1); } else @@ -1196,15 +856,15 @@ static int rrd_config (const char *key, const char *value) } else if (strcasecmp ("StepSize", key) == 0) { - stepsize = atoi (value); - if (stepsize < 0) - stepsize = 0; + int temp = atoi (value); + if (temp > 0) + rrdcreate_config.stepsize = temp; } else if (strcasecmp ("HeartBeat", key) == 0) { - heartbeat = atoi (value); - if (heartbeat < 0) - heartbeat = 0; + int temp = atoi (value); + if (temp > 0) + rrdcreate_config.heartbeat = temp; } else if (strcasecmp ("RRARows", key) == 0) { @@ -1215,7 +875,7 @@ static int rrd_config (const char *key, const char *value) "be greater than 0.\n"); return (1); } - rrarows = tmp; + rrdcreate_config.rrarows = tmp; } else if (strcasecmp ("RRATimespan", key) == 0) { @@ -1234,23 +894,23 @@ static int rrd_config (const char *key, const char *value) { dummy = NULL; - tmp_alloc = realloc (rra_timespans_custom, - sizeof (int) * (rra_timespans_custom_num + 1)); + tmp_alloc = realloc (rrdcreate_config.timespans, + sizeof (int) * (rrdcreate_config.timespans_num + 1)); if (tmp_alloc == NULL) { fprintf (stderr, "rrdtool: realloc failed.\n"); free (value_copy); return (1); } - rra_timespans_custom = tmp_alloc; - rra_timespans_custom[rra_timespans_custom_num] = atoi (ptr); - if (rra_timespans_custom[rra_timespans_custom_num] != 0) - rra_timespans_custom_num++; + rrdcreate_config.timespans = tmp_alloc; + rrdcreate_config.timespans[rrdcreate_config.timespans_num] = atoi (ptr); + if (rrdcreate_config.timespans[rrdcreate_config.timespans_num] != 0) + rrdcreate_config.timespans_num++; } /* while (strtok_r) */ - qsort (/* base = */ rra_timespans_custom, - /* nmemb = */ rra_timespans_custom_num, - /* size = */ sizeof (rra_timespans_custom[0]), + qsort (/* base = */ rrdcreate_config.timespans, + /* nmemb = */ rrdcreate_config.timespans_num, + /* size = */ sizeof (rrdcreate_config.timespans[0]), /* compar = */ rrd_compare_numeric); free (value_copy); @@ -1264,7 +924,7 @@ static int rrd_config (const char *key, const char *value) "be in the range 0 to 1 (exclusive)."); return (1); } - xff = tmp; + rrdcreate_config.xff = tmp; } else if (strcasecmp ("WritesPerSecond", key) == 0) { @@ -1318,16 +978,18 @@ static int rrd_init (void) { int status; - if (stepsize < 0) - stepsize = 0; - if (heartbeat <= 0) - heartbeat = 2 * stepsize; + if (rrdcreate_config.stepsize < 0) + rrdcreate_config.stepsize = 0; + if (rrdcreate_config.heartbeat <= 0) + rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize; - if ((heartbeat > 0) && (heartbeat < interval_g)) + if ((rrdcreate_config.heartbeat > 0) + && (rrdcreate_config.heartbeat < interval_g)) WARNING ("rrdtool plugin: Your `heartbeat' is " "smaller than your `interval'. This will " "likely cause problems."); - else if ((stepsize > 0) && (stepsize < interval_g)) + else if ((rrdcreate_config.stepsize > 0) + && (rrdcreate_config.stepsize < interval_g)) WARNING ("rrdtool plugin: Your `stepsize' is " "smaller than your `interval'. This will " "create needlessly big RRD-files."); @@ -1363,7 +1025,10 @@ static int rrd_init (void) DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;" " heartbeat = %i; rrarows = %i; xff = %lf;", (datadir == NULL) ? "(null)" : datadir, - stepsize, heartbeat, rrarows, xff); + rrdcreate_config.stepsize, + rrdcreate_config.heartbeat, + rrdcreate_config.rrarows, + rrdcreate_config.xff); return (0); } /* int rrd_init */ diff --git a/src/snmp.c b/src/snmp.c index 4d6e7694..3ba93fe8 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -22,6 +22,7 @@ #include "collectd.h" #include "common.h" #include "plugin.h" +#include "utils_complain.h" #include @@ -67,6 +68,7 @@ struct host_definition_s char *community; int version; void *sess_handle; + c_complain_t complaint; uint32_t interval; time_t next_update; data_definition_t **data_list; @@ -116,6 +118,7 @@ static pthread_cond_t host_cond = PTHREAD_COND_INITIALIZER; /* * Private functions */ +/* Many functions to handle the configuration. {{{ */ /* First there are many functions which do configuration stuff. It's a big * bloated and messy, I'm afraid. */ @@ -551,6 +554,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) return (-1); memset (hd, '\0', sizeof (host_definition_t)); hd->version = 2; + C_COMPLAIN_INIT (&hd->complaint); hd->name = strdup (ci->values[0].value.string); if (hd->name == NULL) @@ -653,7 +657,7 @@ static int csnmp_config (oconfig_item_t *ci) return (0); } /* int csnmp_config */ -/* End of the config stuff. Now the interesting part begins */ +/* }}} End of the config stuff. Now the interesting part begins */ static void csnmp_host_close_session (host_definition_t *host) { @@ -1146,7 +1150,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) char *errstr = NULL; snmp_sess_error (host->sess_handle, NULL, NULL, &errstr); - ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s", + + c_complain (LOG_ERR, &host->complaint, + "snmp plugin: host %s: snmp_sess_synch_response failed: %s", host->name, (errstr == NULL) ? "Unknown problem" : errstr); if (res != NULL) @@ -1161,6 +1167,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) } status = 0; assert (res != NULL); + c_release (LOG_INFO, &host->complaint, + "snmp plugin: host %s: snmp_sess_synch_response successful.", + host->name); vb = res->variables; if (vb == NULL) @@ -1626,5 +1635,5 @@ void module_register (void) } /* void module_register */ /* - * vim: shiftwidth=2 softtabstop=2 tabstop=8 + * vim: shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker */ diff --git a/src/types.db b/src/types.db index a3b51dc8..67d8f0d3 100644 --- a/src/types.db +++ b/src/types.db @@ -64,6 +64,7 @@ mysql_threads running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created nfs_procedure value:COUNTER:0:4294967295 nginx_connections value:GAUGE:0:U nginx_requests value:COUNTER:0:134217728 +operations value:COUNTER:0:4294967295 percent percent:GAUGE:0:100.1 pg_blks value:COUNTER:0:U pg_db_size value:GAUGE:0:U @@ -80,6 +81,7 @@ ps_cputime user:COUNTER:0:16000000, syst:COUNTER:0:16000000 ps_pagefaults minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807 ps_rss value:GAUGE:0:9223372036854775807 ps_state value:GAUGE:0:65535 +queue_length value:GAUGE:0:U serial_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 signal_noise value:GAUGE:U:0 signal_power value:GAUGE:U:0 diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h index dccafd15..6b54acec 100644 --- a/src/utils_cmd_flush.h +++ b/src/utils_cmd_flush.h @@ -22,6 +22,8 @@ #ifndef UTILS_CMD_FLUSH_H #define UTILS_CMD_FLUSH_H 1 +#include + int handle_flush (FILE *fh, char *buffer); #endif /* UTILS_CMD_FLUSH_H */ diff --git a/src/utils_cmd_getval.h b/src/utils_cmd_getval.h index 86134cdf..ed9ca9a3 100644 --- a/src/utils_cmd_getval.h +++ b/src/utils_cmd_getval.h @@ -22,6 +22,8 @@ #ifndef UTILS_CMD_GETVAL_H #define UTILS_CMD_GETVAL_H 1 +#include + int handle_getval (FILE *fh, char *buffer); #endif /* UTILS_CMD_GETVAL_H */ diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h index 73146e78..0c72d678 100644 --- a/src/utils_cmd_listval.h +++ b/src/utils_cmd_listval.h @@ -22,6 +22,8 @@ #ifndef UTILS_CMD_LISTVAL_H #define UTILS_CMD_LISTVAL_H 1 +#include + int handle_listval (FILE *fh, char *buffer); #endif /* UTILS_CMD_LISTVAL_H */ diff --git a/src/utils_cmd_putnotif.h b/src/utils_cmd_putnotif.h index 8d5475b9..7e900b50 100644 --- a/src/utils_cmd_putnotif.h +++ b/src/utils_cmd_putnotif.h @@ -22,6 +22,8 @@ #ifndef UTILS_CMD_PUTNOTIF_H #define UTILS_CMD_PUTNOTIF_H 1 +#include + int handle_putnotif (FILE *fh, char *buffer); /* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */ diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h index 9ba52cc8..8460b133 100644 --- a/src/utils_cmd_putval.h +++ b/src/utils_cmd_putval.h @@ -22,6 +22,8 @@ #ifndef UTILS_CMD_PUTVAL_H #define UTILS_CMD_PUTVAL_H 1 +#include + int handle_putval (FILE *fh, char *buffer); #endif /* UTILS_CMD_PUTVAL_H */ diff --git a/src/utils_complain.h b/src/utils_complain.h index e93d823e..b51a81c6 100644 --- a/src/utils_complain.h +++ b/src/utils_complain.h @@ -39,7 +39,8 @@ typedef struct int interval; } c_complain_t; -#define C_COMPLAIN_INIT { 0, 0 } +#define C_COMPLAIN_INIT_STATIC { 0, 0 } +#define C_COMPLAIN_INIT(c) do { (c)->last = 0; (c)->interval = 0; } while (0) /* * NAME diff --git a/src/utils_dns.h b/src/utils_dns.h index b3f08acd..efc79031 100644 --- a/src/utils_dns.h +++ b/src/utils_dns.h @@ -36,6 +36,7 @@ #include "config.h" #include +#include #if HAVE_PCAP_H # include diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c new file mode 100644 index 00000000..8ff025bd --- /dev/null +++ b/src/utils_rrdcreate.c @@ -0,0 +1,413 @@ +/** + * collectd - src/utils_rrdcreate.c + * Copyright (C) 2006-2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "utils_rrdcreate.h" + +#include + +/* + * Private variables + */ +static int rra_timespans[] = +{ + 3600, + 86400, + 604800, + 2678400, + 31622400 +}; +static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans); + +static char *rra_types[] = +{ + "AVERAGE", + "MIN", + "MAX" +}; +static int rra_types_num = STATIC_ARRAY_SIZE (rra_types); + +/* + * Private functions + */ +static void rra_free (int rra_num, char **rra_def) /* {{{ */ +{ + int i; + + for (i = 0; i < rra_num; i++) + { + sfree (rra_def[i]); + } + sfree (rra_def); +} /* }}} void rra_free */ + +/* * * * * * * * * * + * WARNING: Magic * + * * * * * * * * * */ +static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */ + const rrdcreate_config_t *cfg) +{ + char **rra_def; + int rra_num; + + int *rts; + int rts_num; + + int rra_max; + + int span; + + int cdp_num; + int cdp_len; + int i, j; + + char buffer[128]; + + /* The stepsize we use here: If it is user-set, use it. If not, use the + * interval of the value-list. */ + int ss; + + if (cfg->rrarows <= 0) + { + *ret = NULL; + return (-1); + } + + if ((cfg->xff < 0) || (cfg->xff >= 1.0)) + { + *ret = NULL; + return (-1); + } + + ss = (cfg->stepsize > 0) ? cfg->stepsize : vl->interval; + if (ss <= 0) + { + *ret = NULL; + return (-1); + } + + /* Use the configured timespans or fall back to the built-in defaults */ + if (cfg->timespans_num != 0) + { + rts = cfg->timespans; + rts_num = cfg->timespans_num; + } + else + { + rts = rra_timespans; + rts_num = rra_timespans_num; + } + + rra_max = rts_num * rra_types_num; + + if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL) + return (-1); + memset (rra_def, '\0', (rra_max + 1) * sizeof (char *)); + rra_num = 0; + + cdp_len = 0; + for (i = 0; i < rts_num; i++) + { + span = rts[i]; + + if ((span / ss) < cfg->rrarows) + span = ss * cfg->rrarows; + + if (cdp_len == 0) + cdp_len = 1; + else + cdp_len = (int) floor (((double) span) + / ((double) (cfg->rrarows * ss))); + + cdp_num = (int) ceil (((double) span) + / ((double) (cdp_len * ss))); + + for (j = 0; j < rra_types_num; j++) + { + if (rra_num >= rra_max) + break; + + if (ssnprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u", + rra_types[j], cfg->xff, + cdp_len, cdp_num) >= sizeof (buffer)) + { + ERROR ("rra_get: Buffer would have been truncated."); + continue; + } + + rra_def[rra_num++] = sstrdup (buffer); + } + } + + *ret = rra_def; + return (rra_num); +} /* }}} int rra_get */ + +static void ds_free (int ds_num, char **ds_def) /* {{{ */ +{ + int i; + + for (i = 0; i < ds_num; i++) + if (ds_def[i] != NULL) + free (ds_def[i]); + free (ds_def); +} /* }}} void ds_free */ + +static int ds_get (char ***ret, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + const rrdcreate_config_t *cfg) +{ + char **ds_def; + int ds_num; + + char min[32]; + char max[32]; + char buffer[128]; + + ds_def = (char **) malloc (ds->ds_num * sizeof (char *)); + if (ds_def == NULL) + { + char errbuf[1024]; + ERROR ("rrdtool plugin: malloc failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + memset (ds_def, '\0', ds->ds_num * sizeof (char *)); + + for (ds_num = 0; ds_num < ds->ds_num; ds_num++) + { + data_source_t *d = ds->ds + ds_num; + char *type; + int status; + + ds_def[ds_num] = NULL; + + if (d->type == DS_TYPE_COUNTER) + type = "COUNTER"; + else if (d->type == DS_TYPE_GAUGE) + type = "GAUGE"; + else + { + ERROR ("rrdtool plugin: Unknown DS type: %i", + d->type); + break; + } + + if (isnan (d->min)) + { + sstrncpy (min, "U", sizeof (min)); + } + else + ssnprintf (min, sizeof (min), "%lf", d->min); + + if (isnan (d->max)) + { + sstrncpy (max, "U", sizeof (max)); + } + else + ssnprintf (max, sizeof (max), "%lf", d->max); + + status = ssnprintf (buffer, sizeof (buffer), + "DS:%s:%s:%i:%s:%s", + d->name, type, + (cfg->heartbeat > 0) ? cfg->heartbeat : (2 * vl->interval), + min, max); + if ((status < 1) || (status >= sizeof (buffer))) + break; + + ds_def[ds_num] = sstrdup (buffer); + } /* for ds_num = 0 .. ds->ds_num */ + + if (ds_num != ds->ds_num) + { + ds_free (ds_num, ds_def); + return (-1); + } + + *ret = ds_def; + return (ds_num); +} /* }}} int ds_get */ + +#if HAVE_THREADSAFE_LIBRRD +static int srrd_create (const char *filename, /* {{{ */ + unsigned long pdp_step, time_t last_up, + int argc, const char **argv) +{ + int status; + char *filename_copy; + + if ((filename == NULL) || (argv == NULL)) + return (-EINVAL); + + /* Some versions of librrd don't have the `const' qualifier for the first + * argument, so we have to copy the pointer here to avoid warnings. It sucks, + * but what else can we do? :( -octo */ + filename_copy = strdup (filename); + if (filename_copy == NULL) + { + ERROR ("srrd_create: strdup failed."); + return (-ENOMEM); + } + + optind = 0; /* bug in librrd? */ + rrd_clear_error (); + + status = rrd_create_r (filename_copy, pdp_step, last_up, + argc, (void *) argv); + + if (status != 0) + { + WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s", + filename, rrd_get_error ()); + } + + sfree (filename_copy); + + return (status); +} /* }}} int srrd_create */ +/* #endif HAVE_THREADSAFE_LIBRRD */ + +#else /* !HAVE_THREADSAFE_LIBRRD */ +static int srrd_create (const char *filename, /* {{{ */ + unsigned long pdp_step, time_t last_up, + int argc, const char **argv) +{ + int status; + + int new_argc; + char **new_argv; + + char pdp_step_str[16]; + char last_up_str[16]; + + new_argc = 6 + argc; + new_argv = (char **) malloc ((new_argc + 1) * sizeof (char *)); + if (new_argv == NULL) + { + ERROR ("rrdtool plugin: malloc failed."); + return (-1); + } + + if (last_up == 0) + last_up = time (NULL) - 10; + + ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step); + ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up); + + new_argv[0] = "create"; + new_argv[1] = filename; + new_argv[2] = "-s"; + new_argv[3] = pdp_step_str; + new_argv[4] = "-b"; + new_argv[5] = last_up_str; + + memcpy (new_argv + 6, argv, argc * sizeof (char *)); + new_argv[new_argc] = NULL; + + pthread_mutex_lock (&librrd_lock); + optind = 0; /* bug in librrd? */ + rrd_clear_error (); + + status = rrd_create (new_argc, new_argv); + pthread_mutex_unlock (&librrd_lock); + + if (status != 0) + { + WARNING ("rrdtool plugin: rrd_create (%s) failed: %s", + filename, rrd_get_error ()); + } + + sfree (new_argv); + + return (status); +} /* }}} int srrd_create */ +#endif /* !HAVE_THREADSAFE_LIBRRD */ + +/* + * Public functions + */ +int cu_rrd_create_file (const char *filename, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + const rrdcreate_config_t *cfg) +{ + char **argv; + int argc; + char **rra_def; + int rra_num; + char **ds_def; + int ds_num; + int status = 0; + + if (check_create_dir (filename)) + return (-1); + + if ((rra_num = rra_get (&rra_def, vl, cfg)) < 1) + { + ERROR ("cu_rrd_create_file failed: Could not calculate RRAs"); + return (-1); + } + + if ((ds_num = ds_get (&ds_def, ds, vl, cfg)) < 1) + { + ERROR ("cu_rrd_create_file failed: Could not calculate DSes"); + return (-1); + } + + argc = ds_num + rra_num; + + if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL) + { + char errbuf[1024]; + ERROR ("cu_rrd_create_file failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + memcpy (argv, ds_def, ds_num * sizeof (char *)); + memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *)); + argv[ds_num + rra_num] = NULL; + + assert (vl->time > 10); + status = srrd_create (filename, + (cfg->stepsize > 0) ? cfg->stepsize : vl->interval, + vl->time - 10, + argc, (const char **) argv); + + free (argv); + ds_free (ds_num, ds_def); + rra_free (rra_num, rra_def); + + if (status != 0) + { + WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.", + filename, status); + } + else + { + DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".", + filename); + } + + return (status); +} /* }}} int cu_rrd_create_file */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h new file mode 100644 index 00000000..935e4e07 --- /dev/null +++ b/src/utils_rrdcreate.h @@ -0,0 +1,50 @@ +/** + * collectd - src/utils_rrdcreate.h + * Copyright (C) 2008 Florian octo Forster + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_RRDCREATE_H +#define UTILS_RRDCREATE_H 1 + +#include "plugin.h" + +#include + +struct rrdcreate_config_s +{ + int stepsize; + int heartbeat; + int rrarows; + double xff; + + int *timespans; + size_t timespans_num; + + char **consolidation_functions; + size_t consolidation_functions_num; +}; +typedef struct rrdcreate_config_s rrdcreate_config_t; + +int cu_rrd_create_file (const char *filename, + const data_set_t *ds, const value_list_t *vl, + const rrdcreate_config_t *cfg); + +#endif /* UTILS_RRDCREATE_H */ + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/utils_subst.c b/src/utils_subst.c new file mode 100644 index 00000000..05dc8441 --- /dev/null +++ b/src/utils_subst.c @@ -0,0 +1,82 @@ +/** + * collectd - src/utils_subst.c + * Copyright (C) 2008 Sebastian Harl + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Sebastian "tokkee" Harl + **/ + +/* + * This module provides functions for string substitution. + */ + +#include "collectd.h" +#include "common.h" + +char *subst (char *buf, size_t buflen, const char *string, int off1, int off2, + const char *replacement) +{ + char *buf_ptr = buf; + size_t len = buflen; + + if ((NULL == buf) || (0 >= buflen) || (NULL == string) + || (0 > off1) || (0 > off2) || (off1 > off2) + || (NULL == replacement)) + return NULL; + + sstrncpy (buf_ptr, string, (off1 + 1 > buflen) ? buflen : off1 + 1); + buf_ptr += off1; + len -= off1; + + if (0 >= len) + return buf; + + sstrncpy (buf_ptr, replacement, len); + buf_ptr += strlen (replacement); + len -= strlen (replacement); + + if (0 >= len) + return buf; + + sstrncpy (buf_ptr, string + off2, len); + return buf; +} /* subst */ + +char *asubst (const char *string, int off1, int off2, const char *replacement) +{ + char *buf; + int len; + + char *ret; + + if ((NULL == string) || (0 > off1) || (0 > off2) || (off1 > off2) + || (NULL ==replacement)) + return NULL; + + len = off1 + strlen (replacement) + strlen (string) - off2 + 1; + + buf = (char *)malloc (len); + if (NULL == buf) + return NULL; + + ret = subst (buf, len, string, off1, off2, replacement); + if (NULL == ret) + free (buf); + return ret; +} /* asubst */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ + diff --git a/src/utils_subst.h b/src/utils_subst.h new file mode 100644 index 00000000..bba33af4 --- /dev/null +++ b/src/utils_subst.h @@ -0,0 +1,81 @@ +/** + * collectd - src/utils_subst.h + * Copyright (C) 2008 Sebastian Harl + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Sebastian "tokkee" Harl + **/ + +/* + * This module provides functions for string substitution. + */ + +#ifndef UTILS_SUBST_H +#define UTILS_SUBST_H 1 + +#include + +/* + * subst: + * + * Replace a substring of a string with the specified replacement text. The + * resulting string is stored in the buffer pointed to by 'buf' of length + * 'buflen'. Upon success, the buffer will always be null-terminated. The + * result may be truncated if the buffer is too small. + * + * The substring to be replaces is identified by the two offsets 'off1' and + * 'off2' where 'off1' specifies the offset to the beginning of the substring + * and 'off2' specifies the offset to the first byte after the substring. + * + * The minimum buffer size to store the complete return value (including the + * terminating '\0' character) thus has to be: + * off1 + strlen(replacement) + strlen(string) - off2 + 1 + * + * Example: + * + * 01234567890 + * string = "foo_____bar" + * ^ ^ + * | | + * off1 off2 + * + * off1 = 3 + * off2 = 8 + * + * replacement = " - " + * + * -> "foo - bar" + * + * The function returns 'buf' on success, NULL else. + */ +char *subst (char *buf, size_t buflen, const char *string, int off1, int off2, + const char *replacement); + +/* + * asubst: + * + * This function is very similar to subst(). It differs in that it + * automatically allocates the memory required for the return value which the + * user then has to free himself. + * + * Returns the newly allocated result string on success, NULL else. + */ +char *asubst (const char *string, int off1, int off2, const char *replacement); + +#endif /* UTILS_SUBST_H */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ +