From: Florian Forster Date: Tue, 2 Dec 2008 22:31:32 +0000 (+0100) Subject: Merge branch 'collectd-4.5' X-Git-Tag: collectd-4.6.0~137 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=98fef31471541485002b5b87afa8991a280190d6;hp=05a85bbebdef9f8e7c1b0c09d5a2145c2f2d10af 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..c8ce0698 100644 --- a/configure.in +++ b/configure.in @@ -610,8 +610,13 @@ if test "x$fp_layout_type" = "xunknown"; then [[[[ #include #include -#include #include +#if HAVE_STDINT_H +# include +#endif +#if HAVE_STDBOOL_H +# include +#endif ]]]], [[[[ uint64_t i0; @@ -649,8 +654,13 @@ if test "x$fp_layout_type" = "xunknown"; then [[[[ #include #include -#include #include +#if HAVE_STDINT_H +# include +#endif +#if HAVE_STDBOOL_H +# include +#endif #define endianflip(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \ (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \ (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \ @@ -696,8 +706,13 @@ if test "x$fp_layout_type" = "xunknown"; then [[[[ #include #include -#include #include +#if HAVE_STDINT_H +# include +#endif +#if HAVE_STDBOOL_H +# include +#endif #define intswap(A) ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \ (((uint64_t)(A) & 0x00000000ffffffffLL) << 32)) ]]]], @@ -922,102 +937,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 +963,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 +1069,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) -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" -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" + 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 -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" +if test "x$with_libesmtp" = "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,65 +1306,240 @@ 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" + if test "x$withval" = "xno" then - if test -d "$withval/lib" - then - liboping_LDFLAGS="$LDFLAGS -L$withval/lib" + 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 @@ -1502,7 +1579,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 +1695,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 +1735,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 +1925,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" - ]) -fi -if test "x$with_libiptc" = "xyes" -then - SAVE_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS $KERNEL_CFLAGS" - 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" - ]) + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libpq_cppflags" - CFLAGS=$SAVE_CFLAGS + AC_CHECK_HEADERS(libpq-fe.h, [], + [with_libpq="no (libpq-fe.h not found)"], []) + + CPPFLAGS="$SAVE_CPPFLAGS" 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.]) -fi + with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null` + pg_config_status=$? -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" + if test $pg_config_status -eq 0 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" + if test -n "$with_libpq_libdir"; then + for dir in $with_libpq_libdir; do + with_libpq_ldflags="$with_libpq_ldflags -L$dir" + done 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" + AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) fi + + 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 -if test "x$with_libnetsnmp" = "xyes" +if test "x$with_libpq" = "xyes" then - with_snmp_libs=`$with_snmp_config --libs 2>/dev/null` - snmp_config_status=$? + 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") +# }}} - if test $snmp_config_status -ne 0 +# --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="no ($with_snmp_config failed)" + LDFLAGS="$LDFLAGS -L$withval/lib" + CPPFLAGS="$CPPFLAGS -I$withval/include" + with_libpthread="yes" else - AC_CHECK_LIB(netsnmp, init_snmp, - [with_libnetsnmp="yes"], - [with_libnetsnmp="no (libnetsnmp not found)"], - [$with_snmp_libs]) + if test "x$withval" = "xno" + then + with_libpthread="no (disabled)" + fi fi -fi -if test "x$with_libnetsnmp" = "xyes" +], [with_libpthread="yes"]) +if test "x$with_libpthread" = "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) + AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], []) fi -AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes") - -PKG_CHECK_MODULES([LIBNOTIFY], [libnotify], - [with_libnotify="yes"], - [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"]) - -with_libupsclient="no (pkg-config isn't available)" -with_libupsclient_cflags="" -with_libupsclient_libs="" -if test "x$PKG_CONFIG" != "x" +if test "x$with_libpthread" = "xyes" then - pkg-config --exists 'libupsclient' 2>/dev/null - if test "$?" = "0" - then - with_libupsclient="yes" - else - with_libupsclient="no (pkg-config doesn't know library)" - fi + AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"]) fi -if test "x$with_libupsclient" = "xyes" +if test "x$with_libpthread" = "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 + 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") +# }}} + +# --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_libupsclient="no" + librrd_cflags="-I$withval/include" + librrd_ldflags="-L$withval/lib" + with_librrd="yes" + else + with_librrd="$withval" fi -fi -if test "x$with_libupsclient" = "xyes" +], [with_librrd="yes"]) +if test "x$with_librrd" = "xyes" then SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" + SAVE_LDFLAGS="$LDFLAGS" - AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"]) + CPPFLAGS="$CPPFLAGS $librrd_cflags" + LDFLAGS="$LDFLAGS $librrd_ldflags" + + AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"]) CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" fi -if test "x$with_libupsclient" = "xyes" +if test "x$with_librrd" = "xyes" then SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" - LDFLAGS="$LDFLAGS $with_libupsclient_libs" + CPPFLAGS="$CPPFLAGS $librrd_cflags" + LDFLAGS="$LDFLAGS $librrd_ldflags" - AC_CHECK_LIB(upsclient, upscli_connect, - [with_libupsclient="yes"], - [with_libupsclient="no (symbol upscli_connect not found)"]) + 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 + AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"]) + else + 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 - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags" - - AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [], -[#include -#include -#include ]) - - CPPFLAGS="$SAVE_CPPFLAGS" + 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$with_libupsclient" = "xyes" +if test "x$librrd_threadsafe" = "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) + AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).]) 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-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" \ - && 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" + if test "x$withval" = "xno" then - with_libxmms="no" + with_libsensors="no" else - with_libxmms="yes" - fi; fi + 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_libxmms="yes" + if test "x$ac_system" = "xLinux" + then + with_libsensors="yes" + else + with_libsensors="no (Linux only library)" + fi ]) -if test "x$with_libxmms" = "xyes" +if test "x$with_libsensors" = "xyes" then - with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null` - xmms_config_status=$? + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_sensors_cflags" - 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=$? +# 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)"]) - if test $xmms_config_status -ne 0 - then - with_libxmms="no" - fi + CPPFLAGS="$SAVE_CPPFLAGS" fi -if test "x$with_libxmms" = "xyes" +if test "x$with_libsensors" = "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) - ], + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_sensors_cflags" + LDFLAGS="$LDFLAGS $with_sensors_ldflags" + + AC_CHECK_LIB(sensors, sensors_init, [ - with_libxmms="no" + AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).]) ], - [$with_xmms_libs]) + [with_libsensors="no (libsensors not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" fi -with_libxmms_numeric=0 -if test "x$with_libxmms" = "xyes" +if test "x$with_libsensors" = "xyes" then - with_libxmms_numeric=1 + 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 -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 ### +AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes") +# }}} -with_libnetlink_cflags="" -with_libnetlink_libs="-lnetlink" -AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])], +# --with-libstatgrab {{{ +with_libstatgrab_cflags="" +with_libstatgrab_ldflags="" +AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])], [ - 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$withval" != "xno" \ + && test "x$withval" != "xyes" + then + with_libstatgrab_cflags="-I$withval/include" + with_libstatgrab_ldflags="-L$withval/lib" + with_libstatgrab="yes" + else + with_libstatgrab="$withval" + fi ], [ - if test "x$ac_system" = "xLinux" - then - with_libnetlink="yes" - else - with_libnetlink="no (Linux only library)" - fi + if test "x$ac_system" = "xunknown" + then + with_libstatgrab="yes" + else + with_libstatgrab="no" + 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 +2559,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 +2951,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 +2959,7 @@ 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([hddtemp], [yes], [Query hddtempd]) AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters]) @@ -2723,6 +2969,8 @@ AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics]) AC_PLUGIN([libvirt], [$plugin_libvirt], [Virtual machine statistics]) AC_PLUGIN([load], [$plugin_load], [System load]) AC_PLUGIN([logfile], [yes], [File logging plugin]) +AC_PLUGIN([match_regex], [yes], [The regex match]) +AC_PLUGIN([match_value], [yes], [The value match]) AC_PLUGIN([mbmon], [yes], [Query mbmond]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) @@ -2736,19 +2984,22 @@ 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]) AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin]) AC_PLUGIN([tail], [yes], [Parsing of logfiles]) AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics]) +AC_PLUGIN([target_set], [yes], [The set target]) AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics]) AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics]) AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics]) @@ -2797,12 +3048,12 @@ fi AC_SUBST(PERL_BINDINGS) AC_SUBST(PERL_BINDINGS_OPTIONS) -AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile) +AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/libcollectdclient/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 +3085,7 @@ cat < plugin above provides C and C which are both voltages, you can use a hyphen to add a "type instance" to the type. For a list of already defined "types" look at the F file in -collectd's library path, e.Eg. F. +collectd's shared data directory, e.Eg. F. =item B I diff --git a/contrib/fedora/collectd.spec b/contrib/fedora/collectd.spec index 89ce1237..35eb8bec 100644 --- a/contrib/fedora/collectd.spec +++ b/contrib/fedora/collectd.spec @@ -232,7 +232,7 @@ exit 0 %attr(0644,root,root) %{_libdir}/%{name}/wireless.so* %attr(0644,root,root) %{_libdir}/%{name}/wireless.la -%attr(0644,root,root) %{_libdir}/%{name}/types.db +%attr(0644,root,root) %{_datadir}/%{name}/types.db %dir /var/lib/collectd diff --git a/contrib/oracle/create_schema.ddl b/contrib/oracle/create_schema.ddl new file mode 100644 index 00000000..e4dc54f2 --- /dev/null +++ b/contrib/oracle/create_schema.ddl @@ -0,0 +1,243 @@ +-- Description +-------------- +-- This will create a schema to provide collectd with the required permissions +-- and space for statistic data. +-- The idea is to store the output of some expensive queries in static tables +-- and fill these tables with dbms_scheduler jobs as often as necessary. +-- collectd will then just read from the static tables. This will reduces the +-- chance that your system will be killed by excessive monitoring queries and +-- gives the dba control on the interval the information provided to collectd +-- will be refreshed. You have to create a dbms_scheduler job for each of the +-- schemas you what to monitor for object-space-usage. See the example below. +-- +-- Requirements +--------------- +-- make sure you have: +-- write permission in $PWD +-- you have GID of oracle software owner +-- set $ORACLE_HOME +-- set $ORACLE_SID +-- DB is up an running in RW mode +-- execute like this: +-- sqlplus /nolog @ create_collectd-schema.dll + +spool create_collectd-schema.log +connect / as sysdba + +-- Create user, tablespace and permissions + +CREATE TABLESPACE "COLLECTD-TBS" + DATAFILE SIZE 30M + AUTOEXTEND ON + NEXT 10M + MAXSIZE 300M + LOGGING + EXTENT MANAGEMENT LOCAL + SEGMENT SPACE MANAGEMENT AUTO + DEFAULT NOCOMPRESS; + +CREATE ROLE "CREATE_COLLECTD_SCHEMA" NOT IDENTIFIED; +GRANT CREATE JOB TO "CREATE_COLLECTD_SCHEMA"; +GRANT CREATE SEQUENCE TO "CREATE_COLLECTD_SCHEMA"; +GRANT CREATE SYNONYM TO "CREATE_COLLECTD_SCHEMA"; +GRANT CREATE TABLE TO "CREATE_COLLECTD_SCHEMA"; +GRANT CREATE VIEW TO "CREATE_COLLECTD_SCHEMA"; +GRANT CREATE PROCEDURE TO "CREATE_COLLECTD_SCHEMA"; + +CREATE USER "COLLECTDU" + PROFILE "DEFAULT" + IDENTIFIED BY "Change_me-1st" + PASSWORD EXPIRE + DEFAULT TABLESPACE "COLLECTD-TBS" + TEMPORARY TABLESPACE "TEMP" + QUOTA UNLIMITED ON "COLLECTD-TBS" + ACCOUNT UNLOCK; + +GRANT "CONNECT" TO "COLLECTDU"; +GRANT "SELECT_CATALOG_ROLE" TO "COLLECTDU"; +GRANT "CREATE_COLLECTD_SCHEMA" TO "COLLECTDU"; +GRANT analyze any TO "COLLECTDU"; +GRANT select on dba_tables TO "COLLECTDU"; +GRANT select on dba_lobs TO "COLLECTDU"; +GRANT select on dba_indexes TO "COLLECTDU"; +GRANT select on dba_segments TO "COLLECTDU"; +GRANT select on dba_tab_columns TO "COLLECTDU"; +GRANT select on dba_free_space TO "COLLECTDU"; +GRANT select on dba_data_files TO "COLLECTDU"; +-- Create tables and indexes + +alter session set current_schema=collectdu; + +create table c_tbs_usage ( + tablespace_name varchar2(30), + bytes_free number, + bytes_used number, + CONSTRAINT "C_TBS_USAGE_UK1" UNIQUE ("TABLESPACE_NAME") USING INDEX + TABLESPACE "COLLECTD-TBS" ENABLE) + TABLESPACE "COLLECTD-TBS"; + +CREATE TABLE "COLLECTDU"."C_TBL_SIZE" ( + "OWNER" VARCHAR2(30 BYTE), + "TABLE_NAME" VARCHAR2(30 BYTE), + "BYTES" NUMBER, + CONSTRAINT "C_TBL_SIZE_UK1" UNIQUE ("OWNER", "TABLE_NAME") + USING INDEX TABLESPACE "COLLECTD-TBS" ENABLE) + TABLESPACE "COLLECTD-TBS" ; + + +create or replace PROCEDURE get_object_size(owner IN VARCHAR2) AS + +v_owner VARCHAR2(30) := owner; + +l_free_blks NUMBER; +l_total_blocks NUMBER; +l_total_bytes NUMBER; +l_unused_blocks NUMBER; +l_unused_bytes NUMBER; +l_lastusedextfileid NUMBER; +l_lastusedextblockid NUMBER; +l_last_used_block NUMBER; + +CURSOR cur_tbl IS +SELECT owner, + TABLE_NAME +FROM dba_tables +WHERE owner = v_owner; + +CURSOR cur_idx IS +SELECT owner, + index_name, + TABLE_NAME +FROM dba_indexes +WHERE owner = v_owner; + +CURSOR cur_lob IS +SELECT owner, + segment_name, + TABLE_NAME +FROM dba_lobs +WHERE owner = v_owner; + +BEGIN + + DELETE FROM c_tbl_size + WHERE owner = v_owner; + COMMIT; + + FOR r_tbl IN cur_tbl + LOOP + BEGIN + dbms_space.unused_space(segment_owner => 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/contrib/redhat/collectd.conf b/contrib/redhat/collectd.conf index 7a026fac..0aaaee31 100644 --- a/contrib/redhat/collectd.conf +++ b/contrib/redhat/collectd.conf @@ -9,7 +9,7 @@ FQDNLookup true BaseDir "/var/lib/collectd" PIDFile "/var/run/collectd.pid" PluginDir "/usr/lib/collectd" -TypesDB "/usr/lib/collectd/types.db" +TypesDB "/usr/share/collectd/types.db" Interval 10 ReadThreads 5 diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index bce76473..62acc184 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -85,7 +85,7 @@ cp contrib/redhat/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf sed -i 's:#BaseDir "/usr/var/lib/collectd":BaseDir "/var/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf sed -i 's:#PIDFile "/usr/var/run/collectd.pid":PIDFile "/var/run/collectd.pid":' $RPM_BUILD_ROOT/etc/collectd.conf sed -i 's:#PluginDir "/usr/lib/collectd":PluginDir "/usr/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf -sed -i 's:#TypesDB "/usr/lib/collectd/types.db":TypesDB "/usr/lib/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf +sed -i 's:#TypesDB "/usr/share/collectd/types.db":TypesDB "/usr/share/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf sed -i 's:#Interval 10:Interval 10:' $RPM_BUILD_ROOT/etc/collectd.conf sed -i 's:#ReadThreads 5:ReadThreads 5:' $RPM_BUILD_ROOT/etc/collectd.conf ##Move config contribs @@ -247,7 +247,7 @@ exit 0 %attr(0644,root,root) %{_libdir}/%{name}/wireless.so* %attr(0644,root,root) %{_libdir}/%{name}/wireless.la -%attr(0644,root,root) %{_libdir}/%{name}/types.db +%attr(0644,root,root) %{_datadir}/%{name}/types.db %exclude %{_libdir}/perl5/5.8.8/%{_arch}-linux-thread-multi/perllocal.pod %attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd.pm diff --git a/src/Makefile.am b/src/Makefile.am index 47268f51..a631aa5f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = +SUBDIRS = libcollectdclient if BUILD_WITH_OWN_LIBIPTC SUBDIRS += libiptc endif @@ -29,6 +29,8 @@ bin_PROGRAMS = collectd-nagios collectd_SOURCES = collectd.c collectd.h \ common.c common.h \ configfile.c configfile.h \ + filter_chain.c filter_chain.h \ + meta_data.c meta_data.h \ plugin.c plugin.h \ utils_avltree.c utils_avltree.h \ utils_cache.c utils_cache.h \ @@ -38,6 +40,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 @@ -90,6 +93,8 @@ collectd_nagios_LDFLAGS = if BUILD_WITH_LIBSOCKET collectd_nagios_LDFLAGS += -lsocket endif +collectd_nagios_LDADD = libcollectdclient/libcollectdclient.la +collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la pkglib_LTLIBRARIES = @@ -184,6 +189,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 +279,14 @@ 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_HDDTEMP pkglib_LTLIBRARIES += hddtemp.la hddtemp_la_SOURCES = hddtemp.c @@ -370,6 +393,24 @@ collectd_LDADD += "-dlopen" logfile.la collectd_DEPENDENCIES += logfile.la endif +if BUILD_PLUGIN_MATCH_REGEX +pkglib_LTLIBRARIES += match_regex.la +match_regex_la_SOURCES = match_regex.c +match_regex_la_CPPFLAGS = $(BUILD_WITH_LIBPCRE_CFLAGS) +match_regex_la_LDFLAGS = -module -avoid-version \ + $(BUILD_WITH_LIBPCRE_LIBS) +collectd_LDADD += "-dlopen" match_regex.la +collectd_DEPENDENCIES += match_regex.la +endif + +if BUILD_PLUGIN_MATCH_VALUE +pkglib_LTLIBRARIES += match_value.la +match_value_la_SOURCES = match_value.c +match_value_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" match_value.la +collectd_DEPENDENCIES += match_value.la +endif + if BUILD_PLUGIN_MBMON pkglib_LTLIBRARIES += mbmon.la mbmon_la_SOURCES = mbmon.c @@ -529,6 +570,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 +640,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) @@ -680,6 +742,14 @@ collectd_LDADD += "-dlopen" tape.la collectd_DEPENDENCIES += tape.la endif +if BUILD_PLUGIN_TARGET_SET +pkglib_LTLIBRARIES += target_set.la +target_set_la_SOURCES = target_set.c +target_set_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" target_set.la +collectd_DEPENDENCIES += target_set.la +endif + if BUILD_PLUGIN_TCPCONNS pkglib_LTLIBRARIES += tcpconns.la tcpconns_la_SOURCES = tcpconns.c @@ -818,7 +888,7 @@ install-exec-hook: else \ $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \ fi; \ - cp -f $(srcdir)/types.db $(DESTDIR)$(pkglibdir)/; $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + $(INSTALL) -m 0644 $(srcdir)/types.db $(DESTDIR)$(pkgdatadir)/types.db; $(INSTALL) -m 0644 $(srcdir)/postgresql_default.conf \ $(DESTDIR)$(pkgdatadir)/postgresql_default.conf; diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index ca72a68c..c9157918 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -1,15 +1,67 @@ -#include "config.h" +/** + * collectd-nagios - src/collectd-nagios.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 + **/ + +/* Set to C99 and POSIX code */ +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +#endif +#ifndef _REENTRANT +# define _REENTRANT +#endif + +/* Disable non-standard extensions */ +#ifdef _BSD_SOURCE +# undef _BSD_SOURCE +#endif +#ifdef _SVID_SOURCE +# undef _SVID_SOURCE +#endif +#ifdef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +#if !defined(__GNUC__) || !__GNUC__ +# define __attribute__(x) /**/ +#endif #include #include #include #include #include +#include #include #include #include +#include "config.h" +#include "libcollectdclient/client.h" + /* * This is copied directly from collectd.h. Make changes there! */ @@ -69,6 +121,22 @@ static int consolitation_g = CON_NONE; static char **match_ds_g = NULL; static int match_ds_num_g = 0; +/* `strdup' is an XSI extension. I don't want to pull in all of XSI just for + * that, so here's an own implementation.. It's easy enough. The GCC attributes + * are supposed to get good performance.. -octo */ +__attribute__((malloc, nonnull (1))) +static char *cn_strdup (const char *str) /* {{{ */ +{ + size_t strsize; + char *ret; + + strsize = strlen (str) + 1; + ret = (char *) malloc (strsize); + if (ret != NULL) + memcpy (ret, str, strsize); + return (ret); +} /* }}} char *cn_strdup */ + static int ignore_ds (const char *name) { int i; @@ -124,7 +192,7 @@ static void parse_range (char *string, range_t *range) range->max = atof (max_ptr); } /* void parse_range */ -int match_range (range_t *range, double value) +static int match_range (range_t *range, double value) { int ret = 0; @@ -134,164 +202,7 @@ int match_range (range_t *range, double value) ret = 1; return (((ret - range->invert) == 0) ? 0 : 1); -} - -static int get_values (int *ret_values_num, double **ret_values, - char ***ret_values_names) -{ - struct sockaddr_un sa; - int status; - int fd; - FILE *fh_in, *fh_out; - char buffer[4096]; - - int values_num; - double *values; - char **values_names; - - int i; - int j; - - fd = socket (PF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - { - fprintf (stderr, "socket failed: %s\n", - strerror (errno)); - return (-1); - } - - memset (&sa, '\0', sizeof (sa)); - sa.sun_family = AF_UNIX; - strncpy (sa.sun_path, socket_file_g, - sizeof (sa.sun_path) - 1); - - status = connect (fd, (struct sockaddr *) &sa, sizeof (sa)); - if (status != 0) - { - fprintf (stderr, "connect failed: %s\n", - strerror (errno)); - return (-1); - } - - fh_in = fdopen (fd, "r"); - if (fh_in == NULL) - { - fprintf (stderr, "fdopen failed: %s\n", - strerror (errno)); - close (fd); - return (-1); - } - - fh_out = fdopen (fd, "w"); - if (fh_out == NULL) - { - fprintf (stderr, "fdopen failed: %s\n", - strerror (errno)); - fclose (fh_in); - return (-1); - } - - fprintf (fh_out, "GETVAL %s/%s\n", hostname_g, value_string_g); - fflush (fh_out); - - if (fgets (buffer, sizeof (buffer), fh_in) == NULL) - { - fprintf (stderr, "fgets failed: %s\n", - strerror (errno)); - fclose (fh_in); - fclose (fh_out); - return (-1); - } - - { - char *ptr = strchr (buffer, ' '); - - if (ptr != NULL) - *ptr = '\0'; - - values_num = atoi (buffer); - if (values_num < 1) - return (-1); - } - - values = (double *) malloc (values_num * sizeof (double)); - if (values == NULL) - { - fprintf (stderr, "malloc failed: %s\n", - strerror (errno)); - return (-1); - } - - values_names = (char **) malloc (values_num * sizeof (char *)); - if (values_names == NULL) - { - fprintf (stderr, "malloc failed: %s\n", - strerror (errno)); - free (values); - return (-1); - } - memset (values_names, 0, values_num * sizeof (char *)); - - i = 0; /* index of the values returned by the server */ - j = 0; /* number of values in `values_names' and `values' */ - while (fgets (buffer, sizeof (buffer), fh_in) != NULL) - { - do /* while (0) */ - { - char *key; - char *value; - char *endptr; - - key = buffer; - - value = strchr (key, '='); - if (value == NULL) - { - fprintf (stderr, "Cannot parse line: %s\n", buffer); - break; - } - *value = 0; - value++; - - if (ignore_ds (key) != 0) - break; - - endptr = NULL; - errno = 0; - values[j] = strtod (value, &endptr); - if ((endptr == value) || (errno != 0)) - { - fprintf (stderr, "Could not parse buffer " - "as number: %s\n", value); - break; - } - - values_names[j] = strdup (key); - if (values_names[j] == NULL) - { - fprintf (stderr, "strdup failed.\n"); - break; - } - j++; - } while (0); - - i++; - if (i >= values_num) - break; - } - /* Set `values_num' to the number of values actually stored in the - * array. */ - values_num = j; - - fclose (fh_in); fh_in = NULL; fd = -1; - fclose (fh_out); fh_out = NULL; - - *ret_values_num = values_num; - *ret_values = values; - *ret_values_names = values_names; - - return (0); -} /* int get_values */ +} /* int match_range */ static void usage (const char *name) { @@ -319,16 +230,21 @@ static void usage (const char *name) exit (1); } /* void usage */ -int do_check_con_none (int values_num, double *values, char **values_names) +static int do_check_con_none (int values_num, + double *values, char **values_names) { - int i; - int num_critical = 0; int num_warning = 0; int num_okay = 0; + const char *status_str = "UNKNOWN"; + int status_code = RET_UNKNOWN; + int i; for (i = 0; i < values_num; i++) { + if (ignore_ds (values_names[i])) + continue; + if (isnan (values[i])) num_warning++; else if (match_range (&range_critical_g, values[i]) != 0) @@ -339,35 +255,57 @@ int do_check_con_none (int values_num, double *values, char **values_names) num_okay++; } - printf ("%i critical, %i warning, %i okay", + if ((num_critical == 0) && (num_warning == 0) && (num_okay == 0)) + { + printf ("WARNING: No defined values found\n"); + return (RET_WARNING); + } + else if ((num_critical == 0) && (num_warning == 0)) + { + status_str = "OKAY"; + status_code = RET_OKAY; + } + else if (num_critical == 0) + { + status_str = "WARNING"; + status_code = RET_WARNING; + } + else + { + status_str = "CRITICAL"; + status_code = RET_CRITICAL; + } + + printf ("%s: %i critical, %i warning, %i okay", status_str, num_critical, num_warning, num_okay); if (values_num > 0) { printf (" |"); for (i = 0; i < values_num; i++) - printf (" %s=%lf;;;;", values_names[i], values[i]); + printf (" %s=%g;;;;", values_names[i], values[i]); } printf ("\n"); - if ((num_critical != 0) || (values_num == 0)) - return (RET_CRITICAL); - else if (num_warning != 0) - return (RET_WARNING); - - return (RET_OKAY); + return (status_code); } /* int do_check_con_none */ -int do_check_con_average (int values_num, double *values, char **values_names) +static int do_check_con_average (int values_num, + double *values, char **values_names) { int i; double total; int total_num; double average; + const char *status_str = "UNKNOWN"; + int status_code = RET_UNKNOWN; total = 0.0; total_num = 0; for (i = 0; i < values_num; i++) { + if (ignore_ds (values_names[i])) + continue; + if (!isnan (values[i])) { total += values[i]; @@ -376,35 +314,52 @@ int do_check_con_average (int values_num, double *values, char **values_names) } if (total_num == 0) - average = NAN; - else - average = total / total_num; - printf ("%lf average |", average); - for (i = 0; i < values_num; i++) - printf (" %s=%lf;;;;", values_names[i], values[i]); - - if (total_num == 0) + { + printf ("WARNING: No defined values found\n"); return (RET_WARNING); + } - if (isnan (average) - || match_range (&range_critical_g, average)) - return (RET_CRITICAL); + average = total / total_num; + + if (match_range (&range_critical_g, average) != 0) + { + status_str = "CRITICAL"; + status_code = RET_CRITICAL; + } else if (match_range (&range_warning_g, average) != 0) - return (RET_WARNING); + { + status_str = "WARNING"; + status_code = RET_WARNING; + } + else + { + status_str = "OKAY"; + status_code = RET_OKAY; + } + + printf ("%s: %g average |", status_str, average); + for (i = 0; i < values_num; i++) + printf (" %s=%g;;;;", values_names[i], values[i]); + printf ("\n"); - return (RET_OKAY); + return (status_code); } /* int do_check_con_average */ -int do_check_con_sum (int values_num, double *values, char **values_names) +static int do_check_con_sum (int values_num, double *values, char **values_names) { int i; double total; int total_num; + const char *status_str = "UNKNOWN"; + int status_code = RET_UNKNOWN; total = 0.0; total_num = 0; for (i = 0; i < values_num; i++) { + if (ignore_ds (values_names[i])) + continue; + if (!isnan (values[i])) { total += values[i]; @@ -420,47 +375,94 @@ int do_check_con_sum (int values_num, double *values, char **values_names) if (match_range (&range_critical_g, total) != 0) { - printf ("CRITICAL: Sum = %lf\n", total); - return (RET_CRITICAL); + status_str = "CRITICAL"; + status_code = RET_CRITICAL; } else if (match_range (&range_warning_g, total) != 0) { - printf ("WARNING: Sum = %lf\n", total); - return (RET_WARNING); + status_str = "WARNING"; + status_code = RET_WARNING; } else { - printf ("OKAY: Sum = %lf\n", total); - return (RET_OKAY); + status_str = "OKAY"; + status_code = RET_OKAY; } - return (RET_UNKNOWN); + printf ("%s: %g sum |", status_str, total); + for (i = 0; i < values_num; i++) + printf (" %s=%g;;;;", values_names[i], values[i]); + printf ("\n"); + + return (status_code); } /* int do_check_con_sum */ -int do_check (void) +static int do_check (void) { - double *values; + lcc_connection_t *connection; + gauge_t *values; char **values_names; - int values_num; + size_t values_num; + char address[1024]; + char ident_str[1024]; + lcc_identifier_t ident; + size_t i; + int status; - if (get_values (&values_num, &values, &values_names) != 0) + snprintf (address, sizeof (address), "unix:%s", socket_file_g); + address[sizeof (address) - 1] = 0; + + snprintf (ident_str, sizeof (ident_str), "%s/%s", + hostname_g, value_string_g); + ident_str[sizeof (ident_str) - 1] = 0; + + connection = NULL; + status = lcc_connect (address, &connection); + if (status != 0) + { + printf ("ERROR: Connecting to daemon at %s failed.\n", + socket_file_g); + return (RET_CRITICAL); + } + + memset (&ident, 0, sizeof (ident)); + status = lcc_string_to_identifier (connection, &ident, ident_str); + if (status != 0) { - fputs ("ERROR: Cannot get values from daemon\n", stdout); + printf ("ERROR: Creating an identifier failed: %s.\n", + lcc_strerror (connection)); + LCC_DESTROY (connection); return (RET_CRITICAL); } + status = lcc_getval (connection, &ident, + &values_num, &values, &values_names); + if (status != 0) + { + printf ("ERROR: Retrieving values from the daemon failed: %s.\n", + lcc_strerror (connection)); + LCC_DESTROY (connection); + return (RET_CRITICAL); + } + + LCC_DESTROY (connection); + + status = RET_UNKNOWN; if (consolitation_g == CON_NONE) - return (do_check_con_none (values_num, values, values_names)); + status = do_check_con_none (values_num, values, values_names); else if (consolitation_g == CON_AVERAGE) - return (do_check_con_average (values_num, values, values_names)); + status = do_check_con_average (values_num, values, values_names); else if (consolitation_g == CON_SUM) - return (do_check_con_sum (values_num, values, values_names)); + status = do_check_con_sum (values_num, values, values_names); free (values); - free (values_names); /* FIXME? */ + if (values_names != NULL) + for (i = 0; i < values_num; i++) + free (values_names[i]); + free (values_names); - return (RET_UNKNOWN); -} + return (status); +} /* int do_check */ int main (int argc, char **argv) { @@ -520,10 +522,10 @@ int main (int argc, char **argv) return (RET_UNKNOWN); } match_ds_g = tmp; - match_ds_g[match_ds_num_g] = strdup (optarg); + match_ds_g[match_ds_num_g] = cn_strdup (optarg); if (match_ds_g[match_ds_num_g] == NULL) { - fprintf (stderr, "strdup failed: %s\n", + fprintf (stderr, "cn_strdup failed: %s\n", strerror (errno)); return (RET_UNKNOWN); } diff --git a/src/collectd.conf.in b/src/collectd.conf.in index eaafff13..f6b75e7d 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -9,7 +9,7 @@ FQDNLookup true #BaseDir "@prefix@/var/lib/@PACKAGE_NAME@" #PIDFile "@prefix@/var/run/@PACKAGE_NAME@.pid" #PluginDir "@prefix@/lib/@PACKAGE_NAME@" -#TypesDB "@prefix@/lib/@PACKAGE_NAME@/types.db" +#TypesDB "@prefix@/share/@PACKAGE_NAME@/types.db" #Interval 10 #ReadThreads 5 @@ -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 @@ -68,6 +69,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 @@ -80,7 +82,7 @@ FQDNLookup true @BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal @BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock @BUILD_PLUGIN_USERS_TRUE@LoadPlugin users -@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid +#LoadPlugin uuid @BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem @BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver @BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless @@ -110,6 +112,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,6 +167,7 @@ FQDNLookup true # Name "*.conf" # MTime "-5m" # Size "+10k" +# Recursive true # # @@ -318,6 +340,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 @@ -423,3 +452,22 @@ FQDNLookup true # Verbose false # +# * * * * * * * * * * * * * +# * FILTER CONFIGURATION * +# * * * * * * * * * * * * * + +# The following configures collectd's filtering mechanism. Before changing +# anything in this section, please read the `FILTER CONFIGURATION' section in +# the collectd.conf(5) manual page. + +# Load required matches: +#@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex + +# Load required targets: + +# The following block demonstrates the default behavior if no filtering is +# configured at all: All values will be sent to all available write plugins. + +# +# Target "write" +# diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 0df5c3b7..61ba481b 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 @@ -797,6 +1047,23 @@ Password needed to log into the database. Select this database. Defaults to I which is a perfectly reasonable option for what this plugin does. +=item B I + +TCP-port to connect to. The port must be specified in its numeric form, but it +must be passed as a string nonetheless. For example: + + Port "3306" + +If B is set to B (the default), this setting has no effect. +See the documentation for the C function for details. + +=item B I + +Specifies the path to the UNIX domain socket of the MySQL server. This option +only has any effect, if B is set to B (the default). +Otherwise, use the B option above. See the documentation for the +C function for details. + =back =head2 Plugin C @@ -1099,6 +1366,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 +1826,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 @@ -2075,6 +2451,393 @@ only one such notification is generated until the value appears again. =back +=head1 FILTER CONFIGURATION + +TODO: Update this entire section once development is done. + +Starting with collectd 4.6 there is a powerful filtering infrastructure +implemented in the daemon. The concept has mostly been copied from I, +the packet filter infrastructure for Linux. We'll use a similar terminology, so +that users that are familiar with iptables feel right at home. + +=head2 Terminology + +The most important terms are: + +=over 4 + +=item B + +A I is a criteria to select specific values. Examples are, of course, the +name of the value or it's current value. + +=item B + +A I is some action that is to be performed with data. Such actions +could, for example, be to change part of the value's identifier or to ignore +the value completely. Built-in functions are B and B, see below. + +Some targets, for example the built-in B target, signal that processing +of a value should be stopped. In that case processing of the current chain will +be aborted. + +=item B + +The combination of any number of matches and at least one target is called a +I. The target actions will be performed for all values for which B +matches apply. If the rule does not have any matches associated with it, the +target action will be performed for all values. + +If any target returns the stop condition, the processing will stop right away. +This means that any targets following the current one will not be called after +the stop condition has been returned. + +=item B + +A I is a list of rules and possibly default targets. The rules are tried +in order and if one matches, the associated target will be called. If a value +is handled by a rule, it depends on the target whether or not any subsequent +rules are considered or if traversal of the chain is aborted. After all rules +have been checked and no target returned the stop condition, the default +targets will be executed. + +=back + +=head2 General structure + +The following shows the resulting structure: + + +---------+ + ! Chain ! + +---------+ + ! + V + +---------+ +---------+ +---------+ +---------+ + ! Rule !->! Match !->! Match !->! Target ! + +---------+ +---------+ +---------+ +---------+ + ! + V + +---------+ +---------+ +---------+ + ! Rule !->! Target !->! Target ! + +---------+ +---------+ +---------+ + ! + V + : + : + ! + V + +---------+ +---------+ +---------+ + ! Rule !->! Match !->! Target ! + +---------+ +---------+ +---------+ + ! + V + +---------+ + ! Default ! + ! Target ! + +---------+ + +=head2 Synopsis + +The configuration reflects this structure directly: + + + + + Plugin "^mysql$" + Type "^mysql_command$" + TypeInstance "^show_" + + + + + + Plugin "rrdtool" + + + +The above configuration example will ignore all values where the plugin field +is "mysql", the type is "mysql_command" and the type instance begins with +"show_". All other values will be sent to the "rrdtool" write plugin via the +default target of the chain. + +=head2 List of configuration options + +=over 4 + +=item B I + +Adds a new chain with a certain name. This name can be used to refer to a +specific chain, for example to jump to it. + +Within the B block, there can be B blocks and B blocks. + +=item B [I] + +Adds a new rule to the current chain. The name of the rule is optional and +currently has no meaning for the daemon. + +Within the B block, there may be any number of B blocks and there +must be at least one B block. + +=item B I + +Adds a match to a B block. The name specifies what kind of match should +be performed. Available matches depend on the plugins that have been loaded. + +The arguments inside the B block are passed to the plugin implementing +the match, so which arguments are valid here depends on the plugin being used. +If you do not need any to pass any arguments to a match, you can use the +shorter syntax: + + Match "foobar" + +Which is equivalent to: + + + + +=item B I + +Add a target to a rule or a default target to a chain. The name specifies what +kind of target is to be added. Which targets are available depends on the +plugins being loaded. + +The arguments inside the B block are passed to the plugin implementing +the target, so which arguments are valid here depends on the plugin being used. +If you do not need any to pass any arguments to a target, you can use the +shorter syntax: + + Target "stop" + +This is the same as writing: + + + + +=back + +=head2 Built-in targets + +The following targets are built into the core daemon and therefore need no +plugins to be loaded: + +=over 4 + +=item B + +Signals the "return" condition. This causes the current chain to stop +processing the value and returns control to the calling chain. The calling +chain will continue processing targets and rules just after the B target +(see below). This is very similar to the B target of iptables, see +L. + +This target does not have any options. + +Example: + + Target "return" + +=item B + +Signals the "stop" condition, causing processing of the value to be aborted +immediately. This is similar to the B target of iptables, see +L. + +This target does not have any options. + +Example: + + Target "stop" + +=item B + +Sends the value to write plugins. + +Available options: + +=over 4 + +=item B I + +Name of the write plugin to which the data should be sent. This option may be +given multiple times to send the data to more than one write plugin. + +=back + +If no plugin is explicitly specified, the values will be sent to all available +write plugins. + +Example: + + + Plugin "rrdtool" + + +=item B + +Starts processing the rules of another chain. If the end of that chain is +reached, or a stop condition is encountered, processing will continue right +after the B target, i.Ee. with the next target or the next rule. +This is similar to the B<-j> command line option of iptables, see +L. + +Available options: + +=over 4 + +=item B I + +Jumps to the chain I. This argument is required and may appear only once. + +=back + +Example: + + + Chain "foobar" + + +=back + +=head2 Available matches + +=over 4 + +=item B + +Matches a value using regular expressions. + +Available options: + +=over 4 + +=item B I + +=item B I + +=item B I + +=item B I + +=item B I + +Match values where the given regular expressions match the various fields of +the identifier of a value. If multiple regular expressions are given, B +regexen must match for a value to match. + +=back + +Example: + + + Host "customer[0-9]+" + Plugin "^foobar$" + + +=item B + +Matches the actual value of data sources against given minimumE/ maximum +values. If a data-set consists of more than one data-source, all data-sources +must match the specified ranges for a positive match. + +Available options: + +=over 4 + +=item B I + +Sets the smallest value which still results in a match. If unset, behaves like +negative infinity. + +=item B I + +Sets the largest value which still results in a match. If unset, behaves like +positive infinity. + +=item B B|B + +Inverts the selection. If the B and B settings result in a match, +no-match is returned and vice versa. + +=back + +Either B or B, but not both, may be unset. + +Example: + + # Match all values smaller than or equal to 100. + + Max 100 + + +=back + +=head2 Available targets + +=over 4 + +=item B + +Sets part of the identifier of a value to a given string. + +Available options: + +=over 4 + +=item B I + +=item B I + +=item B I + +=item B I + +Set the appropriate field to the given string. The strings for plugin instance +and type instance may be empty, the strings for host and plugin may not be +empty. It's currently not possible to set the type of a value this way. + +=back + +Example: + + + PluginInstance "coretemp" + TypeInstance "core3" + + +=back + +=head2 Backwards compatibility + +If you use collectd with an old configuration, i.Ee. one without a +B block, it will behave as it used to. This is equivalent to the +following configuration: + + + Target "write" + + +If you specify a B block anywhere, the B target will not be added +anywhere and you will have to make sure that it is called where appropriate. We +suggest to add the above snippet as default target to your main chain. + +TODO: Notifications will be implemented using chains, too. Describe that here! + +=head2 Examples + +Ignore all values, where the hostname does not contain a dot, i.Ee. can't +be an FQDN. + + + + + Host "^[^\.]*$" + + Target "stop" + + Target "write" + + =head1 SEE ALSO L, @@ -2083,8 +2846,10 @@ L, L, L, L, +L, L, L, +L, L, L, L diff --git a/src/configfile.c b/src/configfile.c index b1030fe9..bdb63a9a 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -30,6 +30,7 @@ #include "configfile.h" #include "types_list.h" #include "utils_threshold.h" +#include "filter_chain.h" #if HAVE_WORDEXP_H # include @@ -340,6 +341,8 @@ static int dispatch_block (oconfig_item_t *ci) return (dispatch_block_plugin (ci)); else if (strcasecmp (ci->key, "Threshold") == 0) return (ut_config (ci)); + else if (strcasecmp (ci->key, "Chain") == 0) + return (fc_configure (ci)); return (0); } @@ -891,7 +894,7 @@ int cf_read (char *filename) /* Read the default types.db if no `TypesDB' option was given. */ if (cf_default_typesdb) - read_types_list (PLUGINDIR"/types.db"); + read_types_list (PKGDATADIR"/types.db"); return (0); } /* int cf_read */ diff --git a/src/dbi.c b/src/dbi.c new file mode 100644 index 00000000..e1dd1bc6 --- /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 (connection, NULL) != 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 (db->connection, NULL) != 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 (db->connection, NULL) != 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_chain.c b/src/filter_chain.c new file mode 100644 index 00000000..01cd224c --- /dev/null +++ b/src/filter_chain.c @@ -0,0 +1,1058 @@ +/** + * collectd - src/filter_chain.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 + **/ + +/* + * First tell the compiler to stick to the C99 and POSIX standards as close as + * possible. + */ +#ifndef __STRICT_ANSI__ /* {{{ */ +# define __STRICT_ANSI__ +#endif + +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif + +#ifdef _POSIX_C_SOURCE +# undef _POSIX_C_SOURCE +#endif +#define _POSIX_C_SOURCE 200112L + +#if 0 +/* Single UNIX needed for strdup. */ +#ifdef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif +#define _XOPEN_SOURCE 500 +#endif + +#ifndef _REENTRANT +# define _REENTRANT +#endif + +#ifndef _THREAD_SAFE +# define _THREAD_SAFE +#endif + +#ifdef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* }}} */ + +#include "collectd.h" +#include "configfile.h" +#include "plugin.h" +#include "common.h" +#include "filter_chain.h" + +/* + * Data types + */ +/* List of matches, used in fc_rule_t and for the global `match_list_head' + * variable. */ +struct fc_match_s; +typedef struct fc_match_s fc_match_t; /* {{{ */ +struct fc_match_s +{ + char name[DATA_MAX_NAME_LEN]; + match_proc_t proc; + void *user_data; + fc_match_t *next; +}; /* }}} */ + +/* List of targets, used in fc_rule_t and for the global `target_list_head' + * variable. */ +struct fc_target_s; +typedef struct fc_target_s fc_target_t; /* {{{ */ +struct fc_target_s +{ + char name[DATA_MAX_NAME_LEN]; + void *user_data; + target_proc_t proc; + fc_target_t *next; +}; /* }}} */ + +/* List of rules, used in fc_chain_t */ +struct fc_rule_s; +typedef struct fc_rule_s fc_rule_t; /* {{{ */ +struct fc_rule_s +{ + char name[DATA_MAX_NAME_LEN]; + fc_match_t *matches; + fc_target_t *targets; + fc_rule_t *next; +}; /* }}} */ + +/* List of chains, used for `chain_list_head' */ +struct fc_chain_s; +typedef struct fc_chain_s fc_chain_t; /* {{{ */ +struct fc_chain_s +{ + char name[DATA_MAX_NAME_LEN]; + fc_rule_t *rules; + fc_target_t *targets; + fc_chain_t *next; +}; /* }}} */ + +/* + * Global variables + */ +static fc_match_t *match_list_head; +static fc_target_t *target_list_head; +static fc_chain_t *chain_list_head; + +/* + * Private functions + */ +static void fc_free_matches (fc_match_t *m) /* {{{ */ +{ + if (m == NULL) + return; + + if (m->proc.destroy != NULL) + (*m->proc.destroy) (&m->user_data); + else if (m->user_data != NULL) + { + ERROR ("Filter sybsystem: fc_free_matches: There is user data, but no " + "destroy functions has been specified. " + "Memory will probably be lost!"); + } + + if (m->next != NULL) + fc_free_matches (m->next); + + free (m); +} /* }}} void fc_free_matches */ + +static void fc_free_targets (fc_target_t *t) /* {{{ */ +{ + if (t == NULL) + return; + + if (t->proc.destroy != NULL) + (*t->proc.destroy) (&t->user_data); + else if (t->user_data != NULL) + { + ERROR ("Filter sybsystem: fc_free_targets: There is user data, but no " + "destroy functions has been specified. " + "Memory will probably be lost!"); + } + + if (t->next != NULL) + fc_free_targets (t->next); + + free (t); +} /* }}} void fc_free_targets */ + +static void fc_free_rules (fc_rule_t *r) /* {{{ */ +{ + if (r == NULL) + return; + + fc_free_matches (r->matches); + fc_free_targets (r->targets); + + if (r->next != NULL) + fc_free_rules (r->next); + + free (r); +} /* }}} void fc_free_rules */ + +static void fc_free_chains (fc_chain_t *c) /* {{{ */ +{ + if (c == NULL) + return; + + fc_free_rules (c->rules); + fc_free_targets (c->targets); + + if (c->next != NULL) + fc_free_chains (c->next); + + free (c); +} /* }}} void fc_free_chains */ + +static char *fc_strdup (const char *orig) /* {{{ */ +{ + size_t sz; + char *dest; + + if (orig == NULL) + return (NULL); + + sz = strlen (orig) + 1; + dest = (char *) malloc (sz); + if (dest == NULL) + return (NULL); + + memcpy (dest, orig, sz); + + return (dest); +} /* }}} char *fc_strdup */ + +/* + * Configuration. + * + * The configuration looks somewhat like this: + * + * + * + * + * Plugin "^mysql$" + * Type "^mysql_command$" + * TypeInstance "^show_" + * + * + * + * + * + * + * Plugin "rrdtool" + * + * + */ +static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */ + oconfig_item_t *ci) +{ + fc_match_t *m; + fc_match_t *ptr; + int status; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("Filter subsystem: `Match' blocks require " + "exactly one string argument."); + return (-1); + } + + ptr = match_list_head; + while (ptr != NULL) + { + if (strcasecmp (ptr->name, ci->values[0].value.string) == 0) + break; + ptr = ptr->next; + } + + if (ptr == NULL) + { + WARNING ("Filter subsystem: Cannot find a \"%s\" match. " + "Did you load the appropriate plugin?", + ci->values[0].value.string); + return (-1); + } + + m = (fc_match_t *) malloc (sizeof (*m)); + if (m == NULL) + { + ERROR ("fc_config_add_match: malloc failed."); + return (-1); + } + memset (m, 0, sizeof (*m)); + + sstrncpy (m->name, ptr->name, sizeof (m->name)); + memcpy (&m->proc, &ptr->proc, sizeof (m->proc)); + assert (m->proc.create != NULL); + m->user_data = NULL; + m->next = NULL; + + status = (*m->proc.create) (ci, &m->user_data); + if (status != 0) + { + WARNING ("Filter subsystem: Failed to create a %s match.", + m->name); + fc_free_matches (m); + return (-1); + } + + if (*matches_head != NULL) + { + ptr = *matches_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = m; + } + else + { + *matches_head = m; + } + + return (0); +} /* }}} int fc_config_add_match */ + +static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */ + oconfig_item_t *ci) +{ + fc_target_t *t; + fc_target_t *ptr; + int status; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("Filter subsystem: `Target' blocks require " + "exactly one string argument."); + return (-1); + } + + ptr = target_list_head; + while (ptr != NULL) + { + if (strcasecmp (ptr->name, ci->values[0].value.string) == 0) + break; + ptr = ptr->next; + } + + if (ptr == NULL) + { + WARNING ("Filter subsystem: Cannot find a \"%s\" target. " + "Did you load the appropriate plugin?", + ci->values[0].value.string); + return (-1); + } + + t = (fc_target_t *) malloc (sizeof (*t)); + if (t == NULL) + { + ERROR ("fc_config_add_match: malloc failed."); + return (-1); + } + memset (t, 0, sizeof (*t)); + + sstrncpy (t->name, ptr->name, sizeof (t->name)); + memcpy (&t->proc, &ptr->proc, sizeof (t->proc)); + t->user_data = NULL; + t->next = NULL; + + if (t->proc.create != NULL) + { + status = (*t->proc.create) (ci, &t->user_data); + if (status != 0) + { + WARNING ("Filter subsystem: Failed to create a %s match.", + t->name); + fc_free_targets (t); + return (-1); + } + } + else + { + t->user_data = NULL; + } + + if (*targets_head != NULL) + { + ptr = *targets_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = t; + } + else + { + *targets_head = t; + } + + return (0); +} /* }}} int fc_config_add_target */ + +static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */ + oconfig_item_t *ci) +{ + fc_rule_t *rule; + char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule"; + int status = 0; + int i; + + if (ci->values_num > 1) + { + WARNING ("Filter subsystem: `Rule' blocks have at most one argument."); + return (-1); + } + else if ((ci->values_num == 1) + && (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("Filter subsystem: `Rule' blocks expect one string argument " + "or no argument at all."); + return (-1); + } + + rule = (fc_rule_t *) malloc (sizeof (*rule)); + if (rule == NULL) + { + ERROR ("fc_config_add_rule: malloc failed."); + return (-1); + } + memset (rule, 0, sizeof (*rule)); + rule->next = NULL; + + if (ci->values_num == 1) + { + sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name)); + ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"", + ci->values[0].value.string); + } + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Match", option->key) == 0) + status = fc_config_add_match (&rule->matches, option); + else if (strcasecmp ("Target", option->key) == 0) + status = fc_config_add_target (&rule->targets, option); + else + { + WARNING ("Filter subsystem: %s: Option `%s' not allowed " + "inside a block.", rule_name, option->key); + status = -1; + } + + if (status != 0) + break; + } /* for (ci->children) */ + + /* Additional sanity checking. */ + while (status == 0) + { + if (rule->targets == NULL) + { + WARNING ("Filter subsystem: %s: No target has been specified.", + rule_name); + status = -1; + break; + } + + break; + } /* while (status == 0) */ + + if (status != 0) + { + fc_free_rules (rule); + return (-1); + } + + if (chain->rules != NULL) + { + fc_rule_t *ptr; + + ptr = chain->rules; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = rule; + } + else + { + chain->rules = rule; + } + + return (0); +} /* }}} int fc_config_add_rule */ + +static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ +{ + fc_chain_t *chain; + int status = 0; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("Filter subsystem: blocks require exactly one " + "string argument."); + return (-1); + } + + chain = (fc_chain_t *) malloc (sizeof (*chain)); + if (chain == NULL) + { + ERROR ("fc_config_add_chain: malloc failed."); + return (-1); + } + memset (chain, 0, sizeof (*chain)); + sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name)); + chain->rules = NULL; + chain->targets = NULL; + chain->next = NULL; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Rule", option->key) == 0) + status = fc_config_add_rule (chain, option); + else if (strcasecmp ("Target", option->key) == 0) + status = fc_config_add_target (&chain->targets, option); + else + { + WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed " + "inside a block.", chain->name, option->key); + status = -1; + } + + if (status != 0) + break; + } /* for (ci->children) */ + + /* Additional sanity checking. */ + while (status == 0) + { + if (chain->targets == NULL) + { + WARNING ("Filter subsystem: Chain %s: No default target has been " + "specified. Please make sure that there is a block within " + "the block!", chain->name); + status = -1; + break; + } + + break; + } /* while (status == 0) */ + + if (status != 0) + { + fc_free_chains (chain); + return (-1); + } + + if (chain_list_head != NULL) + { + fc_chain_t *ptr; + + ptr = chain_list_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = chain; + } + else + { + chain_list_head = chain; + } + + return (0); +} /* }}} int fc_config_add_chain */ + +int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */ + fc_chain_t *chain) +{ + fc_rule_t *rule; + fc_target_t *target; + int status; + + if (chain == NULL) + return (-1); + + DEBUG ("fc_process_chain (chain = %s);", chain->name); + + status = FC_TARGET_CONTINUE; + for (rule = chain->rules; rule != NULL; rule = rule->next) + { + fc_match_t *match; + + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Testing the `%s' rule.", + chain->name, rule->name); + } + + /* N. B.: rule->matches may be NULL. */ + for (match = rule->matches; match != NULL; match = match->next) + { + status = (*match->proc.match) (ds, vl, /* meta = */ NULL, + &match->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): A match failed.", chain->name); + break; + } + else if (status != FC_MATCH_MATCHES) + break; + } + + /* for-loop has been aborted: Either error or no match. */ + if (match != NULL) + { + status = FC_TARGET_CONTINUE; + continue; + } + + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Rule `%s' matches.", + chain->name, rule->name); + } + + for (target = rule->targets; target != NULL; target = target->next) + { + /* If we get here, all matches have matched the value. Execute the + * target. */ + status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, + &target->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): A target failed.", chain->name); + continue; + } + else if (status == FC_TARGET_CONTINUE) + continue; + else if (status == FC_TARGET_STOP) + break; + else if (status == FC_TARGET_RETURN) + break; + else + { + WARNING ("fc_process_chain (%s): Unknown return value " + "from target `%s': %i", + chain->name, target->name, status); + } + } + + if ((status == FC_TARGET_STOP) + || (status == FC_TARGET_RETURN)) + { + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Rule `%s' signaled " + "the %s condition.", + chain->name, rule->name, + (status == FC_TARGET_STOP) ? "stop" : "return"); + } + break; + } + else + { + status = FC_TARGET_CONTINUE; + } + } /* for (rule) */ + + if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else if (status == FC_TARGET_RETURN) + return (FC_TARGET_CONTINUE); + + /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */ + if (rule != NULL) + return (FC_TARGET_CONTINUE); + + DEBUG ("fc_process_chain (%s): Executing the default targets.", + chain->name); + + status = FC_TARGET_CONTINUE; + for (target = chain->targets; target != NULL; target = target->next) + { + /* If we get here, all matches have matched the value. Execute the + * target. */ + status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, + &target->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): The default target failed.", + chain->name); + } + else if (status == FC_TARGET_CONTINUE) + continue; + else if (status == FC_TARGET_STOP) + break; + else if (status == FC_TARGET_RETURN) + break; + else + { + WARNING ("fc_process_chain (%s): Unknown return value " + "from target `%s': %i", + chain->name, target->name, status); + } + } + + if ((status == FC_TARGET_STOP) + || (status == FC_TARGET_RETURN)) + { + assert (target != NULL); + DEBUG ("fc_process_chain (%s): Default target `%s' signaled " + "the %s condition.", + chain->name, target->name, + (status == FC_TARGET_STOP) ? "stop" : "return"); + if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else + return (FC_TARGET_CONTINUE); + } + + DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.", + chain->name); + + return (FC_TARGET_CONTINUE); +} /* }}} int fc_process_chain */ + +/* + * Built-in target "jump" + * + * Prefix `bit' like `_b_uilt-_i_n _t_arget' + */ +static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */ + void **user_data) +{ + oconfig_item_t *ci_chain; + + if (ci->children_num != 1) + { + ERROR ("Filter subsystem: The built-in target `jump' needs exactly " + "one `Chain' argument!"); + return (-1); + } + + ci_chain = ci->children; + if (strcasecmp ("Chain", ci_chain->key) != 0) + { + ERROR ("Filter subsystem: The built-in target `jump' does not " + "support the configuration option `%s'.", + ci_chain->key); + return (-1); + } + + if ((ci_chain->values_num != 1) + || (ci_chain->values[0].type != OCONFIG_TYPE_STRING)) + { + ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option " + "needs exactly one string argument."); + return (-1); + } + + *user_data = fc_strdup (ci_chain->values[0].value.string); + if (*user_data == NULL) + { + ERROR ("fc_bit_jump_create: fc_strdup failed."); + return (-1); + } + + return (0); +} /* }}} int fc_bit_jump_create */ + +static int fc_bit_jump_destroy (void **user_data) /* {{{ */ +{ + if (user_data != NULL) + { + free (*user_data); + *user_data = NULL; + } + + return (0); +} /* }}} int fc_bit_jump_destroy */ + +static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */ + value_list_t *vl, notification_meta_t **meta, void **user_data) +{ + char *chain_name; + fc_chain_t *chain; + int status; + + chain_name = *user_data; + + for (chain = chain_list_head; chain != NULL; chain = chain->next) + if (strcasecmp (chain_name, chain->name) == 0) + break; + + if (chain == NULL) + { + ERROR ("Filter subsystem: Built-in target `jump': There is no chain " + "named `%s'.", chain_name); + return (-1); + } + + status = fc_process_chain (ds, vl, chain); + if (status < 0) + return (status); + else if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else + return (FC_TARGET_CONTINUE); +} /* }}} int fc_bit_jump_invoke */ + +static int fc_bit_stop_invoke (const data_set_t *ds, /* {{{ */ + value_list_t *vl, notification_meta_t **meta, void **user_data) +{ + return (FC_TARGET_STOP); +} /* }}} int fc_bit_stop_invoke */ + +static int fc_bit_return_invoke (const data_set_t *ds, /* {{{ */ + value_list_t *vl, notification_meta_t **meta, void **user_data) +{ + return (FC_TARGET_RETURN); +} /* }}} int fc_bit_return_invoke */ + +static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */ + void **user_data) +{ + int i; + + char **plugin_list; + size_t plugin_list_len; + + plugin_list = NULL; + plugin_list_len = 0; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + char **temp; + int j; + + if (strcasecmp ("Plugin", child->key) != 0) + { + ERROR ("Filter subsystem: The built-in target `write' does not " + "support the configuration option `%s'.", + child->key); + continue; + } + + for (j = 0; j < child->values_num; j++) + { + if (child->values[j].type != OCONFIG_TYPE_STRING) + { + ERROR ("Filter subsystem: Built-in target `write': " + "The `Plugin' option accepts only string arguments."); + continue; + } + + temp = (char **) realloc (plugin_list, (plugin_list_len + 2) + * (sizeof (*plugin_list))); + if (temp == NULL) + { + ERROR ("fc_bit_write_create: realloc failed."); + continue; + } + plugin_list = temp; + + plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string); + if (plugin_list[plugin_list_len] == NULL) + { + ERROR ("fc_bit_write_create: fc_strdup failed."); + continue; + } + plugin_list_len++; + plugin_list[plugin_list_len] = NULL; + } /* for (j = 0; j < child->values_num; j++) */ + } /* for (i = 0; i < ci->children_num; i++) */ + + *user_data = plugin_list; + + return (0); +} /* }}} int fc_bit_write_create */ + +static int fc_bit_write_destroy (void **user_data) /* {{{ */ +{ + char **plugin_list; + size_t i; + + if ((user_data == NULL) || (*user_data == NULL)) + return (0); + + plugin_list = *user_data; + + for (i = 0; plugin_list[i] != NULL; i++) + free (plugin_list[i]); + free (plugin_list); + + return (0); +} /* }}} int fc_bit_write_destroy */ + +static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ + value_list_t *vl, notification_meta_t **meta, void **user_data) +{ + char **plugin_list; + int status; + + plugin_list = NULL; + if (user_data != NULL) + plugin_list = *user_data; + + if ((plugin_list == NULL) || (plugin_list[0] == NULL)) + { + status = plugin_write (/* plugin = */ NULL, ds, vl); + if (status != 0) + { + INFO ("Filter subsystem: Built-in target `write': Dispatching value to " + "all write plugins failed with status %i.", status); + } + } + else + { + size_t i; + + for (i = 0; plugin_list[i] != NULL; i++) + { + status = plugin_write (plugin_list[i], ds, vl); + if (status != 0) + { + INFO ("Filter subsystem: Built-in target `write': Dispatching value to " + "the `%s' plugin failed with status %i.", plugin_list[i], status); + } + } /* for (i = 0; plugin_list[i] != NULL; i++) */ + } + + return (FC_TARGET_CONTINUE); +} /* }}} int fc_bit_write_invoke */ + +static int fc_init_once (void) /* {{{ */ +{ + static int done = 0; + target_proc_t tproc; + + if (done != 0) + return (0); + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = fc_bit_jump_create; + tproc.destroy = fc_bit_jump_destroy; + tproc.invoke = fc_bit_jump_invoke; + fc_register_target ("jump", tproc); + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = NULL; + tproc.destroy = NULL; + tproc.invoke = fc_bit_stop_invoke; + fc_register_target ("stop", tproc); + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = NULL; + tproc.destroy = NULL; + tproc.invoke = fc_bit_return_invoke; + fc_register_target ("return", tproc); + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = fc_bit_write_create; + tproc.destroy = fc_bit_write_destroy; + tproc.invoke = fc_bit_write_invoke; + fc_register_target ("write", tproc); + + done++; + return (0); +} /* }}} int fc_init_once */ + +/* + * Public functions + */ +/* Add a match to list of available matches. */ +int fc_register_match (const char *name, match_proc_t proc) /* {{{ */ +{ + fc_match_t *m; + + DEBUG ("fc_register_match (%s);", name); + + m = (fc_match_t *) malloc (sizeof (*m)); + if (m == NULL) + return (-ENOMEM); + memset (m, 0, sizeof (*m)); + + sstrncpy (m->name, name, sizeof (m->name)); + memcpy (&m->proc, &proc, sizeof (m->proc)); + m->next = NULL; + + if (match_list_head == NULL) + { + match_list_head = m; + } + else + { + fc_match_t *ptr; + + ptr = match_list_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = m; + } + + return (0); +} /* }}} int fc_register_match */ + +/* Add a target to list of available targets. */ +int fc_register_target (const char *name, target_proc_t proc) /* {{{ */ +{ + fc_target_t *t; + + DEBUG ("fc_register_target (%s);", name); + + t = (fc_target_t *) malloc (sizeof (*t)); + if (t == NULL) + return (-ENOMEM); + memset (t, 0, sizeof (*t)); + + sstrncpy (t->name, name, sizeof (t->name)); + memcpy (&t->proc, &proc, sizeof (t->proc)); + t->next = NULL; + + if (target_list_head == NULL) + { + target_list_head = t; + } + else + { + fc_target_t *ptr; + + ptr = target_list_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = t; + } + + return (0); +} /* }}} int fc_register_target */ + +/* Iterate over all rules in the chain and execute all targets for which all + * matches match. */ +int fc_process (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + fc_chain_t *chain; + + for (chain = chain_list_head; chain != NULL; chain = chain->next) + if (strcasecmp ("Main", chain->name) == 0) + break; + + if (chain != NULL) + return (fc_process_chain (ds, vl, chain)); + + return (fc_bit_write_invoke (ds, vl, + /* meta = */ NULL, /* user_data = */ NULL)); +} /* }}} int fc_process */ + +int fc_configure (const oconfig_item_t *ci) /* {{{ */ +{ + fc_init_once (); + + if (ci == NULL) + return (-EINVAL); + + if (strcasecmp ("Chain", ci->key) == 0) + return (fc_config_add_chain (ci)); + + WARNING ("Filter subsystem: Unknown top level config option `%s'.", + ci->key); + + return (-1); +} /* }}} int fc_configure */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/filter_chain.h b/src/filter_chain.h new file mode 100644 index 00000000..2fd78d97 --- /dev/null +++ b/src/filter_chain.h @@ -0,0 +1,93 @@ +/** + * collectd - src/filter_chain.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 FILTER_CHAIN_H +#define FILTER_CHAIN_H 1 + +#include "collectd.h" +#include "plugin.h" + +#define FC_MATCH_NO_MATCH 0 +#define FC_MATCH_MATCHES 1 + +#define FC_TARGET_CONTINUE 0 +#define FC_TARGET_STOP 1 +#define FC_TARGET_RETURN 2 + +/* + * Match functions + */ +struct match_proc_s +{ + int (*create) (const oconfig_item_t *ci, void **user_data); + int (*destroy) (void **user_data); + int (*match) (const data_set_t *ds, const value_list_t *vl, + notification_meta_t **meta, void **user_data); +}; +typedef struct match_proc_s match_proc_t; + +int fc_register_match (const char *name, match_proc_t proc); + +/* + * Target functions + */ +struct target_proc_s +{ + int (*create) (const oconfig_item_t *ci, void **user_data); + int (*destroy) (void **user_data); + int (*invoke) (const data_set_t *ds, value_list_t *vl, + notification_meta_t **meta, void **user_data); +}; +typedef struct target_proc_s target_proc_t; + +int fc_register_target (const char *name, target_proc_t proc); + +/* + * TODO: Chain management + */ +#if 0 +int fc_chain_add (const char *chain_name, + const char *target_name, int target_argc, char **target_argv); +int fc_chain_delete (const char *chain_name); +#endif + +/* + * TODO: Rule management + */ +#if 0 +int fc_rule_add (const char *chain_name, int position, + int match_num, const char **match_name, int *match_argc, char ***match_argv, + const char *target_name, int target_argc, char **target_argv); +int fc_rule_delete (const char *chain_name, int position); +#endif + +/* + * Processing function + */ +int fc_process (const data_set_t *ds, value_list_t *vl); + +/* + * Shortcut for global configuration + */ +int fc_configure (const oconfig_item_t *ci); + +#endif /* FILTER_CHAIN_H */ +/* vim: set sw=2 sts=2 et : */ 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/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/libcollectdclient/Makefile.am b/src/libcollectdclient/Makefile.am new file mode 100644 index 00000000..f96db631 --- /dev/null +++ b/src/libcollectdclient/Makefile.am @@ -0,0 +1,7 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +pkginclude_HEADERS = client.h +lib_LTLIBRARIES = libcollectdclient.la + +libcollectdclient_la_SOURCES = client.c +libcollectdclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c new file mode 100644 index 00000000..98b73723 --- /dev/null +++ b/src/libcollectdclient/client.c @@ -0,0 +1,1004 @@ +/** + * libcollectdclient - src/libcollectdclient/client.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 + **/ + +/* Set to C99 and POSIX code */ +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +#endif +#ifndef _REENTRANT +# define _REENTRANT +#endif + +/* Disable non-standard extensions */ +#ifdef _BSD_SOURCE +# undef _BSD_SOURCE +#endif +#ifdef _SVID_SOURCE +# undef _SVID_SOURCE +#endif +#ifdef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +#if !defined(__GNUC__) || !__GNUC__ +# define __attribute__(x) /**/ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + +/* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11 + * to no longer define it. We'll use the old, RFC 2553 value here. */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif + +/* Secure/static macros. They work like `strcpy' and `strcat', but assure null + * termination. They work for static buffers only, because they use `sizeof'. + * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which + * is very useful to add formatted stuff to the end of a buffer. */ +#define SSTRCPY(d,s) do { \ + strncpy ((d), (s), sizeof (d)); \ + (d)[sizeof (d) - 1] = 0; \ + } while (0) + +#define SSTRCAT(d,s) do { \ + strncat ((d), (s), sizeof (d)); \ + (d)[sizeof (d) - 1] = 0; \ + } while (0) + +#define SSTRCATF(d, ...) do { \ + char _b[sizeof (d)]; \ + snprintf (_b, sizeof (_b), __VA_ARGS__); \ + _b[sizeof (_b) - 1] = 0; \ + SSTRCAT ((d), _b); \ + } while (0) + + +#define LCC_SET_ERRSTR(c, ...) do { \ + snprintf ((c)->errbuf, sizeof ((c)->errbuf), __VA_ARGS__); \ + (c)->errbuf[sizeof ((c)->errbuf) - 1] = 0; \ +} while (0) + +#if 1 +# define LCC_DEBUG(...) printf (__VA_ARGS__) +#else +# define LCC_DEBUG(...) /**/ +#endif + +/* + * Types + */ +struct lcc_connection_s +{ + FILE *fh; + char errbuf[1024]; +}; + +struct lcc_response_s +{ + int status; + char message[1024]; + char **lines; + size_t lines_num; +}; +typedef struct lcc_response_s lcc_response_t; + +/* + * Private functions + */ +static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */ +{ + if (c == NULL) + return (-1); + + strerror_r (err, c->errbuf, sizeof (c->errbuf)); + c->errbuf[sizeof (c->errbuf) - 1] = 0; + + return (0); +} /* }}} int lcc_set_errno */ + +/* lcc_strdup: Since `strdup' is an XSI extension, we provide our own version + * here. */ +__attribute__((malloc, nonnull (1))) +static char *lcc_strdup (const char *str) /* {{{ */ +{ + size_t strsize; + char *ret; + + strsize = strlen (str) + 1; + ret = (char *) malloc (strsize); + if (ret != NULL) + memcpy (ret, str, strsize); + return (ret); +} /* }}} char *lcc_strdup */ + +__attribute__((nonnull (1, 2))) +static char *lcc_strescape (char *dest, char *src, size_t dest_size) /* {{{ */ +{ + size_t dest_pos; + size_t src_pos; + + dest_pos = 0; + src_pos = 0; + + assert (dest_size >= 3); + + dest[dest_pos] = '"'; + dest_pos++; + + while (42) + { + if ((dest_pos == (dest_size - 2)) + || (src[src_pos] == 0)) + break; + + if ((src[src_pos] == '"') || (src[src_pos] == '\\')) + { + /* Check if there is enough space for both characters.. */ + if (dest_pos == (dest_size - 3)) + break; + + dest[dest_pos] = '\\'; + dest_pos++; + } + + dest[dest_pos] = src[src_pos]; + dest_pos++; + src_pos++; + } + + assert (dest_pos <= (dest_size - 2)); + + dest[dest_pos] = '"'; + dest_pos++; + + dest[dest_pos] = 0; + dest_pos++; + src_pos++; + + return (dest); +} /* }}} char *lcc_strescape */ + +/* lcc_chomp: Removes all control-characters at the end of a string. */ +static void lcc_chomp (char *str) /* {{{ */ +{ + size_t str_len; + + str_len = strlen (str); + while (str_len > 0) + { + if (str[str_len - 1] >= 32) + break; + str[str_len - 1] = 0; + str_len--; + } +} /* }}} void lcc_chomp */ + +static void lcc_response_free (lcc_response_t *res) /* {{{ */ +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->lines_num; i++) + free (res->lines[i]); + free (res->lines); + res->lines = NULL; +} /* }}} void lcc_response_free */ + +static int lcc_send (lcc_connection_t *c, const char *command) /* {{{ */ +{ + int status; + + LCC_DEBUG ("send: --> %s\n", command); + + status = fprintf (c->fh, "%s\r\n", command); + if (status < 0) + { + lcc_set_errno (c, errno); + return (-1); + } + + return (0); +} /* }}} int lcc_send */ + +static int lcc_receive (lcc_connection_t *c, /* {{{ */ + lcc_response_t *ret_res) +{ + lcc_response_t res; + char *ptr; + char buffer[4096]; + size_t i; + + memset (&res, 0, sizeof (res)); + + /* Read the first line, containing the status and a message */ + ptr = fgets (buffer, sizeof (buffer), c->fh); + if (ptr == NULL) + { + lcc_set_errno (c, errno); + return (-1); + } + lcc_chomp (buffer); + LCC_DEBUG ("receive: <-- %s\n", buffer); + + /* Convert the leading status to an integer and make `ptr' to point to the + * beginning of the message. */ + ptr = NULL; + errno = 0; + res.status = strtol (buffer, &ptr, 0); + if ((errno != 0) || (ptr == &buffer[0])) + { + lcc_set_errno (c, errno); + return (-1); + } + + /* Skip white spaces after the status number */ + while ((*ptr == ' ') || (*ptr == '\t')) + ptr++; + + /* Now copy the message. */ + strncpy (res.message, ptr, sizeof (res.message)); + res.message[sizeof (res.message) - 1] = 0; + + /* Error or no lines follow: We're done. */ + if (res.status <= 0) + { + memcpy (ret_res, &res, sizeof (res)); + return (0); + } + + /* Allocate space for the char-pointers */ + res.lines_num = (size_t) res.status; + res.status = 0; + res.lines = (char **) malloc (res.lines_num * sizeof (char *)); + if (res.lines == NULL) + { + lcc_set_errno (c, ENOMEM); + return (-1); + } + + /* Now receive all the lines */ + for (i = 0; i < res.lines_num; i++) + { + ptr = fgets (buffer, sizeof (buffer), c->fh); + if (ptr == NULL) + { + lcc_set_errno (c, errno); + break; + } + lcc_chomp (buffer); + LCC_DEBUG ("receive: <-- %s\n", buffer); + + res.lines[i] = lcc_strdup (buffer); + if (res.lines[i] == NULL) + { + lcc_set_errno (c, ENOMEM); + break; + } + } + + /* Check if the for-loop exited with an error. */ + if (i < res.lines_num) + { + while (i > 0) + { + i--; + free (res.lines[i]); + } + free (res.lines); + return (-1); + } + + memcpy (ret_res, &res, sizeof (res)); + return (0); +} /* }}} int lcc_receive */ + +static int lcc_sendreceive (lcc_connection_t *c, /* {{{ */ + const char *command, lcc_response_t *ret_res) +{ + lcc_response_t res; + int status; + + status = lcc_send (c, command); + if (status != 0) + return (status); + + memset (&res, 0, sizeof (res)); + status = lcc_receive (c, &res); + if (status == 0) + memcpy (ret_res, &res, sizeof (*ret_res)); + + return (status); +} /* }}} int lcc_sendreceive */ + +static int lcc_open_unixsocket (lcc_connection_t *c, const char *path) /* {{{ */ +{ + struct sockaddr_un sa; + int fd; + int status; + + assert (c != NULL); + assert (c->fh == NULL); + assert (path != NULL); + + fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0); + if (fd < 0) + { + lcc_set_errno (c, errno); + return (-1); + } + + memset (&sa, 0, sizeof (sa)); + sa.sun_family = AF_UNIX; + strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1); + + status = connect (fd, (struct sockaddr *) &sa, sizeof (sa)); + if (status != 0) + { + lcc_set_errno (c, errno); + close (fd); + return (-1); + } + + c->fh = fdopen (fd, "r+"); + if (c->fh == NULL) + { + lcc_set_errno (c, errno); + close (fd); + return (-1); + } + + return (0); +} /* }}} int lcc_open_unixsocket */ + +static int lcc_open_netsocket (lcc_connection_t *c, /* {{{ */ + const char *addr_orig) +{ + struct addrinfo ai_hints; + struct addrinfo *ai_res; + struct addrinfo *ai_ptr; + char addr_copy[NI_MAXHOST]; + char *addr; + char *port; + int fd; + int status; + + assert (c != NULL); + assert (c->fh == NULL); + assert (addr_orig != NULL); + + strncpy(addr_copy, addr_orig, sizeof(addr_copy)); + addr_copy[sizeof(addr_copy) - 1] = '\0'; + addr = addr_copy; + + memset (&ai_hints, 0, sizeof (ai_hints)); + ai_hints.ai_flags = 0; +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = AF_UNSPEC; + ai_hints.ai_socktype = SOCK_STREAM; + + port = NULL; + if (*addr == '[') /* IPv6+port format */ + { + /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */ + addr++; + + port = strchr (addr, ']'); + if (port == NULL) + { + LCC_SET_ERRSTR (c, "malformed address: %s", addr_orig); + return (-1); + } + *port = 0; + port++; + + if (*port == ':') + port++; + else if (*port == 0) + port = NULL; + else + { + LCC_SET_ERRSTR (c, "garbage after address: %s", port); + return (-1); + } + } /* if (*addr = ']') */ + else if (strchr (addr, '.') != NULL) /* Hostname or IPv4 */ + { + port = strrchr (addr, ':'); + if (port != NULL) + { + *port = 0; + port++; + } + } + + ai_res = NULL; + status = getaddrinfo (addr, + port == NULL ? LCC_DEFAULT_PORT : port, + &ai_hints, &ai_res); + if (status != 0) + { + LCC_SET_ERRSTR (c, "getaddrinfo: %s", gai_strerror (status)); + return (-1); + } + + for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); + if (fd < 0) + { + status = errno; + fd = -1; + continue; + } + + status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + if (status != 0) + { + status = errno; + close (fd); + fd = -1; + continue; + } + + c->fh = fdopen (fd, "r+"); + if (c->fh == NULL) + { + status = errno; + close (fd); + fd = -1; + continue; + } + + assert (status == 0); + break; + } /* for (ai_ptr) */ + + if (status != 0) + { + lcc_set_errno (c, status); + return (-1); + } + + return (0); +} /* }}} int lcc_open_netsocket */ + +static int lcc_open_socket (lcc_connection_t *c, const char *addr) /* {{{ */ +{ + int status = 0; + + if (addr == NULL) + return (-1); + + assert (c != NULL); + assert (c->fh == NULL); + assert (addr != NULL); + + if (strncmp ("unix:", addr, strlen ("unix:")) == 0) + status = lcc_open_unixsocket (c, addr + strlen ("unix:")); + else if (addr[0] == '/') + status = lcc_open_unixsocket (c, addr); + else + status = lcc_open_netsocket (c, addr); + + return (status); +} /* }}} int lcc_open_socket */ + +/* + * Public functions + */ +int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */ +{ + lcc_connection_t *c; + + if (address == NULL) + return (-1); + + if (ret_con == NULL) + return (-1); + + c = (lcc_connection_t *) malloc (sizeof (*c)); + if (c == NULL) + return (-1); + memset (c, 0, sizeof (*c)); + + *ret_con = c; + return (lcc_open_socket (c, address)); +} /* }}} int lcc_connect */ + +int lcc_disconnect (lcc_connection_t *c) /* {{{ */ +{ + if (c == NULL) + return (-1); + + if (c->fh != NULL) + { + fclose (c->fh); + c->fh = NULL; + } + + free (c); + return (0); +} /* }}} int lcc_disconnect */ + +int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ + size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names) +{ + char ident_str[6 * LCC_NAME_LEN]; + char ident_esc[12 * LCC_NAME_LEN]; + char command[14 * LCC_NAME_LEN]; + + lcc_response_t res; + size_t values_num; + gauge_t *values = NULL; + char **values_names = NULL; + + size_t i; + int status; + + if (c == NULL) + return (-1); + + if (ident == NULL) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + /* Build a commend with an escaped version of the identifier string. */ + status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident); + if (status != 0) + return (status); + + snprintf (command, sizeof (command), "GETVAL %s", + lcc_strescape (ident_esc, ident_str, sizeof (ident_esc))); + command[sizeof (command) - 1] = 0; + + /* Send talk to the daemon.. */ + status = lcc_sendreceive (c, command, &res); + if (status != 0) + return (status); + + if (res.status != 0) + { + LCC_SET_ERRSTR (c, "Server error: %s", res.message); + lcc_response_free (&res); + return (-1); + } + + values_num = res.lines_num; + +#define BAIL_OUT(e) do { \ + lcc_set_errno (c, (e)); \ + free (values); \ + if (values_names != NULL) { \ + for (i = 0; i < values_num; i++) { \ + free (values_names[i]); \ + } \ + } \ + free (values_names); \ + lcc_response_free (&res); \ + return (-1); \ +} while (0) + + /* If neither the values nor the names are requested, return here.. */ + if ((ret_values == NULL) && (ret_values_names == NULL)) + { + if (ret_values_num != NULL) + *ret_values_num = values_num; + lcc_response_free (&res); + return (0); + } + + /* Allocate space for the values */ + if (ret_values != NULL) + { + values = (gauge_t *) malloc (values_num * sizeof (*values)); + if (values == NULL) + BAIL_OUT (ENOMEM); + } + + if (ret_values_names != NULL) + { + values_names = (char **) calloc (values_num, sizeof (*values_names)); + if (values_names == NULL) + BAIL_OUT (ENOMEM); + } + + for (i = 0; i < res.lines_num; i++) + { + char *key; + char *value; + char *endptr; + + key = res.lines[i]; + value = strchr (key, '='); + if (value == NULL) + BAIL_OUT (EPROTO); + + *value = 0; + value++; + + if (values != NULL) + { + endptr = NULL; + errno = 0; + values[i] = strtod (value, &endptr); + + if ((endptr == value) || (errno != 0)) + BAIL_OUT (errno); + } + + if (values_names != NULL) + { + values_names[i] = lcc_strdup (key); + if (values_names[i] == NULL) + BAIL_OUT (ENOMEM); + } + } /* for (i = 0; i < res.lines_num; i++) */ + + if (ret_values_num != NULL) + *ret_values_num = values_num; + if (ret_values != NULL) + *ret_values = values; + if (ret_values_names != NULL) + *ret_values_names = values_names; + + return (0); +} /* }}} int lcc_getval */ + +int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ +{ + char ident_str[6 * LCC_NAME_LEN]; + char ident_esc[12 * LCC_NAME_LEN]; + char command[1024] = ""; + lcc_response_t res; + int status; + size_t i; + + if ((c == NULL) || (vl == NULL) || (vl->values_len < 1) + || (vl->values == NULL) || (vl->values_types == NULL)) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), + &vl->identifier); + if (status != 0) + return (status); + + SSTRCATF (command, "PUTVAL %s", + lcc_strescape (ident_esc, ident_str, sizeof (ident_esc))); + + if (vl->interval > 0) + SSTRCATF (command, " interval=%i", vl->interval); + + if (vl->time > 0) + SSTRCATF (command, "%u", (unsigned int) vl->time); + else + SSTRCAT (command, "N"); + + for (i = 0; i < vl->values_len; i++) + { + if (vl->values_types[i] == LCC_TYPE_COUNTER) + SSTRCATF (command, ":%"PRIu64, vl->values[i].counter); + else if (vl->values_types[i] == LCC_TYPE_GAUGE) + { + if (isnan (vl->values[i].gauge)) + SSTRCPY (command, ":U"); + else + SSTRCATF (command, ":%g", vl->values[i].gauge); + } + } /* for (i = 0; i < vl->values_len; i++) */ + + status = lcc_sendreceive (c, command, &res); + if (status != 0) + return (status); + + if (res.status != 0) + { + LCC_SET_ERRSTR (c, "Server error: %s", res.message); + lcc_response_free (&res); + return (-1); + } + + lcc_response_free (&res); + return (0); +} /* }}} int lcc_putval */ + +int lcc_flush (lcc_connection_t *c, const char *plugin, /* {{{ */ + lcc_identifier_t *ident, int timeout) +{ + char command[1024]; + lcc_response_t res; + int status; + + if (c == NULL) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + SSTRCPY (command, "FLUSH"); + + if (timeout > 0) + SSTRCATF (command, " timeout=%i", timeout); + + if (plugin != NULL) + { + char buffer[2 * LCC_NAME_LEN]; + SSTRCATF (command, " plugin=%s", + lcc_strescape (buffer, plugin, sizeof (buffer))); + } + + if (ident != NULL) + { + char ident_str[6 * LCC_NAME_LEN]; + char ident_esc[12 * LCC_NAME_LEN]; + + status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident); + if (status != 0) + return (status); + + SSTRCATF (command, " identifier=%s", + lcc_strescape (ident_esc, ident_str, sizeof (ident_esc))); + } + + status = lcc_sendreceive (c, command, &res); + if (status != 0) + return (status); + + if (res.status != 0) + { + LCC_SET_ERRSTR (c, "Server error: %s", res.message); + lcc_response_free (&res); + return (-1); + } + + lcc_response_free (&res); + return (0); +} /* }}} int lcc_flush */ + +/* TODO: Implement lcc_putnotif */ + +int lcc_listval (lcc_connection_t *c, /* {{{ */ + lcc_identifier_t **ret_ident, size_t *ret_ident_num) +{ + lcc_response_t res; + size_t i; + int status; + + lcc_identifier_t *ident; + size_t ident_num; + + if (c == NULL) + return (-1); + + if ((ret_ident == NULL) || (ret_ident_num == NULL)) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + status = lcc_sendreceive (c, "LISTVAL", &res); + if (status != 0) + return (status); + + if (res.status != 0) + { + LCC_SET_ERRSTR (c, "Server error: %s", res.message); + lcc_response_free (&res); + return (-1); + } + + ident_num = res.lines_num; + ident = (lcc_identifier_t *) malloc (ident_num * sizeof (*ident)); + if (ident == NULL) + { + lcc_response_free (&res); + lcc_set_errno (c, ENOMEM); + return (-1); + } + + for (i = 0; i < res.lines_num; i++) + { + char *time_str; + char *ident_str; + + /* First field is the time. */ + time_str = res.lines[i]; + + /* Set `ident_str' to the beginning of the second field. */ + ident_str = time_str; + while ((*ident_str != ' ') && (*ident_str != '\t') && (*ident_str != 0)) + ident_str++; + while ((*ident_str == ' ') || (*ident_str == '\t')) + { + *ident_str = 0; + ident_str++; + } + + if (*ident_str == 0) + { + lcc_set_errno (c, EPROTO); + status = -1; + break; + } + + status = lcc_string_to_identifier (c, ident + i, ident_str); + if (status != 0) + break; + } + + lcc_response_free (&res); + + if (status != 0) + { + free (ident); + return (-1); + } + + *ret_ident = ident; + *ret_ident_num = ident_num; + + return (0); +} /* }}} int lcc_listval */ + +const char *lcc_strerror (lcc_connection_t *c) /* {{{ */ +{ + if (c == NULL) + return ("Invalid object"); + return (c->errbuf); +} /* }}} const char *lcc_strerror */ + +int lcc_identifier_to_string (lcc_connection_t *c, /* {{{ */ + char *string, size_t string_size, const lcc_identifier_t *ident) +{ + if ((string == NULL) || (string_size < 6) || (ident == NULL)) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + if (ident->plugin_instance[0] == 0) + { + if (ident->type_instance[0] == 0) + snprintf (string, string_size, "%s/%s/%s", + ident->host, + ident->plugin, + ident->type); + else + snprintf (string, string_size, "%s/%s/%s-%s", + ident->host, + ident->plugin, + ident->type, + ident->type_instance); + } + else + { + if (ident->type_instance[0] == 0) + snprintf (string, string_size, "%s/%s-%s/%s", + ident->host, + ident->plugin, + ident->plugin_instance, + ident->type); + else + snprintf (string, string_size, "%s/%s-%s/%s-%s", + ident->host, + ident->plugin, + ident->plugin_instance, + ident->type, + ident->type_instance); + } + + string[string_size - 1] = 0; + return (0); +} /* }}} int lcc_identifier_to_string */ + +int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */ + lcc_identifier_t *ident, const char *string) +{ + char *string_copy; + char *host; + char *plugin; + char *plugin_instance; + char *type; + char *type_instance; + + string_copy = lcc_strdup (string); + if (string_copy == NULL) + { + lcc_set_errno (c, ENOMEM); + return (-1); + } + + host = string_copy; + plugin = strchr (host, '/'); + if (plugin == NULL) + { + LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string); + free (string_copy); + return (-1); + } + *plugin = 0; + plugin++; + + type = strchr (plugin, '/'); + if (type == NULL) + { + LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string); + free (string_copy); + return (-1); + } + *type = 0; + type++; + + plugin_instance = strchr (plugin, '-'); + if (plugin_instance != NULL) + { + *plugin_instance = 0; + plugin_instance++; + } + + type_instance = strchr (type, '-'); + if (type_instance != NULL) + { + *type_instance = 0; + type_instance++; + } + + memset (ident, 0, sizeof (*ident)); + + SSTRCPY (ident->host, host); + SSTRCPY (ident->plugin, plugin); + if (plugin_instance != NULL) + SSTRCPY (ident->plugin_instance, plugin_instance); + SSTRCPY (ident->type, type); + if (type_instance != NULL) + SSTRCPY (ident->type_instance, type_instance); + + free (string_copy); + return (0); +} /* }}} int lcc_string_to_identifier */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h new file mode 100644 index 00000000..a0ab94c7 --- /dev/null +++ b/src/libcollectdclient/client.h @@ -0,0 +1,109 @@ +/** + * libcollectdclient - src/libcollectdclient/client.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 LIBCOLLECTD_COLLECTDCLIENT_H +#define LIBCOLLECTD_COLLECTDCLIENT_H 1 + +/* + * Includes (for data types) + */ +#include +#include +#include + +/* + * Defines + */ +#define LCC_VERSION 0 +#define LCC_NAME_LEN 64 +#define LCC_DEFAULT_PORT "25826" + +/* + * Types + */ +#define LCC_TYPE_COUNTER 0 +#define LCC_TYPE_GAUGE 1 + +typedef uint64_t counter_t; +typedef double gauge_t; + +union value_u +{ + counter_t counter; + gauge_t gauge; +}; +typedef union value_u value_t; + +struct lcc_identifier_s +{ + char host[LCC_NAME_LEN]; + char plugin[LCC_NAME_LEN]; + char plugin_instance[LCC_NAME_LEN]; + char type[LCC_NAME_LEN]; + char type_instance[LCC_NAME_LEN]; +}; +typedef struct lcc_identifier_s lcc_identifier_t; +#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" } + +struct lcc_value_list_s +{ + value_t *values; + int *values_types; + size_t values_len; + time_t time; + int interval; + lcc_identifier_t identifier; +}; +typedef struct lcc_value_list_s lcc_value_list_t; +#define LCC_VALUE_LIST_INIT { NULL, 0, 0, 0, LCC_IDENTIFIER_INIT } + +struct lcc_connection_s; +typedef struct lcc_connection_s lcc_connection_t; + +/* + * Functions + */ +int lcc_connect (const char *address, lcc_connection_t **ret_con); +int lcc_disconnect (lcc_connection_t *c); +#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0) + +int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, + size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names); + +int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl); + +int lcc_flush (lcc_connection_t *c, const char *plugin, + lcc_identifier_t *ident, int timeout); + +int lcc_listval (lcc_connection_t *c, + lcc_identifier_t **ret_ident, size_t *ret_ident_num); + +/* TODO: putnotif */ + +const char *lcc_strerror (lcc_connection_t *c); + +int lcc_identifier_to_string (lcc_connection_t *c, + char *string, size_t string_size, const lcc_identifier_t *ident); +int lcc_string_to_identifier (lcc_connection_t *c, + lcc_identifier_t *ident, const char *string); + +/* vim: set sw=2 sts=2 et : */ +#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */ 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/match_regex.c b/src/match_regex.c new file mode 100644 index 00000000..9233d803 --- /dev/null +++ b/src/match_regex.c @@ -0,0 +1,298 @@ +/** + * collectd - src/match_regex.c + * Copyright (C) 2008 Sebastian Harl + * Copyright (C) 2008 Florian 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: + * Sebastian Harl + * Florian Forster + **/ + +/* + * This module allows to filter and rewrite value lists based on + * Perl-compatible regular expressions. + */ + +#include "collectd.h" +#include "filter_chain.h" + +#include +#include + +#define log_err(...) ERROR ("`regex' match: " __VA_ARGS__) +#define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__) + +/* + * private data types + */ + +struct mr_regex_s; +typedef struct mr_regex_s mr_regex_t; +struct mr_regex_s +{ + regex_t re; + char *re_str; + + mr_regex_t *next; +}; + +struct mr_match_s; +typedef struct mr_match_s mr_match_t; +struct mr_match_s +{ + mr_regex_t *host; + mr_regex_t *plugin; + mr_regex_t *plugin_instance; + mr_regex_t *type; + mr_regex_t *type_instance; +}; + +/* + * internal helper functions + */ +static void mr_free_regex (mr_regex_t *r) /* {{{ */ +{ + if (r == NULL) + return; + + regfree (&r->re); + memset (&r->re, 0, sizeof (r->re)); + free (r->re_str); + + if (r->next != NULL) + mr_free_regex (r->next); +} /* }}} void mr_free_regex */ + +static void mr_free_match (mr_match_t *m) /* {{{ */ +{ + if (m == NULL) + return; + + mr_free_regex (m->host); + mr_free_regex (m->plugin); + mr_free_regex (m->plugin_instance); + mr_free_regex (m->type); + mr_free_regex (m->type_instance); + + free (m); +} /* }}} void mr_free_match */ + +static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */ + const char *string) +{ + mr_regex_t *re; + + if (re_head == NULL) + return (FC_MATCH_MATCHES); + + for (re = re_head; re != NULL; re = re->next) + { + int status; + + status = regexec (&re->re, string, + /* nmatch = */ 0, /* pmatch = */ NULL, + /* eflags = */ 0); + if (status == 0) + { + DEBUG ("regex match: Regular expression `%s' matches `%s'.", + re->re_str, string); + } + else + { + DEBUG ("regex match: Regular expression `%s' does not match `%s'.", + re->re_str, string); + return (FC_MATCH_NO_MATCH); + } + + } + + return (FC_MATCH_MATCHES); +} /* }}} int mr_match_regexen */ + +static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */ + oconfig_item_t *ci) +{ + mr_regex_t *re; + int status; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + log_warn ("`%s' needs exactly one string argument.", ci->key); + return (-1); + } + + re = (mr_regex_t *) malloc (sizeof (*re)); + if (re == NULL) + { + log_err ("mr_config_add_regex: malloc failed."); + return (-1); + } + memset (re, 0, sizeof (*re)); + re->next = NULL; + + re->re_str = strdup (ci->values[0].value.string); + if (re->re_str == NULL) + { + free (re); + log_err ("mr_config_add_regex: strdup failed."); + return (-1); + } + + status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB); + if (status != 0) + { + char errmsg[1024]; + regerror (status, &re->re, errmsg, sizeof (errmsg)); + errmsg[sizeof (errmsg) - 1] = 0; + log_err ("Compiling regex `%s' for `%s' failed: %s.", + re->re_str, ci->key, errmsg); + free (re->re_str); + free (re); + return (-1); + } + + if (*re_head == NULL) + { + *re_head = re; + } + else + { + mr_regex_t *ptr; + + ptr = *re_head; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = re; + } + + return (0); +} /* }}} int mr_config_add_regex */ + +static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ +{ + mr_match_t *m; + int status; + int i; + + m = (mr_match_t *) malloc (sizeof (*m)); + if (m == NULL) + { + log_err ("mr_create: malloc failed."); + return (-ENOMEM); + } + memset (m, 0, sizeof (*m)); + + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if ((strcasecmp ("Host", child->key) == 0) + || (strcasecmp ("Hostname", child->key) == 0)) + status = mr_config_add_regex (&m->host, child); + else if (strcasecmp ("Plugin", child->key) == 0) + status = mr_config_add_regex (&m->plugin, child); + else if (strcasecmp ("PluginInstance", child->key) == 0) + status = mr_config_add_regex (&m->plugin_instance, child); + else if (strcasecmp ("Type", child->key) == 0) + status = mr_config_add_regex (&m->type, child); + else if (strcasecmp ("TypeInstance", child->key) == 0) + status = mr_config_add_regex (&m->type_instance, child); + else + { + log_err ("The `%s' configuration option is not understood and " + "will be ignored.", child->key); + status = 0; + } + + if (status != 0) + break; + } + + /* Additional sanity-checking */ + while (status == 0) + { + if ((m->host == NULL) + && (m->plugin == NULL) + && (m->plugin_instance == NULL) + && (m->type == NULL) + && (m->type_instance == NULL)) + { + log_err ("No (valid) regular expressions have been configured. " + "This match will be ignored."); + status = -1; + } + + break; + } + + if (status != 0) + { + mr_free_match (m); + return (status); + } + + *user_data = m; + return (0); +} /* }}} int mr_create */ + +static int mr_destroy (void **user_data) /* {{{ */ +{ + if ((user_data != NULL) && (*user_data != NULL)) + mr_free_match (*user_data); + return (0); +} /* }}} int mr_destroy */ + +static int mr_match (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + notification_meta_t **meta, void **user_data) +{ + mr_match_t *m; + + if ((user_data == NULL) || (*user_data == NULL)) + return (-1); + + m = *user_data; + + if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH) + return (FC_MATCH_NO_MATCH); + if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH) + return (FC_MATCH_NO_MATCH); + if (mr_match_regexen (m->plugin_instance, + vl->plugin_instance) == FC_MATCH_NO_MATCH) + return (FC_MATCH_NO_MATCH); + if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH) + return (FC_MATCH_NO_MATCH); + if (mr_match_regexen (m->type_instance, + vl->type_instance) == FC_MATCH_NO_MATCH) + return (FC_MATCH_NO_MATCH); + + return (FC_MATCH_MATCHES); +} /* }}} int mr_match */ + +void module_register (void) +{ + match_proc_t mproc; + + memset (&mproc, 0, sizeof (mproc)); + mproc.create = mr_create; + mproc.destroy = mr_destroy; + mproc.match = mr_match; + fc_register_match ("regex", mproc); +} /* module_register */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */ + diff --git a/src/match_value.c b/src/match_value.c new file mode 100644 index 00000000..b850b777 --- /dev/null +++ b/src/match_value.c @@ -0,0 +1,219 @@ +/** + * collectd - src/match_value.c + * Copyright (C) 2008 Florian 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 Forster + **/ + +/* + * This module allows to filter and rewrite value lists based on + * Perl-compatible regular expressions. + */ + +#include "collectd.h" +#include "utils_cache.h" +#include "filter_chain.h" + +/* + * private data types + */ +struct mv_match_s; +typedef struct mv_match_s mv_match_t; +struct mv_match_s +{ + gauge_t min; + gauge_t max; + int invert; +}; + +/* + * internal helper functions + */ +static void mv_free_match (mv_match_t *m) /* {{{ */ +{ + if (m == NULL) + return; + + free (m); +} /* }}} void mv_free_match */ + +static int mv_config_add_gauge (gauge_t *ret_value, /* {{{ */ + oconfig_item_t *ci) +{ + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + ERROR ("`value' match: `%s' needs exactly one numeric argument.", + ci->key); + return (-1); + } + + *ret_value = ci->values[0].value.number; + + return (0); +} /* }}} int mv_config_add_gauge */ + +static int mv_config_add_boolean (int *ret_value, /* {{{ */ + oconfig_item_t *ci) +{ + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) + { + ERROR ("`value' match: `%s' needs exactly one boolean argument.", + ci->key); + return (-1); + } + + if (ci->values[0].value.boolean) + *ret_value = 1; + else + *ret_value = 0; + + return (0); +} /* }}} int mv_config_add_boolean */ + +static int mv_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ +{ + mv_match_t *m; + int status; + int i; + + m = (mv_match_t *) malloc (sizeof (*m)); + if (m == NULL) + { + ERROR ("mv_create: malloc failed."); + return (-ENOMEM); + } + memset (m, 0, sizeof (*m)); + + m->min = NAN; + m->max = NAN; + m->invert = 0; + + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Min", child->key) == 0) + status = mv_config_add_gauge (&m->min, child); + else if (strcasecmp ("Max", child->key) == 0) + status = mv_config_add_gauge (&m->max, child); + else if (strcasecmp ("Invert", child->key) == 0) + status = mv_config_add_boolean (&m->invert, child); + else + { + ERROR ("`value' match: The `%s' configuration option is not " + "understood and will be ignored.", child->key); + status = 0; + } + + if (status != 0) + break; + } + + /* Additional sanity-checking */ + while (status == 0) + { + if (isnan (m->min) && isnan (m->max)) + { + ERROR ("`value' match: Neither minimum nor maximum are defined. " + "This match will be ignored."); + status = -1; + } + + break; + } + + if (status != 0) + { + mv_free_match (m); + return (status); + } + + *user_data = m; + return (0); +} /* }}} int mv_create */ + +static int mv_destroy (void **user_data) /* {{{ */ +{ + if ((user_data != NULL) && (*user_data != NULL)) + mv_free_match (*user_data); + return (0); +} /* }}} int mv_destroy */ + +static int mv_match (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + notification_meta_t **meta, void **user_data) +{ + mv_match_t *m; + gauge_t *values; + int status; + int i; + + if ((user_data == NULL) || (*user_data == NULL)) + return (-1); + + m = *user_data; + + values = uc_get_rate (ds, vl); + if (values == NULL) + { + ERROR ("`value' match: Retrieving the current rate from the cache " + "failed."); + return (-1); + } + + status = FC_MATCH_MATCHES; + for (i = 0; i < ds->ds_num; i++) + { + DEBUG ("`value' match: current = %g; min = %g; max = %g; invert = %s;", + values[i], m->min, m->max, + m->invert ? "true" : "false"); + + if ((!isnan (m->min) && (values[i] < m->min)) + || (!isnan (m->max) && (values[i] > m->max))) + { + status = FC_MATCH_NO_MATCH; + break; + } + } + + if (m->invert) + { + if (status == FC_MATCH_MATCHES) + status = FC_MATCH_NO_MATCH; + else + status = FC_MATCH_MATCHES; + } + + free (values); + return (status); +} /* }}} int mv_match */ + +void module_register (void) +{ + match_proc_t mproc; + + memset (&mproc, 0, sizeof (mproc)); + mproc.create = mv_create; + mproc.destroy = mv_destroy; + mproc.match = mv_match; + fc_register_match ("value", mproc); +} /* module_register */ + +/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */ + diff --git a/src/meta_data.c b/src/meta_data.c new file mode 100644 index 00000000..1412f7ef --- /dev/null +++ b/src/meta_data.c @@ -0,0 +1,536 @@ +/** + * collectd - src/meta_data.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 + **/ + +/* + * First tell the compiler to stick to the C99 and POSIX standards as close as + * possible. + */ +#ifndef __STRICT_ANSI__ /* {{{ */ +# define __STRICT_ANSI__ +#endif + +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif + +#ifdef _POSIX_C_SOURCE +# undef _POSIX_C_SOURCE +#endif +#define _POSIX_C_SOURCE 200112L + +#if 0 +/* Single UNIX needed for strdup. */ +#ifdef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif +#define _XOPEN_SOURCE 500 +#endif + +#ifndef _REENTRANT +# define _REENTRANT +#endif + +#ifndef _THREAD_SAFE +# define _THREAD_SAFE +#endif + +#ifdef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* }}} */ + +#include "collectd.h" +#include "plugin.h" +#include "meta_data.h" + +#include + +/* + * Defines + */ +#define MD_TYPE_STRING 1 +#define MD_TYPE_SIGNED_INT 2 +#define MD_TYPE_UNSIGNED_INT 3 +#define MD_TYPE_DOUBLE 4 + +/* + * Data types + */ +union meta_value_u +{ + char *mv_string; + int64_t mv_signed_int; + uint64_t mv_unsigned_int; + double mv_double; +}; +typedef union meta_value_u meta_value_t; + +struct meta_entry_s; +typedef struct meta_entry_s meta_entry_t; +struct meta_entry_s +{ + char *key; + meta_value_t value; + int type; + meta_entry_t *next; +}; + +struct meta_data_s +{ + meta_entry_t *head; + pthread_mutex_t lock; +}; + +/* + * Private functions + */ +static char *md_strdup (const char *orig) /* {{{ */ +{ + size_t sz; + char *dest; + + if (orig == NULL) + return (NULL); + + sz = strlen (orig) + 1; + dest = (char *) malloc (sz); + if (dest == NULL) + return (NULL); + + memcpy (dest, orig, sz); + + return (dest); +} /* }}} char *md_strdup */ + +static meta_entry_t *md_entry_alloc (const char *key) /* {{{ */ +{ + meta_entry_t *e; + + e = (meta_entry_t *) malloc (sizeof (*e)); + if (e == NULL) + { + ERROR ("md_entry_alloc: malloc failed."); + return (NULL); + } + memset (e, 0, sizeof (*e)); + + e->key = md_strdup (key); + if (e->key == NULL) + { + free (e); + ERROR ("md_entry_alloc: md_strdup failed."); + return (NULL); + } + + e->type = 0; + e->next = NULL; + + return (e); +} /* }}} meta_entry_t *md_entry_alloc */ + +static void md_entry_free (meta_entry_t *e) /* {{{ */ +{ + if (e == NULL) + return; + + free (e->key); + + if (e->type == MD_TYPE_STRING) + free (e->value.mv_string); + + if (e->next != NULL) + md_entry_free (e->next); + + free (e); +} /* }}} void md_entry_free */ + +static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */ +{ + meta_entry_t *this; + meta_entry_t *prev; + + if ((md == NULL) || (e == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + prev = NULL; + this = md->head; + while (this != NULL) + { + if (strcasecmp (e->key, this->key) == 0) + break; + + prev = this; + this = this->next; + } + + if (this == NULL) + { + /* This key does not exist yet. */ + if (md->head == NULL) + md->head = e; + else + { + assert (prev != NULL); + prev->next = e; + } + + e->next = NULL; + } + else /* (this != NULL) */ + { + if (prev == NULL) + md->head = e; + else + prev->next = e; + + e->next = this->next; + } + + pthread_mutex_unlock (&md->lock); + + if (this != NULL) + { + this->next = NULL; + md_entry_free (this); + } + + return (0); +} /* }}} int md_entry_insert */ + +/* XXX: The lock on md must be held while calling this function! */ +static meta_entry_t *md_entry_lookup (meta_data_t *md, /* {{{ */ + const char *key) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return (NULL); + + for (e = md->head; e != NULL; e = e->next) + if (strcasecmp (key, e->key) == 0) + break; + + return (e); +} /* }}} meta_entry_t *md_entry_lookup */ + +/* + * Public functions + */ +meta_data_t *meta_data_create (void) /* {{{ */ +{ + meta_data_t *md; + + md = (meta_data_t *) malloc (sizeof (*md)); + if (md == NULL) + { + ERROR ("meta_data_create: malloc failed."); + return (NULL); + } + memset (md, 0, sizeof (*md)); + + md->head = NULL; + pthread_mutex_init (&md->lock, /* attr = */ NULL); + + return (md); +} /* }}} meta_data_t *meta_data_create */ + +void meta_data_destroy (meta_data_t *md) /* {{{ */ +{ + if (md == NULL) + return; + + md_entry_free (md->head); + free (md); +} /* }}} void meta_data_destroy */ + +int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */ +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + for (e = md->head; e != NULL; e = e->next) + { + if (strcasecmp (key, e->key) == 0) + { + pthread_mutex_unlock (&md->lock); + return (1); + } + } + + pthread_mutex_unlock (&md->lock); + return (0); +} /* }}} int meta_data_exists */ + +int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */ +{ + meta_entry_t *this; + meta_entry_t *prev; + + if ((md == NULL) || (key == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + prev = NULL; + this = md->head; + while (this != NULL) + { + if (strcasecmp (key, this->key) == 0) + break; + + prev = this; + this = this->next; + } + + if (this == NULL) + { + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + if (prev == NULL) + md->head = this->next; + else + prev->next = this->next; + + pthread_mutex_unlock (&md->lock); + + this->next = NULL; + md_entry_free (this); + + return (0); +} /* }}} int meta_data_delete */ + +int meta_data_add_string (meta_data_t *md, /* {{{ */ + const char *key, const char *value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return (-EINVAL); + + e = md_entry_alloc (key); + if (e == NULL) + return (-ENOMEM); + + e->value.mv_string = md_strdup (value); + if (e->value.mv_string == NULL) + { + ERROR ("meta_data_add_string: md_strdup failed."); + md_entry_free (e); + return (-ENOMEM); + } + e->type = MD_TYPE_STRING; + + return (md_entry_insert (md, e)); +} /* }}} int meta_data_add_string */ + +int meta_data_add_signed_int (meta_data_t *md, /* {{{ */ + const char *key, int64_t value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return (-EINVAL); + + e = md_entry_alloc (key); + if (e == NULL) + return (-ENOMEM); + + e->value.mv_signed_int = value; + e->type = MD_TYPE_SIGNED_INT; + + return (md_entry_insert (md, e)); +} /* }}} int meta_data_add_signed_int */ + +int meta_data_add_unsigned_int (meta_data_t *md, /* {{{ */ + const char *key, uint64_t value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return (-EINVAL); + + e = md_entry_alloc (key); + if (e == NULL) + return (-ENOMEM); + + e->value.mv_unsigned_int = value; + e->type = MD_TYPE_UNSIGNED_INT; + + return (md_entry_insert (md, e)); +} /* }}} int meta_data_add_unsigned_int */ + +int meta_data_add_double (meta_data_t *md, /* {{{ */ + const char *key, double value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return (-EINVAL); + + e = md_entry_alloc (key); + if (e == NULL) + return (-ENOMEM); + + e->value.mv_double = value; + e->type = MD_TYPE_DOUBLE; + + return (md_entry_insert (md, e)); +} /* }}} int meta_data_add_double */ + +int meta_data_get_string (meta_data_t *md, /* {{{ */ + const char *key, char **value) +{ + meta_entry_t *e; + char *temp; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + e = md_entry_lookup (md, key); + if (e == NULL) + { + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + if (e->type != MD_TYPE_SIGNED_INT) + { + ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + temp = md_strdup (e->value.mv_string); + if (temp == NULL) + { + pthread_mutex_unlock (&md->lock); + ERROR ("meta_data_get_string: md_strdup failed."); + return (-ENOMEM); + } + + pthread_mutex_unlock (&md->lock); + + *value = temp; + + return (0); +} /* }}} int meta_data_get_string */ + +int meta_data_get_signed_int (meta_data_t *md, /* {{{ */ + const char *key, int64_t *value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + e = md_entry_lookup (md, key); + if (e == NULL) + { + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + if (e->type != MD_TYPE_SIGNED_INT) + { + ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + *value = e->value.mv_signed_int; + + pthread_mutex_unlock (&md->lock); + return (0); +} /* }}} int meta_data_get_signed_int */ + +int meta_data_get_unsigned_int (meta_data_t *md, /* {{{ */ + const char *key, uint64_t *value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + e = md_entry_lookup (md, key); + if (e == NULL) + { + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + if (e->type != MD_TYPE_UNSIGNED_INT) + { + ERROR ("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + *value = e->value.mv_unsigned_int; + + pthread_mutex_unlock (&md->lock); + return (0); +} /* }}} int meta_data_get_unsigned_int */ + +int meta_data_get_double (meta_data_t *md, /* {{{ */ + const char *key, double *value) +{ + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&md->lock); + + e = md_entry_lookup (md, key); + if (e == NULL) + { + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + if (e->type != MD_TYPE_DOUBLE) + { + ERROR ("meta_data_get_double: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock (&md->lock); + return (-ENOENT); + } + + *value = e->value.mv_double; + + pthread_mutex_unlock (&md->lock); + return (0); +} /* }}} int meta_data_get_double */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/meta_data.h b/src/meta_data.h new file mode 100644 index 00000000..30e72854 --- /dev/null +++ b/src/meta_data.h @@ -0,0 +1,63 @@ +/** + * collectd - src/meta_data.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 META_DATA_H +#define META_DATA_H + +#include "collectd.h" + +struct meta_data_s; +typedef struct meta_data_s meta_data_t; + +meta_data_t *meta_data_create (void); +void meta_data_destroy (meta_data_t *md); + +int meta_data_exists (meta_data_t *md, const char *key); +int meta_data_delete (meta_data_t *md, const char *key); + +int meta_data_add_string (meta_data_t *md, + const char *key, + const char *value); +int meta_data_add_signed_int (meta_data_t *md, + const char *key, + int64_t value); +int meta_data_add_unsigned_int (meta_data_t *md, + const char *key, + uint64_t value); +int meta_data_add_double (meta_data_t *md, + const char *key, + double value); + +int meta_data_get_string (meta_data_t *md, + const char *key, + char **value); +int meta_data_get_signed_int (meta_data_t *md, + const char *key, + int64_t *value); +int meta_data_get_unsigned_int (meta_data_t *md, + const char *key, + uint64_t *value); +int meta_data_get_double (meta_data_t *md, + const char *key, + double *value); + +#endif /* META_DATA_H */ +/* vim: set sw=2 sts=2 et : */ diff --git a/src/mysql.c b/src/mysql.c index 4e72b5bf..57ad2397 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -38,14 +38,18 @@ static const char *config_keys[] = "User", "Password", "Database", + "Port", + "Socket", NULL }; -static int config_keys_num = 4; +static int config_keys_num = 6; static char *host = "localhost"; static char *user; static char *pass; static char *db = NULL; +static char *socket = NULL; +static int port = 0; static MYSQL *getconnection (void) { @@ -88,7 +92,7 @@ static MYSQL *getconnection (void) return (NULL); } - if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL) + if (mysql_real_connect (con, host, user, pass, db, port, socket, 0) == NULL) { ERROR ("mysql_real_connect failed: %s", mysql_error (con)); state = 0; @@ -113,9 +117,36 @@ static int config (const char *key, const char *value) return ((pass = strdup (value)) == NULL ? 1 : 0); else if (strcasecmp (key, "database") == 0) return ((db = strdup (value)) == NULL ? 1 : 0); + else if (strcasecmp (key, "socket") == 0) + return ((socket = strdup (value)) == NULL ? 1 : 0); + else if (strcasecmp (key, "port") == 0) + { + char *endptr = NULL; + int temp; + + errno = 0; + temp = strtol (value, &endptr, 0); + if ((errno != 0) || (value == endptr)) + { + ERROR ("mysql plugin: Invalid \"Port\" argument: %s", + value); + port = 0; + return (1); + } + else if ((temp < 0) || (temp >= 65535)) + { + ERROR ("mysql plugin: Port number out of range: %i", + temp); + port = 0; + return (1); + } + + port = temp; + return (0); + } else return (-1); -} +} /* int config */ static void counter_submit (const char *type, const char *type_instance, counter_t value) 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 a8873277..4ad7366c 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -36,6 +36,7 @@ #include "utils_llist.h" #include "utils_cache.h" #include "utils_threshold.h" +#include "filter_chain.h" /* * Private structures @@ -55,6 +56,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 +444,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 +550,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)); @@ -665,6 +678,73 @@ void plugin_read_all (void) pthread_mutex_unlock (&read_lock); } /* void plugin_read_all */ +int plugin_write (const char *plugin, /* {{{ */ + const data_set_t *ds, const value_list_t *vl) +{ + int (*callback) (const data_set_t *ds, const value_list_t *vl); + llentry_t *le; + int status; + + if (vl == NULL) + return (EINVAL); + + if (list_write == NULL) + return (ENOENT); + + if (ds == NULL) + { + ds = plugin_get_ds (vl->type); + if (ds == NULL) + { + ERROR ("plugin_write: Unable to lookup type `%s'.", vl->type); + return (ENOENT); + } + } + + if (plugin == NULL) + { + int success = 0; + int failure = 0; + + le = llist_head (list_write); + while (le != NULL) + { + callback = le->value; + status = (*callback) (ds, vl); + if (status != 0) + failure++; + else + success++; + + le = le->next; + } + + if ((success == 0) && (failure != 0)) + status = -1; + else + status = 0; + } + else /* plugin != NULL */ + { + le = llist_head (list_write); + while (le != NULL) + { + if (strcasecmp (plugin, le->key) == 0) + break; + + le = le->next; + } + + if (le == NULL) + return (ENOENT); + + callback = le->value; + status = (*callback) (ds, vl); + } + + return (status); +} /* }}} int plugin_write */ + int plugin_flush (const char *plugin, int timeout, const char *identifier) { int (*callback) (int timeout, const char *identifier); @@ -718,11 +798,9 @@ 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; if ((vl == NULL) || (*vl->type == '\0')) { ERROR ("plugin_dispatch_values: Invalid value list."); @@ -787,16 +865,8 @@ int plugin_dispatch_values (value_list_t *vl) /* Update the value cache */ uc_update (ds, vl); - ut_check_threshold (ds, vl); - le = llist_head (list_write); - while (le != NULL) - { - callback = (int (*) (const data_set_t *, const value_list_t *)) le->value; - (*callback) (ds, vl); - - le = le->next; - } + fc_process (ds, vl); return (0); } /* int plugin_dispatch_values */ diff --git a/src/plugin.h b/src/plugin.h index dc3bbb08..a6f89a0e 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 */ @@ -178,6 +183,36 @@ void plugin_init_all (void); void plugin_read_all (void); void plugin_shutdown_all (void); +/* + * NAME + * plugin_write + * + * DESCRIPTION + * Calls the write function of the given plugin with the provided data set and + * value list. It differs from `plugin_dispatch_value' in that it does not + * update the cache, does no do threshold checking, call the chain subsystem + * and so on. It looks up the requested plugin and invokes the function, end + * of story. + * + * ARGUMENTS + * plugin Name of the plugin. If NULL, the value is sent to all registered + * write functions. + * ds Pointer to the data_set_t structure. If NULL, the data set is + * looked up according to the `type' member in the `vl' argument. + * vl The actual value to be processes. Must not be NULL. + * + * RETURN VALUE + * Returns zero upon success or non-zero if an error occurred. If `plugin' is + * NULL and more than one plugin is called, an error is only returned if *all* + * plugins fail. + * + * NOTES + * This is the function used by the `write' built-in target. May be used by + * other target plugins. + */ +int plugin_write (const char *plugin, + const data_set_t *ds, const value_list_t *vl); + int plugin_flush (const char *plugin, int timeout, const char *identifier); /* @@ -196,6 +231,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 +248,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/target_set.c b/src/target_set.c new file mode 100644 index 00000000..70b0fdfc --- /dev/null +++ b/src/target_set.c @@ -0,0 +1,267 @@ +/** + * collectd - src/target_set.c + * Copyright (C) 2008 Florian 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 Forster + **/ + +/* + * First tell the compiler to stick to the C99 and POSIX standards as close as + * possible. + */ +#ifndef __STRICT_ANSI__ /* {{{ */ +# define __STRICT_ANSI__ +#endif + +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif + +#ifdef _POSIX_C_SOURCE +# undef _POSIX_C_SOURCE +#endif +#define _POSIX_C_SOURCE 200112L + +#if 0 +/* Single UNIX needed for strdup. */ +#ifdef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif +#define _XOPEN_SOURCE 500 +#endif + +#ifndef _REENTRANT +# define _REENTRANT +#endif + +#ifndef _THREAD_SAFE +# define _THREAD_SAFE +#endif + +#ifdef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* }}} */ + +#include "collectd.h" +#include "common.h" +#include "filter_chain.h" + +struct ts_data_s +{ + char *host; + char *plugin; + char *plugin_instance; + /* char *type; */ + char *type_instance; +}; +typedef struct ts_data_s ts_data_t; + +static char *ts_strdup (const char *orig) /* {{{ */ +{ + size_t sz; + char *dest; + + if (orig == NULL) + return (NULL); + + sz = strlen (orig) + 1; + dest = (char *) malloc (sz); + if (dest == NULL) + return (NULL); + + memcpy (dest, orig, sz); + + return (dest); +} /* }}} char *ts_strdup */ + +static int ts_config_add_string (char **dest, /* {{{ */ + const oconfig_item_t *ci, int may_be_empty) +{ + char *temp; + + if (dest == NULL) + return (-EINVAL); + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + ERROR ("Target `set': The `%s' option requires exactly one string " + "argument.", ci->key); + return (-1); + } + + if ((!may_be_empty) && (ci->values[0].value.string[0] == 0)) + { + ERROR ("Target `set': The `%s' option does not accept empty strings.", + ci->key); + return (-1); + } + + temp = ts_strdup (ci->values[0].value.string); + if (temp == NULL) + { + ERROR ("ts_config_add_string: ts_strdup failed."); + return (-1); + } + + free (*dest); + *dest = temp; + + return (0); +} /* }}} int ts_config_add_string */ + +static int ts_destroy (void **user_data) /* {{{ */ +{ + ts_data_t *data; + + if (user_data == NULL) + return (-EINVAL); + + data = *user_data; + if (data == NULL) + return (0); + + free (data->host); + free (data->plugin); + free (data->plugin_instance); + /* free (data->type); */ + free (data->type_instance); + free (data); + + return (0); +} /* }}} int ts_destroy */ + +static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ +{ + ts_data_t *data; + int status; + int i; + + data = (ts_data_t *) malloc (sizeof (*data)); + if (data == NULL) + { + ERROR ("ts_create: malloc failed."); + return (-ENOMEM); + } + memset (data, 0, sizeof (*data)); + + data->host = NULL; + data->plugin = NULL; + data->plugin_instance = NULL; + /* data->type = NULL; */ + data->type_instance = NULL; + + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if ((strcasecmp ("Host", child->key) == 0) + || (strcasecmp ("Hostname", child->key) == 0)) + status = ts_config_add_string (&data->host, child, + /* may be empty = */ 0); + else if (strcasecmp ("Plugin", child->key) == 0) + status = ts_config_add_string (&data->plugin, child, + /* may be empty = */ 0); + else if (strcasecmp ("PluginInstance", child->key) == 0) + status = ts_config_add_string (&data->plugin_instance, child, + /* may be empty = */ 1); +#if 0 + else if (strcasecmp ("Type", child->key) == 0) + status = ts_config_add_string (&data->type, child, + /* may be empty = */ 0); +#endif + else if (strcasecmp ("TypeInstance", child->key) == 0) + status = ts_config_add_string (&data->type_instance, child, + /* may be empty = */ 1); + else + { + ERROR ("Target `set': The `%s' configuration option is not understood " + "and will be ignored.", child->key); + status = 0; + } + + if (status != 0) + break; + } + + /* Additional sanity-checking */ + while (status == 0) + { + if ((data->host == NULL) + && (data->plugin == NULL) + && (data->plugin_instance == NULL) + /* && (data->type == NULL) */ + && (data->type_instance == NULL)) + { + ERROR ("Target `set': You need to set at lease one of `Host', " + "`Plugin', `PluginInstance', `Type', or `TypeInstance'."); + status = -1; + } + + break; + } + + if (status != 0) + { + ts_destroy ((void *) &data); + return (status); + } + + *user_data = data; + return (0); +} /* }}} int ts_create */ + +static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */ + notification_meta_t **meta, void **user_data) +{ + ts_data_t *data; + + if ((ds == NULL) || (vl == NULL) || (user_data == NULL)) + return (-EINVAL); + + data = *user_data; + if (data == NULL) + { + ERROR ("Target `set': Invoke: `data' is NULL."); + return (-EINVAL); + } + +#define SET_FIELD(f) if (data->f != NULL) { sstrncpy (vl->f, data->f, sizeof (vl->f)); } + SET_FIELD (host); + SET_FIELD (plugin); + SET_FIELD (plugin_instance); + /* SET_FIELD (type); */ + SET_FIELD (type_instance); + + return (FC_TARGET_CONTINUE); +} /* }}} int ts_invoke */ + +void module_register (void) +{ + target_proc_t tproc; + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = ts_create; + tproc.destroy = ts_destroy; + tproc.invoke = ts_invoke; + fc_register_target ("set", tproc); +} /* module_register */ + +/* vim: set sw=2 sts=2 tw=78 et 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/types.db.pod b/src/types.db.pod index f0a49f6b..11c7f794 100644 --- a/src/types.db.pod +++ b/src/types.db.pod @@ -29,9 +29,10 @@ happen. See L for more details. =head1 FILES The location of the types.db file is defined by the B configuration -option (see L). If you want to specify custom data-sets, you -should do so by using a custom file specified as an additional argument to the -B option. +option (see L). It defaults to collectd's shared data +directory, i.Ee. F/share/collectd/>. If you want to specify +custom data-sets, you should do so by using a custom file specified as an +additional argument to the B option. =head1 SEE ALSO 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 : */ +