From: Marc Fournier Date: Tue, 19 May 2015 11:34:10 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/pr/651' X-Git-Tag: collectd-5.5.0~24 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=8395c8dbb8f5a872c5cde8db4c47a34cdb5f7c2f;hp=363bd0300297144a29b58cc813b23d9353525178;p=collectd.git Merge remote-tracking branch 'origin/pr/651' Conflicts: contrib/redhat/collectd.spec --- diff --git a/.gitignore b/.gitignore index f005eddd..5202bc26 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ src/*.1 src/*.5 src/.pod2man.tmp.* src/libcollectdclient/collectd/lcc_features.h -src/utils_vl_lookup_test # patch stuff *.rej @@ -82,6 +81,8 @@ src/tags # tests stuff src/tests/.deps/ src/tests/mock/.deps/ +src/tests/.dirstamp +src/tests/mock/.dirstamp # new daemon repo src/daemon/.deps/ diff --git a/AUTHORS b/AUTHORS index 00812226..a0972cca 100644 --- a/AUTHORS +++ b/AUTHORS @@ -81,6 +81,9 @@ Cyril Feraudet Dan Berrange - uuid plugin. +Dan Ryder + - ceph plugin. + David Bacher - serial plugin. @@ -103,6 +106,9 @@ Fabian Linzberger Fabien Wernli - Solaris improvements in the memory and interfaces plugin. +Fabrice A. Marie + - write_sensu plugin. + Flavio Stanchina - mbmon plugin. diff --git a/ChangeLog b/ChangeLog index 8112afac..28be899a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,93 @@ +2015-02-26, Version 5.4.2 + * Build system: Numerous fixes. Thanks to Bjørn Nordbø, Jim Radford, + KOMEDA Shinji, Lauri Tirkkonen, Manuel Luis Sanmartin Rozada, Marc + Fournier, Rainer Müller, Yoga Ramalingam and Yves Mettier. #326, + #373, #653, #828 + * collectd: A use-after-free has been fixed in the "parse_value()" + function. Thanks to Matthias Urlichs. + * collectd: Fix carriage return sign in types_list Thanks to Marc + Fournier and @NsLib. + * collectd: Fix programming error in src/configfile.c. Thanks to + Wilfried Goesgens. + * collectd: An off-by-one error has been fixed in the + "strstripnewline()" function. Patch by Florian Forster. + * collectd: Use the complain mechanism to report filter chain write + failures. Thanks to Sebastian Harl. + * collectd: Spelling and grammar of error messages have been fixed. + Thanks to Katelyn Perry and Tim Laszlo. + * collectdctl: Fixed buffering issues which caused trouble on AIX and + Solaris. Thanks to Yoga Ramalingam. + * Documentation: Details and example about multi-instance filterchain + targets have been added. Thanks to Marc Fournier. + * Documentation: The "CollectStatistics" option of the rrdcached has + been documented. Thanks to Micha Krause. #907 + * Documentation: The write_redis has been documented. Thanks to Marc + Fournier. + * Documentation: The "GraphiteSeparateInstances" and + "GraphiteAlwaysAppendDS" options of the amqp have been documented. + Thanks to Marc Fournier. + * Documentation: Documentation of the "AutoLoadPlugin" option has been + improved. Thanks to Florian Forster. #715 + * aggregation: "utils_vl_lookup": A race when creating user objects + has been fixed. Thanks to Sebastian Harl. #535 + * cpu: Temperature code for Mac OS X has been removed. + Thanks to Florian Forster and Marc Fournier. #22 + * cURL, cURL-JSON, cURL-XML and Write HTTP plugins: Call + "curl_global_init()" in the plugins' "init()" callback. Thanks to + Jeremy Katz. + * cURL and memcachec plugins: Fix calculation of gauge, average, + minimum and maximum. Previously, they were calculated from the start + of the daemon, which is not the documented behavior. Thanks to + Florian Forster. #663 + * curl-json: A bug, which triggered when two URLs with a long common + prefix were configured, was fixed. Thanks to Marc Fournier. #582 + * dbi: Compatibility with new versions of libdbi has been restored. + Thanks to Florian Forster. #950 + * Exec, UnixSock plugins: Fix parsing of the "time" option of the + "PUTNOTIF" command. Thanks to Adrian Miron. #477 + * ipmi: A conflict with the java over the "SIGUSR2" signal has been + fixed. Thanks to Vincent Bernat. #114 + * java: Conversion from Java's time representation to collectd's + representation has been fixed. Thanks to Manuel Luis Sanmartín + Rozada. + * java: Make sure "cjni_thread_detach()" is called on all paths. + Thanks to Florian Forster. + * LogFile and SysLog plugins: Avoid total silence in case of a + misconfiguration. Thanks to Marc Fournier and Wilfried Goesgens. + * network: Support for recent versions of gcrypt has been added. + Thanks to Vincent Bernat. #632 + * network: Robustness of the client connecting behavior has been + improved. Thanks to Florian Forster. #627 + * python: Don't create empty "meta_data_t" objects. Thanks to Florian + Forster. #716 + * python: Fix Py list length check in "cpy_build_meta()". Thanks to + Yoga Ramalingam. + * python: The "interval" member was fixed to export seconds as a + double. Thanks to Justin Burnham. + * RRDtool and RRDCacheD plugins: A memory leak when creating RRD files + has been fixed. Thanks to Yves Mettier. #661 + * snmp: Fix a memory leak. Thanks to Marc Fournier and Pierre-Yves + Ritschard. #610, #804 + * statsd: Support for samplerates in timer metrics was added. Thanks + to John Leach. #461 + * swap: Fix behavior under OpenVZ by making "cached" optional. Thanks + to Florian Forster. #733 + * threshold: Population of the "time" field in notifications has been + added. Thanks to Manuel Luis Sanmartín Rozada. + * libvirt: Only gather stats for running domains. Thanks to Ruben + Kerkhof. + * lvm: An issue with Volume Groups (VGs) without Logical Volumes (LVs) + has been fixed. Thanks to Jan Kundrát. + * write_graphite: Escape characters not supported by Graphite. Thanks + to Pierre-Yves Ritschard and Marc Fournier. + * write_http: Make callback names context-dependent. Thanks to Marc + Fournier. #821 + * write_redis: A formatting bug, which resulted in totally unusable + numbers being transmitted to Redis, was fixed. Thanks to Marc + Fournier. + * write_riemann: Receive acknowledge message when using TCP. Thanks to + John-John Tedro. + 2014-01-26, Version 5.4.1 * amqp plugin: Add support for RabbitMQ 0.4.x to avoid compiler warnings. Thanks to Sebastian Harl for implementing this. @@ -74,6 +164,95 @@ * zfs_arc plugin: Support for FreeBSD has been added. Thanks to Xin Li for his patch. +2015-02-26, Version 5.3.2 + * Build system: Numerous fixes. Thanks to Bjørn Nordbø, Jim Radford, + KOMEDA Shinji, Lauri Tirkkonen, Manuel Luis Sanmartin Rozada, Marc + Fournier, Rainer Müller, Yoga Ramalingam and Yves Mettier. #326, + #373, #653, #828 + * collectd: A use-after-free has been fixed in the "parse_value()" + function. Thanks to Matthias Urlichs. + * collectd: Fix carriage return sign in types_list Thanks to Marc + Fournier and @NsLib. + * collectd: Fix programming error in src/configfile.c Thanks to + Wilfried Goesgens. + * collectd: An off-by-one error has been fixed in the + "strstripnewline()" function. Patch by Florian Forster. + * collectd: Use the complain mechanism to report filter chain write + failures. Thanks to Sebastian Harl. + * collectd: Spelling and grammar of error messages have been fixed. + Thanks to Katelyn Perry and Tim Laszlo. + * collectdctl: Fixed buffering issues which caused trouble on AIX and + Solaris. Thanks to Yoga Ramalingam. + * Documentation: Details and example about multi-instance filterchain + targets have been added. Thanks to Marc Fournier. + * Documentation: The "CollectStatistics" option of the rrdcached has + been documented. Thanks to Micha Krause. #907 + * Documentation: The write_redis has been documented. Thanks to Marc + Fournier. + * Documentation: The synopsis of the threshold has been fixed. Thanks + to Fabien Wernli. + * Documentation: The "GraphiteSeparateInstances" and + "GraphiteAlwaysAppendDS" options of the amqp have been documented. + Thanks to Marc Fournier. + * aggregation: "utils_vl_lookup": A race when creating user objects + has been fixed. Thanks to Sebastian Harl. #535 + * cpu: Temperature code for Mac OS X has been removed. + Thanks to Florian Forster and Marc Fournier. #22 + * csv: A regression which would lead to the "DataDir" option to be + ignored has been fixed. Thanks to Manuel Luis Sanmartin Rozada. + * curl, curl-json, curl-xml and write_http plugins: Call + "curl_global_init()" in the plugins' "init()" callback. Thanks to + Jeremy Katz. + * curl and memcachec plugins: Fix calculation of gauge, average, + minimum and maximum. Previously, they were calculated from the start + of the daemon, which is not the documented behavior. Thanks to + Florian Forster. #663 + * dbi plugin: Compatibility with new versions of libdbi has been + restored. Thanks to Florian Forster. #950 + * exec, unixsock plugins: Fix parsing of the "time" option of the + "PUTNOTIF" command. Thanks to Adrian Miron. #477 + * java: Conversion from Java's time representation to collectd's + representation has been fixed. Thanks to Manuel Luis Sanmartín + Rozada. + * ipmi: A conflict with the java over the "SIGUSR2" signal has been + fixed. Thanks to Vincent Bernat. #114 + * java: Make sure "cjni_thread_detach()" is called on all paths. + Thanks to Florian Forster. + * logfile and syslog plugins: Avoid total silence in case of a + misconfiguration. Thanks to Marc Fournier and Wilfried Goesgens. + * memcached: Connecting to a UNIX socket has been fixed. Thanks to Jim + Radford. + * network: Support for recent versions of gcrypt has been added. + Thanks to Vincent Bernat. #632 + * network: Robustness of the client connecting behavior has been + improved. Thanks to Florian Forster. #627 + * python: Don't create empty "meta_data_t" objects. Thanks to Florian + Forster. #716 + * python: Fix Py list length check in "cpy_build_meta()". Thanks to + Yoga Ramalingam. + * python: The "interval" member was fixed to export seconds as a + double. Thanks to Justin Burnham. + * replace and set targets: Fix error message. Thanks to Marc Fournier. + #448 + * rrdtool and rrdcached plugins: Honor the "DataDir" config option; + this fixes a regression. Thanks to Florian Forster. #380 + * rrdtool and rrdcached plugins: A memory leak when creating RRD files + has been fixed. Thanks to Yves Mettier. #661 + * snmp: Fix a memory leak. Thanks to Marc Fournier and Pierre-Yves + Ritschard. #610, #804 + * swap: Fix behavior under OpenVZ by making "cached" optional. Thanks + to Florian Forster. #733 + * threshold: Population of the "time" field in notifications has been + added. Thanks to Manuel Luis Sanmartín Rozada. + * libvirt: Only gather stats for running domains. Thanks to Ruben + Kerkhof. + * write_graphite: Escape characters not supported by Graphite. Thanks + to Pierre-Yves Ritschard and Marc Fournier. + * write_http: Make callback names context-dependent. Thanks to Marc + Fournier. #821 + * write_riemann: Receive acknowledge message when using TCP. Thanks to + John-John Tedro. + 2013-07-13, Version 5.3.1 * Documentation: Various fixes. * Configuration: Fix error handling: Errors in included files were diff --git a/README b/README index 133c034c..539de205 100644 --- a/README +++ b/README @@ -33,9 +33,9 @@ Features Statistics about Ascent, a free server for the game `World of Warcraft'. - barometer - Using digital barometer sensor MPL115A2 or MPL3115 from Freescale - provides absolute barometric pressure, air pressure reduced to sea level - and temperature. + Reads absolute barometric pressure, air pressure reduced to sea level and + temperature. Supported sensors are MPL115A2 and MPL3115 from Freescale + and BMP085 from Bosch. - battery Batterycharge, -current and voltage of ACPI and PMU based laptop @@ -45,6 +45,9 @@ Features Name server and resolver statistics from the `statistics-channel' interface of BIND 9.5, 9,6 and later. + - ceph + Statistics from the Ceph distributed storage system. + - cgroups CPU accounting information for process groups under Linux. @@ -120,13 +123,17 @@ Features Interface traffic: Number of octets, packets and errors for each interface. - - iptables - Iptables' counters: Number of bytes that were matched by a certain - iptables rule. + - ipc + IPC counters: semaphores used, number of allocated segments in shared + memory and more. - ipmi IPMI (Intelligent Platform Management Interface) sensors information. + - iptables + Iptables' counters: Number of bytes that were matched by a certain + iptables rule. + - ipvs IPVS connection statistics (number of connections, octets and packets for each service and destination). @@ -214,13 +221,13 @@ Features - ntpd NTP daemon statistics: Local clock drift, offset to peers, etc. + - numa + Information about Non-Uniform Memory Access (NUMA). + - nut Network UPS tools: UPS current, voltage, power, charge, utilisation, temperature, etc. See upsd(8). - - numa - Information about Non-Uniform Memory Access (NUMA). - - olsrd Queries routing information from the “Optimized Link State Routing” daemon. @@ -425,10 +432,6 @@ Features can be configured to avoid logging send errors (especially useful when using UDP). - - write_tsdb - Sends data OpenTSDB, a scalable no master, no shared state time series - database. - - write_http Sends the values collected by collectd to a web-server using HTTP POST requests. The transmitted data is either in a form understood by the @@ -449,6 +452,14 @@ Features - write_riemann Sends data to Riemann, a stream processing and monitoring system. + - write_sensu + Sends data to Sensu, a stream processing and monitoring system, via the + Sensu client local TCP socket. + + - write_tsdb + Sends data OpenTSDB, a scalable no master, no shared state time series + database. + * Logging is, as everything in collectd, provided by plugins. The following plugins keep up informed about what's going on: @@ -807,8 +818,8 @@ Prerequisites * libyajl (optional) - Parse JSON data. This is needed for the `curl_json' and `log_logstash' - plugins. + Parse JSON data. This is needed for the `ceph', `curl_json' and + `log_logstash' plugins. * libvarnish (optional) diff --git a/clean.sh b/clean.sh index 46e8c29e..6780cdab 100755 --- a/clean.sh +++ b/clean.sh @@ -27,7 +27,6 @@ true \ && rm -f src/*.o \ && rm -f src/*.la \ && rm -f src/*.lo \ -&& rm -f src/collectd \ && rm -f src/collectd.1 \ && rm -f src/collectd.conf \ && rm -f src/collectdctl \ @@ -44,6 +43,21 @@ true \ && rm -f src/*.pb-c.c \ && rm -f src/*.pb-c.h \ && rm -f src/Makefile.in \ +&& rm -f src/test-suite.log \ +&& rm -f src/test_common* \ +&& rm -f src/test_utils* \ +&& rm -f -r src/tests/.deps \ +&& rm -f -r src/tests/mock/.deps \ +&& rm -f src/tests/*.o \ +&& rm -f src/tests/mock/*.o \ +&& rm -f -r src/daemon/.deps \ +&& rm -f -r src/daemon/.libs \ +&& rm -f src/daemon/*.o \ +&& rm -f src/daemon/*.la \ +&& rm -f src/daemon/*.lo \ +&& rm -f src/daemon/collectd \ +&& rm -f src/daemon/Makefile.in \ +&& rm -f src/daemon/Makefile \ && rm -f src/liboconfig/*.o \ && rm -f src/liboconfig/*.la \ && rm -f src/liboconfig/*.lo \ diff --git a/configure.ac b/configure.ac index 90420f8d..d9509820 100644 --- a/configure.ac +++ b/configure.ac @@ -1221,6 +1221,7 @@ FILE *fh; struct mntent *me; fh = setmntent ("/etc/mtab", "r"); me = getmntent (fh); +return(me->mnt_passno); ]]] )], [c_cv_have_one_getmntent="yes"], @@ -1240,6 +1241,7 @@ me = getmntent (fh); int status; fh = fopen ("/etc/mnttab", "r"); status = getmntent (fh, &mt); + return(status); ]]] )], [c_cv_have_two_getmntent="yes"], @@ -1731,6 +1733,10 @@ then [have_curlopt_username="yes"], [have_curlopt_username="no"], [[#include ]]) + AC_CHECK_DECL(CURLOPT_TIMEOUT_MS, + [have_curlopt_timeout="yes"], + [have_curlopt_timeout="no"], + [[#include ]]) fi fi if test "x$with_libcurl" = "xyes" @@ -1744,6 +1750,11 @@ then then AC_DEFINE(HAVE_CURLOPT_USERNAME, 1, [Define if libcurl supports CURLOPT_USERNAME option.]) fi + + if test "x$have_curlopt_timeout" = "xyes" + then + AC_DEFINE(HAVE_CURLOPT_TIMEOUT_MS, 1, [Define if libcurl supports CURLOPT_TIMEOUT_MS option.]) + fi fi AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes") # }}} @@ -1782,7 +1793,6 @@ then LDFLAGS="$LDFLAGS $with_libdbi_ldflags" AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"]) - AC_CHECK_LIB(dbi, dbi_driver_open_r, [with_libdbi_r="yes"], [with_libdbi_r="no"]) CPPFLAGS="$SAVE_CPPFLAGS" LDFLAGS="$SAVE_LDFLAGS" @@ -1795,11 +1805,6 @@ then AC_SUBST(BUILD_WITH_LIBDBI_CPPFLAGS) AC_SUBST(BUILD_WITH_LIBDBI_LDFLAGS) AC_SUBST(BUILD_WITH_LIBDBI_LIBS) - - if test "x$with_libdbi_r" = "xyes" - then - AC_DEFINE(HAVE_LIBDBI_R, 1, [Define if reentrant dbi facility is present and usable.]) - fi fi AM_CONDITIONAL(BUILD_WITH_LIBDBI, test "x$with_libdbi" = "xyes") # }}} @@ -2112,6 +2117,10 @@ fi # --with-java {{{ with_java_home="$JAVA_HOME" +if test "x$with_java_home" = "x" +then + with_java_home="/usr/lib/jvm" +fi with_java_vmtype="client" with_java_cflags="" with_java_libs="" @@ -2136,7 +2145,7 @@ then if test -d "$with_java_home" then AC_MSG_CHECKING([for jni.h]) - TMPVAR=`find "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` + TMPVAR=`find -L "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` if test "x$TMPVAR" != "x" then AC_MSG_RESULT([found in $TMPVAR]) @@ -2146,7 +2155,7 @@ then fi AC_MSG_CHECKING([for jni_md.h]) - TMPVAR=`find "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` + TMPVAR=`find -L "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` if test "x$TMPVAR" != "x" then AC_MSG_RESULT([found in $TMPVAR]) @@ -2156,7 +2165,7 @@ then fi AC_MSG_CHECKING([for libjvm.so]) - TMPVAR=`find "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` + TMPVAR=`find -L "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1` if test "x$TMPVAR" != "x" then AC_MSG_RESULT([found in $TMPVAR]) @@ -2168,7 +2177,7 @@ then if test "x$JAVAC" = "x" then AC_MSG_CHECKING([for javac]) - TMPVAR=`find "$with_java_home" -name javac -type f 2>/dev/null | head -n 1` + TMPVAR=`find -L "$with_java_home" -name javac -type f 2>/dev/null | head -n 1` if test "x$TMPVAR" != "x" then JAVAC="$TMPVAR" @@ -2180,7 +2189,7 @@ then if test "x$JAR" = "x" then AC_MSG_CHECKING([for jar]) - TMPVAR=`find "$with_java_home" -name jar -type f 2>/dev/null | head -n 1` + TMPVAR=`find -L "$with_java_home" -name jar -type f 2>/dev/null | head -n 1` if test "x$TMPVAR" != "x" then JAR="$TMPVAR" @@ -3200,6 +3209,7 @@ then ]]], [[[ int val = PCAP_ERROR_IFACE_NOT_UP; + return(val); ]]] )], [c_cv_libpcap_have_pcap_error_iface_not_up="yes"], @@ -3746,10 +3756,11 @@ fi # --with-librdkafka {{{ AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Path to librdkafka.])], [ - if test "x$withval" = "xno" && test "x$withval" != "xyes" + if test "x$withval" != "xno" && test "x$withval" != "xyes" then with_librdkafka_cppflags="-I$withval/include" with_librdkafka_ldflags="-L$withval/lib" + with_librdkafka_rpath="$withval/lib" with_librdkafka="yes" else with_librdkafka="$withval" @@ -3761,6 +3772,9 @@ AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Pat SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $with_librdkafka_cppflags" +LDFLAGS="$LDFLAGS $with_librdkafka_ldflags" + if test "x$with_librdkafka" = "xyes" then AC_CHECK_HEADERS(librdkafka/rdkafka.h, [with_librdkafka="yes"], [with_librdkafka="no (librdkafka/rdkafka.h not found)"]) @@ -3770,13 +3784,18 @@ if test "x$with_librdkafka" = "xyes" then AC_CHECK_LIB(rdkafka, rd_kafka_new, [with_librdkafka="yes"], [with_librdkafka="no (Symbol 'rd_kafka_new' not found)"]) AC_CHECK_LIB(rdkafka, rd_kafka_conf_set_log_cb, [with_librdkafka_log_cb="yes"], [with_librdkafka_log_cb="no"]) - AC_CHECK_LIB(rdkafka, rd_kafka_conf_set_logger, [with_librdkafka_logger="yes"], [with_librdkafka_logger="no"]) + AC_CHECK_LIB(rdkafka, rd_kafka_set_logger, [with_librdkafka_logger="yes"], [with_librdkafka_logger="no"]) fi if test "x$with_librdkafka" = "xyes" then BUILD_WITH_LIBRDKAFKA_CPPFLAGS="$with_librdkafka_cppflags" BUILD_WITH_LIBRDKAFKA_LDFLAGS="$with_librdkafka_ldflags" - BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka" + if test "x$with_librdkafka_rpath" != "x" + then + BUILD_WITH_LIBRDKAFKA_LIBS="-Wl,-rpath,$with_librdkafka_rpath -lrdkafka" + else + BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka" + fi AC_SUBST(BUILD_WITH_LIBRDKAFKA_CPPFLAGS) AC_SUBST(BUILD_WITH_LIBRDKAFKA_LDFLAGS) AC_SUBST(BUILD_WITH_LIBRDKAFKA_LIBS) @@ -5134,6 +5153,7 @@ plugin_ascent="no" plugin_barometer="no" plugin_battery="no" plugin_bind="no" +plugin_ceph="no" plugin_cgroups="no" plugin_conntrack="no" plugin_contextswitch="no" @@ -5190,6 +5210,7 @@ then plugin_entropy="yes" plugin_fscache="yes" plugin_interface="yes" + plugin_ipc="yes" plugin_irq="yes" plugin_load="yes" plugin_lvm="yes" @@ -5235,6 +5256,7 @@ fi if test "x$ac_system" = "xAIX" then plugin_tcpconns="yes" + plugin_ipc="yes" fi # FreeBSD @@ -5266,6 +5288,7 @@ fi if test "x$with_kstat" = "xyes" then plugin_nfs="yes" + plugin_processes="yes" plugin_uptime="yes" plugin_zfs_arc="yes" fi @@ -5332,6 +5355,11 @@ then plugin_curl_xml="yes" fi +if test "x$with_libyajl" = "xyes" +then + plugin_ceph="yes" +fi + if test "x$have_processor_info" = "xyes" then plugin_cpu="yes" @@ -5504,6 +5532,7 @@ AC_PLUGIN([ascent], [$plugin_ascent], [AscentEmu player statistics]) AC_PLUGIN([barometer], [$plugin_barometer], [Barometer sensor on I2C]) AC_PLUGIN([battery], [$plugin_battery], [Battery statistics]) AC_PLUGIN([bind], [$plugin_bind], [ISC Bind nameserver statistics]) +AC_PLUGIN([ceph], [$plugin_ceph], [Ceph daemon statistics]) AC_PLUGIN([conntrack], [$plugin_conntrack], [nf_conntrack statistics]) AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics]) AC_PLUGIN([cpufreq], [$plugin_cpufreq], [CPU frequency statistics]) @@ -5527,6 +5556,7 @@ AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics]) AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin]) AC_PLUGIN([hddtemp], [yes], [Query hddtempd]) AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) +AC_PLUGIN([ipc], [$plugin_ipc], [IPC statistics]) AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics]) AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters]) AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics]) @@ -5621,6 +5651,7 @@ AC_PLUGIN([write_log], [yes], [Log output plugin]) AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin]) AC_PLUGIN([write_redis], [$with_libhiredis], [Redis output plugin]) AC_PLUGIN([write_riemann], [$have_protoc_c], [Riemann output plugin]) +AC_PLUGIN([write_sensu], [yes], [Sensu output plugin]) AC_PLUGIN([write_tsdb], [yes], [TSDB output plugin]) AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics]) AC_PLUGIN([zfs_arc], [$plugin_zfs_arc], [ZFS ARC statistics]) @@ -5811,10 +5842,11 @@ Configuration: libatasmart . . . . . $with_libatasmart libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi - libhiredis . . . . . $with_libhiredis libesmtp . . . . . . $with_libesmtp libganglia . . . . . $with_libganglia libgcrypt . . . . . . $with_libgcrypt + libhal . . . . . . . $with_libhal + libhiredis . . . . . $with_libhiredis libi2c-dev . . . . . $with_libi2c libiokit . . . . . . $with_libiokit libiptc . . . . . . . $with_libiptc @@ -5826,6 +5858,7 @@ Configuration: libmemcached . . . . $with_libmemcached libmnl . . . . . . . $with_libmnl libmodbus . . . . . . $with_libmodbus + libmongoc . . . . . . $with_libmongoc libmysql . . . . . . $with_libmysql libnetapp . . . . . . $with_libnetapp libnetsnmp . . . . . $with_libnetsnmp @@ -5833,6 +5866,7 @@ Configuration: liboconfig . . . . . $with_liboconfig libopenipmi . . . . . $with_libopenipmipthread liboping . . . . . . $with_liboping + libowcapi . . . . . . $with_libowcapi libpcap . . . . . . . $with_libpcap libperfstat . . . . . $with_perfstat libperl . . . . . . . $with_libperl @@ -5853,9 +5887,8 @@ Configuration: libxml2 . . . . . . . $with_libxml2 libxmms . . . . . . . $with_libxmms libyajl . . . . . . . $with_libyajl - libevent . . . . . . $with_libevent - protobuf-c . . . . . $have_protoc_c oracle . . . . . . . $with_oracle + protobuf-c . . . . . $have_protoc_c python . . . . . . . $with_python Features: @@ -5870,15 +5903,16 @@ Configuration: amqp . . . . . . . $enable_amqp apache . . . . . . . $enable_apache apcups . . . . . . . $enable_apcups - aquaero . . . . . . . $enable_aquaero apple_sensors . . . . $enable_apple_sensors + aquaero . . . . . . . $enable_aquaero ascent . . . . . . . $enable_ascent barometer . . . . . . $enable_barometer battery . . . . . . . $enable_battery bind . . . . . . . . $enable_bind + ceph . . . . . . . . $enable_ceph + cgroups . . . . . . . $enable_cgroups conntrack . . . . . . $enable_conntrack contextswitch . . . . $enable_contextswitch - cgroups . . . . . . . $enable_cgroups cpu . . . . . . . . . $enable_cpu cpufreq . . . . . . . $enable_cpufreq csv . . . . . . . . . $enable_csv @@ -5899,6 +5933,7 @@ Configuration: gmond . . . . . . . . $enable_gmond hddtemp . . . . . . . $enable_hddtemp interface . . . . . . $enable_interface + ipc . . . . . . . . . $enable_ipc ipmi . . . . . . . . $enable_ipmi iptables . . . . . . $enable_iptables ipvs . . . . . . . . $enable_ipvs @@ -5906,8 +5941,8 @@ Configuration: java . . . . . . . . $enable_java load . . . . . . . . $enable_load logfile . . . . . . . $enable_logfile - lpar . . . . . . . . $enable_lpar log_logstash . . . . $enable_log_logstash + lpar . . . . . . . . $enable_lpar lvm . . . . . . . . . $enable_lvm madwifi . . . . . . . $enable_madwifi match_empty_counter . $enable_match_empty_counter @@ -5961,8 +5996,8 @@ Configuration: swap . . . . . . . . $enable_swap syslog . . . . . . . $enable_syslog table . . . . . . . . $enable_table - tail . . . . . . . . $enable_tail tail_csv . . . . . . $enable_tail_csv + tail . . . . . . . . $enable_tail tape . . . . . . . . $enable_tape target_notification . $enable_target_notification target_replace . . . $enable_target_replace @@ -5992,6 +6027,7 @@ Configuration: write_mongodb . . . . $enable_write_mongodb write_redis . . . . . $enable_write_redis write_riemann . . . . $enable_write_riemann + write_sensu . . . . . $enable_write_sensu write_tsdb . . . . . $enable_write_tsdb xmms . . . . . . . . $enable_xmms zfs_arc . . . . . . . $enable_zfs_arc diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index ec034859..0950f23a 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -77,6 +77,7 @@ %define with_ascent 0%{!?_without_ascent:1} %define with_battery 0%{!?_without_battery:1} %define with_bind 0%{!?_without_bind:1} +%define with_ceph 0%{!?_without_ceph:0%{?_has_libyajl}} %define with_cgroups 0%{!?_without_cgroups:1} %define with_conntrack 0%{!?_without_conntrack:1} %define with_contextswitch 0%{!?_without_contextswitch:1} @@ -100,6 +101,7 @@ %define with_gmond 0%{!?_without_gmond:0%{?_has_recent_libganglia}} %define with_hddtemp 0%{!?_without_hddtemp:1} %define with_interface 0%{!?_without_interface:1} +%define with_ipc 0%{!?_without_ipc:1} %define with_ipmi 0%{!?_without_ipmi:1} %define with_iptables 0%{!?_without_iptables:0%{?_has_working_libiptc}} %define with_ipvs 0%{!?_without_ipvs:0%{?_has_ip_vs_h}} @@ -171,6 +173,7 @@ %define with_write_log 0%{!?_without_write_log:1} %define with_write_redis 0%{!?_without_write_redis:0%{?_has_hiredis}} %define with_write_riemann 0%{!?_without_write_riemann:1} +%define with_write_sensu 0%{!?_without_write_sensu:1} %define with_write_tsdb 0%{!?_without_write_tsdb:1} %define with_zfs_arc 0%{!?_without_zfs_arc:1} %define with_zookeeper 0%{!?_without_zookeeper:1} @@ -211,9 +214,9 @@ # plugin xmms disabled, requires xmms %define with_xmms 0%{!?_without_xmms:0} -Summary: Statistics collection daemon for filling RRD files +Summary: statistics collection and monitoring daemon Name: collectd -Version: 5.4.0 +Version: 5.4.2 Release: 1%{?dist} URL: http://collectd.org Source: http://collectd.org/files/%{name}-%{version}.tar.bz2 @@ -304,6 +307,16 @@ The BIND plugin retrieves this information that's encoded in XML and provided via HTTP and submits the values to collectd. %endif +%if %{with_ceph} +%package ceph +Summary: Ceph plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: yajl-devel +%description ceph +Ceph plugin for collectd +%endif + %if %{with_curl} %package curl Summary: Curl plugin for collectd @@ -911,6 +924,12 @@ Collectd utilities %define _with_csv --disable-csv %endif +%if %{with_ceph} +%define _with_ceph --enable-ceph +%else +%define _with_ceph --disable-ceph +%endif + %if %{with_curl} %define _with_curl --enable-curl %else @@ -932,7 +951,7 @@ Collectd utilities %if %{with_dbi} %define _with_dbi --enable-dbi %else -%define _with_dbi --disable-dbi --without-libdbi +%define _with_dbi --disable-dbi %endif %if %{with_df} @@ -1013,6 +1032,12 @@ Collectd utilities %define _with_interface --disable-interface %endif +%if %{with_ipc} +%define _with_ipc --enable-ipc +%else +%define _with_ipc --disable-ipc +%endif + %if %{with_ipmi} %define _with_ipmi --enable-ipmi %else @@ -1178,7 +1203,7 @@ Collectd utilities %if %{with_notify_email} %define _with_notify_email --enable-notify_email %else -%define _with_notify_email --disable-notify_email --without-libesmpt +%define _with_notify_email --disable-notify_email %endif %if %{with_ntpd} @@ -1232,7 +1257,7 @@ Collectd utilities %if %{with_perl} %define _with_perl --enable-perl --with-perl-bindings="INSTALLDIRS=vendor" %else -%define _with_perl --disable-perl --without-libperl +%define _with_perl --disable-perl %endif %if %{with_pf} @@ -1500,7 +1525,7 @@ Collectd utilities %if %{with_write_mongodb} %define _with_write_mongodb --enable-write_mongodb %else -%define _with_write_mongodb --disable-write_mongodb --without-libmongoc +%define _with_write_mongodb --disable-write_mongodb %endif %if %{with_write_redis} @@ -1515,6 +1540,12 @@ Collectd utilities %define _with_write_riemann --disable-write_riemann %endif +%if %{with_write_sensu} +%define _with_write_sensu --enable-write_sensu +%else +%define _with_write_sensu --disable-write_sensu +%endif + %if %{with_write_tsdb} %define _with_write_tsdb --enable-write_tsdb %else @@ -1563,6 +1594,7 @@ Collectd utilities %{?_with_barometer} \ %{?_with_battery} \ %{?_with_bind} \ + %{?_with_ceph} \ %{?_with_cgroups} \ %{?_with_conntrack} \ %{?_with_contextswitch} \ @@ -1586,6 +1618,7 @@ Collectd utilities %{?_with_gmond} \ %{?_with_hddtemp} \ %{?_with_interface} \ + %{?_with_ipc} \ %{?_with_ipmi} \ %{?_with_iptables} \ %{?_with_ipvs} \ @@ -1673,6 +1706,7 @@ Collectd utilities %{?_with_write_http} \ %{?_with_write_log} \ %{?_with_write_riemann} \ + %{?_with_write_sensu} \ %{?_with_write_tsdb} @@ -1865,6 +1899,9 @@ fi %if %{with_interface} %{_libdir}/%{name}/interface.so %endif +%if %{with_ipc} +%{_libdir}/%{name}/ipc.so +%endif %if %{with_ipvs} %{_libdir}/%{name}/ipvs.so %endif @@ -1955,7 +1992,7 @@ fi %if %{with_thermal} %{_libdir}/%{name}/thermal.so %endif -%if %{with_load} +%if %{with_threshold} %{_libdir}/%{name}/threshold.so %endif %if %{with_turbostat} @@ -1988,6 +2025,9 @@ fi %if %{with_write_log} %{_libdir}/%{name}/write_log.so %endif +%if %{with_write_sensu} +%{_libdir}/%{name}/write_sensu.so +%endif %if %{with_write_tsdb} %{_libdir}/%{name}/write_tsdb.so %endif @@ -2047,6 +2087,11 @@ fi %{_libdir}/%{name}/bind.so %endif +%if %{with_ceph} +%files ceph +%{_libdir}/%{name}/ceph.so +%endif + %if %{with_curl} %files curl %{_libdir}/%{name}/curl.so @@ -2281,7 +2326,7 @@ fi %changelog # * TODO 5.5.0-1 # - New upstream version -# - New plugins enabled by default: drbd, log_logstash, write_tsdb, smart, openldap, redis, write_redis, zookeeper, write_log, turbostat +# - New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart, openldap, redis, write_redis, zookeeper, write_log, write_sensu, ipc, turbostat # - New plugins disabled by default: barometer, write_kafka # - Enable zfs_arc, now supported on Linux # - Install disk plugin in a dedicated package, as it depends on libudev diff --git a/src/Makefile.am b/src/Makefile.am index 4848be10..8e574a33 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,6 +171,15 @@ bind_la_CFLAGS = $(AM_CFLAGS) \ bind_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS) endif +if BUILD_PLUGIN_CEPH +pkglib_LTLIBRARIES += ceph.la +ceph_la_SOURCES = ceph.c +ceph_la_CFLAGS = $(AM_CFLAGS) +ceph_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) +ceph_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) +ceph_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) +endif + if BUILD_PLUGIN_CGROUPS pkglib_LTLIBRARIES += cgroups.la cgroups_la_SOURCES = cgroups.c \ @@ -397,6 +406,13 @@ interface_la_LIBADD += -lperfstat endif endif # BUILD_PLUGIN_INTERFACE +if BUILD_PLUGIN_IPC +pkglib_LTLIBRARIES += ipc.la +ipc_la_SOURCES = ipc.c +ipc_la_CFLAGS = $(AM_CFLAGS) +ipc_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_IPTABLES pkglib_LTLIBRARIES += iptables.la iptables_la_SOURCES = iptables.c @@ -681,7 +697,7 @@ if BUILD_PLUGIN_NOTIFY_EMAIL pkglib_LTLIBRARIES += notify_email.la notify_email_la_SOURCES = notify_email.c notify_email_la_LDFLAGS = $(PLUGIN_LDFLAGS) -notify_email_la_LIBADD = -lesmtp -lssl -lcrypto -lpthread -ldl +notify_email_la_LIBADD = -lesmtp -lssl -lcrypto -lpthread endif if BUILD_PLUGIN_NTPD @@ -1178,6 +1194,7 @@ write_kafka_la_SOURCES = write_kafka.c \ utils_format_json.c utils_format_json.h \ utils_cmd_putval.c utils_cmd_putval.h \ utils_crc32.c utils_crc32.h +write_kafka_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRDKAFKA_CPPFLAGS) write_kafka_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRDKAFKA_LDFLAGS) write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS) endif @@ -1213,6 +1230,12 @@ write_riemann_la_LDFLAGS = $(PLUGIN_LDFLAGS) write_riemann_la_LIBADD = -lprotobuf-c endif +if BUILD_PLUGIN_WRITE_SENSU +pkglib_LTLIBRARIES += write_sensu.la +write_sensu_la_SOURCES = write_sensu.c +write_sensu_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_WRITE_TSDB pkglib_LTLIBRARIES += write_tsdb.la write_tsdb_la_SOURCES = write_tsdb.c @@ -1348,6 +1371,7 @@ check_PROGRAMS = test_common test_utils_avltree test_utils_heap test_utils_mount test_common_SOURCES = tests/test_common.c \ daemon/common.h daemon/common.c \ + tests/macros.h \ tests/mock/plugin.c \ tests/mock/utils_cache.c \ tests/mock/utils_time.c diff --git a/src/amqp.c b/src/amqp.c index 1764129f..97359cfd 100644 --- a/src/amqp.c +++ b/src/amqp.c @@ -80,6 +80,9 @@ struct camqp_config_s char *exchange; char *routing_key; + /* Number of seconds to wait before connection is retried */ + int connection_retry_delay; + /* publish only */ uint8_t delivery_mode; _Bool store_rates; @@ -301,6 +304,10 @@ static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */ /* type = */ amqp_cstring_bytes (conf->exchange_type), /* passive = */ 0, /* durable = */ 0, +#if defined(AMQP_VERSION) && AMQP_VERSION >= 0x00060000 + /* auto delete = */ 0, + /* internal = */ 0, +#endif /* arguments = */ argument_table); if ((ed_ret == NULL) && camqp_is_error (conf)) { @@ -405,6 +412,8 @@ static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */ static int camqp_connect (camqp_config_t *conf) /* {{{ */ { + static time_t last_connect_time = 0; + amqp_rpc_reply_t reply; int status; #ifdef HAVE_AMQP_TCP_SOCKET @@ -416,6 +425,19 @@ static int camqp_connect (camqp_config_t *conf) /* {{{ */ if (conf->connection != NULL) return (0); + time_t now = time(NULL); + if (now < (last_connect_time + conf->connection_retry_delay)) + { + DEBUG("amqp plugin: skipping connection retry, " + "ConnectionRetryDelay: %d", conf->connection_retry_delay); + return(1); + } + else + { + DEBUG ("amqp plugin: retrying connection"); + last_connect_time = now; + } + conf->connection = amqp_new_connection (); if (conf->connection == NULL) { @@ -922,6 +944,8 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */ conf->password = NULL; conf->exchange = NULL; conf->routing_key = NULL; + conf->connection_retry_delay = 0; + /* publish only */ conf->delivery_mode = CAMQP_DM_VOLATILE; conf->store_rates = 0; @@ -1017,6 +1041,8 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */ conf->escape_char = tmp_buff[0]; sfree (tmp_buff); } + else if (strcasecmp ("ConnectionRetryDelay", child->key) == 0) + status = cf_util_get_int (child, &conf->connection_retry_delay); else WARNING ("amqp plugin: Ignoring unknown " "configuration option \"%s\".", child->key); diff --git a/src/apache.c b/src/apache.c index 75ef3e1b..0c6318e3 100644 --- a/src/apache.c +++ b/src/apache.c @@ -48,11 +48,13 @@ struct apache_s _Bool verify_peer; _Bool verify_host; char *cacert; + char *ssl_ciphers; char *server; /* user specific server type */ char *apache_buffer; char apache_curl_error[CURL_ERROR_SIZE]; size_t apache_buffer_size; size_t apache_buffer_fill; + int timeout; CURL *curl; }; /* apache_s */ @@ -72,6 +74,7 @@ static void apache_free (apache_t *st) sfree (st->user); sfree (st->pass); sfree (st->cacert); + sfree (st->ssl_ciphers); sfree (st->server); sfree (st->apache_buffer); if (st->curl) { @@ -179,6 +182,8 @@ static int config_add (oconfig_item_t *ci) } memset (st, 0, sizeof (*st)); + st->timeout = -1; + status = cf_util_get_string (ci, &st->name); if (status != 0) { @@ -205,8 +210,12 @@ static int config_add (oconfig_item_t *ci) status = cf_util_get_boolean (child, &st->verify_host); else if (strcasecmp ("CACert", child->key) == 0) status = cf_util_get_string (child, &st->cacert); + else if (strcasecmp ("SSLCiphers", child->key) == 0) + status = cf_util_get_string (child, &st->ssl_ciphers); else if (strcasecmp ("Server", child->key) == 0) status = cf_util_get_string (child, &st->server); + else if (strcasecmp ("Timeout", child->key) == 0) + status = cf_util_get_int (child, &st->timeout); else { WARNING ("apache plugin: Option `%s' not allowed here.", @@ -283,8 +292,6 @@ static int config (oconfig_item_t *ci) /* initialize curl for each host */ static int init_host (apache_t *st) /* {{{ */ { - static char credentials[1024]; - assert (st->url != NULL); /* (Assured by `config_add') */ @@ -334,6 +341,12 @@ static int init_host (apache_t *st) /* {{{ */ if (st->user != NULL) { +#ifdef HAVE_CURLOPT_USERNAME + curl_easy_setopt (st->curl, CURLOPT_USERNAME, st->user); + curl_easy_setopt (st->curl, CURLOPT_PASSWORD, + (st->pass == NULL) ? "" : st->pass); +#else + static char credentials[1024]; int status; status = ssnprintf (credentials, sizeof (credentials), "%s:%s", @@ -349,6 +362,7 @@ static int init_host (apache_t *st) /* {{{ */ } curl_easy_setopt (st->curl, CURLOPT_USERPWD, credentials); +#endif } curl_easy_setopt (st->curl, CURLOPT_URL, st->url); @@ -361,6 +375,16 @@ static int init_host (apache_t *st) /* {{{ */ st->verify_host ? 2L : 0L); if (st->cacert != NULL) curl_easy_setopt (st->curl, CURLOPT_CAINFO, st->cacert); + if (st->ssl_ciphers != NULL) + curl_easy_setopt (st->curl, CURLOPT_SSL_CIPHER_LIST,st->ssl_ciphers); + +#ifdef HAVE_CURLOPT_TIMEOUT_MS + if (st->timeout >= 0) + curl_easy_setopt (st->curl, CURLOPT_TIMEOUT_MS, (long) st->timeout); + else + curl_easy_setopt (st->curl, CURLOPT_TIMEOUT_MS, + CDTIME_T_TO_MS(plugin_get_interval())); +#endif return (0); } /* }}} int init_host */ diff --git a/src/ascent.c b/src/ascent.c index ca0fac7f..11175af5 100644 --- a/src/ascent.c +++ b/src/ascent.c @@ -102,6 +102,7 @@ static char *pass = NULL; static char *verify_peer = NULL; static char *verify_host = NULL; static char *cacert = NULL; +static char *timeout = NULL; static CURL *curl = NULL; @@ -117,7 +118,8 @@ static const char *config_keys[] = "Password", "VerifyPeer", "VerifyHost", - "CACert" + "CACert", + "Timeout", }; static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); @@ -518,14 +520,14 @@ static int ascent_config (const char *key, const char *value) /* {{{ */ return (config_set (&verify_host, value)); else if (strcasecmp (key, "CACert") == 0) return (config_set (&cacert, value)); + else if (strcasecmp (key, "Timeout") == 0) + return (config_set (&timeout, value)); else return (-1); } /* }}} int ascent_config */ static int ascent_init (void) /* {{{ */ { - static char credentials[1024]; - if (url == NULL) { WARNING ("ascent plugin: ascent_init: No URL configured, " @@ -551,6 +553,11 @@ static int ascent_init (void) /* {{{ */ if (user != NULL) { +#ifdef HAVE_CURLOPT_USERNAME + curl_easy_setopt (curl, CURLOPT_USERNAME, user); + curl_easy_setopt (curl, CURLOPT_PASSWORD, (pass == NULL) ? "" : pass); +#else + static char credentials[1024]; int status; status = ssnprintf (credentials, sizeof (credentials), "%s:%s", @@ -563,6 +570,7 @@ static int ascent_init (void) /* {{{ */ } curl_easy_setopt (curl, CURLOPT_USERPWD, credentials); +#endif } curl_easy_setopt (curl, CURLOPT_URL, url); @@ -582,6 +590,14 @@ static int ascent_init (void) /* {{{ */ if (cacert != NULL) curl_easy_setopt (curl, CURLOPT_CAINFO, cacert); +#ifdef HAVE_CURLOPT_TIMEOUT_MS + if (timeout != NULL) + curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, atol(timeout)); + else + curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, + CDTIME_T_TO_MS(plugin_get_interval())); +#endif + return (0); } /* }}} int ascent_init */ diff --git a/src/barometer.c b/src/barometer.c index 95b05f4e..2bfd51e0 100644 --- a/src/barometer.c +++ b/src/barometer.c @@ -111,6 +111,41 @@ #define MPL3115_NUM_CONV_VALS 5 +/* ------------ BMP085 defines ------------ */ +/* I2C address of the BMP085 sensor */ +#define BMP085_I2C_ADDRESS 0x77 + +/* register addresses */ +#define BMP085_ADDR_ID_REG 0xD0 +#define BMP085_ADDR_VERSION 0xD1 + +#define BMP085_ADDR_CONV 0xF6 + +#define BMP085_ADDR_CTRL_REG 0xF4 +#define BMP085_ADDR_COEFFS 0xAA + +/* register sizes */ +#define BMP085_NUM_COEFFS 22 + +/* commands, values */ +#define BMP085_CHIP_ID 0x55 + +#define BMP085_CMD_CONVERT_TEMP 0x2E + +#define BMP085_CMD_CONVERT_PRESS_0 0x34 +#define BMP085_CMD_CONVERT_PRESS_1 0x74 +#define BMP085_CMD_CONVERT_PRESS_2 0xB4 +#define BMP085_CMD_CONVERT_PRESS_3 0xF4 + +/* in us */ +#define BMP085_TIME_CNV_TEMP 4500 + +#define BMP085_TIME_CNV_PRESS_0 4500 +#define BMP085_TIME_CNV_PRESS_1 7500 +#define BMP085_TIME_CNV_PRESS_2 13500 +#define BMP085_TIME_CNV_PRESS_3 25500 + + /* ------------ Normalization ------------ */ /* Mean sea level pressure normalization methods */ #define MSLP_NONE 0 @@ -120,7 +155,17 @@ /** Temperature reference history depth for averaging. See #get_reference_temperature */ #define REF_TEMP_AVG_NUM 5 + /* ------------------------------------------ */ + +/** Supported sensor types */ +enum Sensor_type { + Sensor_none = 0, + Sensor_MPL115, + Sensor_MPL3115, + Sensor_BMP085 +}; + static const char *config_keys[] = { "Device", @@ -146,9 +191,15 @@ static int config_normalize = 0; /**< normalization method */ static _Bool configured = 0; /**< the whole plugin config status */ static int i2c_bus_fd = -1; /**< I2C bus device FD */ - -static _Bool is_MPL3115 = 0; /**< is this MPL3115? */ -static __s32 oversample_MPL3115 = 0; /**< MPL3115 CTRL1 oversample setting */ + +static enum Sensor_type sensor_type = Sensor_none; /**< detected/used sensor type */ + +static __s32 mpl3115_oversample = 0; /**< MPL3115 CTRL1 oversample setting */ + +// BMP085 configuration +static unsigned bmp085_oversampling; /**< BMP085 oversampling (0-3) */ +static unsigned long bmp085_timeCnvPress; /**< BMP085 conversion time for pressure in us */ +static __u8 bmp085_cmdCnvPress; /**< BMP085 pressure conversion command */ /* MPL115 conversion coefficients */ @@ -159,6 +210,21 @@ static double mpl115_coeffC12; static double mpl115_coeffC11; static double mpl115_coeffC22; +/* BMP085 conversion coefficients */ +static short bmp085_AC1; +static short bmp085_AC2; +static short bmp085_AC3; +static unsigned short bmp085_AC4; +static unsigned short bmp085_AC5; +static unsigned short bmp085_AC6; +static short bmp085_B1; +static short bmp085_B2; +static short bmp085_MB; +static short bmp085_MC; +static short bmp085_MD; + + + /* ------------------------ averaging ring buffer ------------------------ */ /* Used only for MPL115. MPL3115 supports real oversampling in the device so */ /* no need for any postprocessing. */ @@ -484,9 +550,45 @@ static int get_reference_temperature(double * result) return 0; } + /* ------------------------ MPL115 access ------------------------ */ /** + * Detect presence of a MPL115 pressure sensor. + * + * Unfortunately there seems to be no ID register so we just try to read first + * conversion coefficient from device at MPL115 address and hope it is really + * MPL115. We should use this check as the last resort (which would be the typical + * case anyway since MPL115 is the least accurate sensor). + * As a sideeffect will leave set I2C slave address. + * + * @return 1 if MPL115, 0 otherwise + */ +static int MPL115_detect(void) +{ + __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) + { + ERROR("barometer: MPL115_detect problem setting i2c slave address to 0x%02X: %s", + MPL115_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + + res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL115_ADDR_COEFFS); + if(res >= 0) + { + DEBUG ("barometer: MPL115_detect - positive detection"); + return 1; + } + + DEBUG ("barometer: MPL115_detect - negative detection"); + return 0; +} + +/** * Read the MPL115 sensor conversion coefficients. * * These are (device specific) constants so we can read them just once. @@ -510,7 +612,7 @@ static int MPL115_read_coeffs(void) mpl115_coeffs); if (res < 0) { - ERROR ("barometer: read_mpl115_coeffs - problem reading data: %s", + ERROR ("barometer: MPL115_read_coeffs - problem reading data: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return -1; } @@ -567,7 +669,7 @@ static int MPL115_read_coeffs(void) mpl115_coeffC22 /= 32.0; //16-11=5 mpl115_coeffC22 /= 33554432.0; /* 10+15=25 fract */ - DEBUG("barometer: read_mpl115_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf", + DEBUG("barometer: MPL115_read_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf", mpl115_coeffA0, mpl115_coeffB1, mpl115_coeffB2, @@ -578,7 +680,7 @@ static int MPL115_read_coeffs(void) } -/* +/** * Convert raw adc values to real data using the sensor coefficients. * * @param adc_pressure adc pressure value to be converted @@ -598,7 +700,7 @@ static void MPL115_convert_adc_to_real(double adc_pressure, *pressure = ((1150.0-500.0) * Pcomp / 1023.0) + 500.0; *temperature = (472.0 - adc_temp) / 5.35 + 25.0; - DEBUG ("barometer: convert_adc_to_real - got %lf hPa, %lf C", + DEBUG ("barometer: MPL115_convert_adc_to_real - got %lf hPa, %lf C", *pressure, *temperature); } @@ -709,12 +811,23 @@ static int MPL115_read_averaged(double * pressure, double * temperature) /** * Detect presence of a MPL3115 pressure sensor by checking register "WHO AM I" + * + * As a sideeffect will leave set I2C slave address. * * @return 1 if MPL3115, 0 otherwise */ static int MPL3115_detect(void) { __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL3115_I2C_ADDRESS) < 0) + { + ERROR("barometer: MPL3115_detect problem setting i2c slave address to 0x%02X: %s", + MPL3115_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_WHO_AM_I); if(res == MPL3115_WHO_AM_I_RESP) @@ -739,45 +852,45 @@ static void MPL3115_adjust_oversampling(void) if(config_oversample > 100) { new_val = 128; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_128; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_128; } else if(config_oversample > 48) { new_val = 64; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_64; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_64; } else if(config_oversample > 24) { new_val = 32; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_32; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_32; } else if(config_oversample > 12) { new_val = 16; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_16; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_16; } else if(config_oversample > 6) { new_val = 8; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_8; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_8; } else if(config_oversample > 3) { new_val = 4; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_4; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_4; } else if(config_oversample > 1) { new_val = 2; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_2; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_2; } else { new_val = 1; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_1; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_1; } - DEBUG("barometer: correcting oversampling for MPL3115 from %d to %d", + DEBUG("barometer: MPL3115_adjust_oversampling - correcting oversampling from %d to %d", config_oversample, new_val); config_oversample = new_val; @@ -859,7 +972,7 @@ static int MPL3115_read(double * pressure, double * temperature) tmp_value = (data[0] << 16) | (data[1] << 8) | data[2]; *pressure = ((double) tmp_value) / 4.0 / 16.0 / 100.0; - DEBUG ("barometer: MPL3115_read, absolute pressure = %lf hPa", *pressure); + DEBUG ("barometer: MPL3115_read - absolute pressure = %lf hPa", *pressure); if(data[3] > 0x7F) { @@ -873,7 +986,7 @@ static int MPL3115_read(double * pressure, double * temperature) } *temperature += (double)(data[4]) / 256.0; - DEBUG ("barometer: MPL3115_read, temperature = %lf C", *temperature); + DEBUG ("barometer: MPL3115_read - temperature = %lf C", *temperature); return 0; } @@ -938,7 +1051,7 @@ static int MPL3115_init_sensor(void) /* Set to barometer with an OSR */ res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1, - oversample_MPL3115); + mpl3115_oversample); if (res < 0) { ERROR ("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s", @@ -949,6 +1062,327 @@ static int MPL3115_init_sensor(void) return 0; } +/* ------------------------ BMP085 access ------------------------ */ + +/** + * Detect presence of a BMP085 pressure sensor by checking its ID register + * + * As a sideeffect will leave set I2C slave address. + * + * @return 1 if BMP085, 0 otherwise + */ +static int BMP085_detect(void) +{ + __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, BMP085_I2C_ADDRESS) < 0) + { + ERROR("barometer: BMP085_detect - problem setting i2c slave address to 0x%02X: %s", + BMP085_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + + res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_ID_REG); + if(res == BMP085_CHIP_ID) + { + DEBUG ("barometer: BMP085_detect - positive detection"); + + /* get version */ + res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_VERSION ); + if (res < 0) + { + ERROR("barometer: BMP085_detect - problem checking chip version: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + DEBUG ("barometer: BMP085_detect - chip version ML:0x%02X AL:0x%02X", + res & 0x0f, + (res & 0xf0) >> 4); + return 1; + } + + DEBUG ("barometer: BMP085_detect - negative detection"); + return 0; +} + + +/** + * Adjusts oversampling settings to values supported by BMP085 + * + * BMP085 supports only 1,2,4 or 8 samples. + */ +static void BMP085_adjust_oversampling(void) +{ + int new_val = 0; + + if( config_oversample > 6 ) /* 8 */ + { + new_val = 8; + bmp085_oversampling = 3; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_3; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_3; + } + else if( config_oversample > 3 ) /* 4 */ + { + new_val = 4; + bmp085_oversampling = 2; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_2; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_2; + } + else if( config_oversample > 1 ) /* 2 */ + { + new_val = 2; + bmp085_oversampling = 1; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_1; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_1; + } + else /* 1 */ + { + new_val = 1; + bmp085_oversampling = 0; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_0; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_0; + } + + DEBUG("barometer: BMP085_adjust_oversampling - correcting oversampling from %d to %d", + config_oversample, + new_val); + config_oversample = new_val; +} + + +/** + * Read the BMP085 sensor conversion coefficients. + * + * These are (device specific) constants so we can read them just once. + * + * @return Zero when successful + */ +static int BMP085_read_coeffs(void) +{ + __s32 res; + __u8 coeffs[BMP085_NUM_COEFFS]; + char errbuf[1024]; + + res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, + BMP085_ADDR_COEFFS, + BMP085_NUM_COEFFS, + coeffs); + if (res < 0) + { + ERROR ("barometer: BMP085_read_coeffs - problem reading data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + bmp085_AC1 = ((int16_t) coeffs[0] <<8) | (int16_t) coeffs[1]; + bmp085_AC2 = ((int16_t) coeffs[2] <<8) | (int16_t) coeffs[3]; + bmp085_AC3 = ((int16_t) coeffs[4] <<8) | (int16_t) coeffs[5]; + bmp085_AC4 = ((uint16_t) coeffs[6] <<8) | (uint16_t) coeffs[7]; + bmp085_AC5 = ((uint16_t) coeffs[8] <<8) | (uint16_t) coeffs[9]; + bmp085_AC6 = ((uint16_t) coeffs[10] <<8) | (uint16_t) coeffs[11]; + bmp085_B1 = ((int16_t) coeffs[12] <<8) | (int16_t) coeffs[13]; + bmp085_B2 = ((int16_t) coeffs[14] <<8) | (int16_t) coeffs[15]; + bmp085_MB = ((int16_t) coeffs[16] <<8) | (int16_t) coeffs[17]; + bmp085_MC = ((int16_t) coeffs[18] <<8) | (int16_t) coeffs[19]; + bmp085_MD = ((int16_t) coeffs[20] <<8) | (int16_t) coeffs[21]; + + DEBUG("barometer: BMP085_read_coeffs - AC1=%d, AC2=%d, AC3=%d, AC4=%u,"\ + " AC5=%u, AC6=%u, B1=%d, B2=%d, MB=%d, MC=%d, MD=%d", + bmp085_AC1, + bmp085_AC2, + bmp085_AC3, + bmp085_AC4, + bmp085_AC5, + bmp085_AC6, + bmp085_B1, + bmp085_B2, + bmp085_MB, + bmp085_MC, + bmp085_MD); + + return 0; +} + + +/** + * Convert raw BMP085 adc values to real data using the sensor coefficients. + * + * @param adc_pressure adc pressure value to be converted + * @param adc_temp adc temperature value to be converted + * @param pressure computed real pressure + * @param temperature computed real temperature + */ +static void BMP085_convert_adc_to_real(long adc_pressure, + long adc_temperature, + double * pressure, + double * temperature) + +{ + long X1, X2, X3; + long B3, B5, B6; + unsigned long B4, B7; + + long T; + long P; + + + /* calculate real temperature */ + X1 = ( (adc_temperature - bmp085_AC6) * bmp085_AC5) >> 15; + X2 = (bmp085_MC << 11) / (X1 + bmp085_MD); + + /* B5, T */ + B5 = X1 + X2; + T = (B5 + 8) >> 4; + *temperature = (double)T * 0.1; + + /* calculate real pressure */ + /* in general X1, X2, X3 are recycled while values of B3, B4, B5, B6 are kept */ + + /* B6, B3 */ + B6 = B5 - 4000; + X1 = ((bmp085_B2 * ((B6 * B6)>>12)) >> 11 ); + X2 = (((long)bmp085_AC2 * B6) >> 11); + X3 = X1 + X2; + B3 = (((((long)bmp085_AC1 * 4) + X3) << bmp085_oversampling) + 2) >> 2; + + /* B4 */ + X1 = (((long)bmp085_AC3*B6) >> 13); + X2 = (bmp085_B1*((B6*B6) >> 12) ) >> 16; + X3 = ((X1 + X2) + 2 ) >> 2; + B4 = ((long)bmp085_AC4* (unsigned long)(X3 + 32768)) >> 15; + + /* B7, P */ + B7 = (unsigned long)(adc_pressure - B3)*(50000>>bmp085_oversampling); + if( B7 < 0x80000000 ) + { + P = (B7 << 1) / B4; + } + else + { + P = (B7/B4) << 1; + } + X1 = (P >> 8) * (P >> 8); + X1 = (X1 * 3038) >> 16; + X2 = ((-7357) * P) >> 16; + P = P + ( ( X1 + X2 + 3791 ) >> 4); + + *pressure = P / 100.0; // in [hPa] + DEBUG ("barometer: BMP085_convert_adc_to_real - got %lf hPa, %lf C", + *pressure, + *temperature); +} + + +/** + * Read compensated sensor measurements + * + * @param pressure averaged measured pressure + * @param temperature averaged measured temperature + * + * @return Zero when successful + */ +static int BMP085_read(double * pressure, double * temperature) +{ + __s32 res; + __u8 measBuff[3]; + + long adc_pressure; + long adc_temperature; + + char errbuf[1024]; + + /* start conversion of temperature */ + res = i2c_smbus_write_byte_data( i2c_bus_fd, + BMP085_ADDR_CTRL_REG, + BMP085_CMD_CONVERT_TEMP ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem requesting temperature conversion: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + usleep(BMP085_TIME_CNV_TEMP); /* wait for the conversion */ + + res = i2c_smbus_read_i2c_block_data( i2c_bus_fd, + BMP085_ADDR_CONV, + 2, + measBuff); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem reading temperature data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + adc_temperature = ( (unsigned short)measBuff[0] << 8 ) + measBuff[1]; + + + /* get presure */ + res = i2c_smbus_write_byte_data( i2c_bus_fd, + BMP085_ADDR_CTRL_REG, + bmp085_cmdCnvPress ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem requesting pressure conversion: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + usleep(bmp085_timeCnvPress); /* wait for the conversion */ + + res = i2c_smbus_read_i2c_block_data( i2c_bus_fd, + BMP085_ADDR_CONV, + 3, + measBuff ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem reading pressure data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + adc_pressure = (long)((((ulong)measBuff[0]<<16) | ((ulong)measBuff[1]<<8) | (ulong)measBuff[2] ) >> (8 - bmp085_oversampling)); + + + DEBUG ("barometer: BMP085_read - raw pressure ADC value = %ld, " \ + "raw temperature ADC value = %ld", + adc_pressure, + adc_temperature); + + BMP085_convert_adc_to_real(adc_pressure, adc_temperature, pressure, temperature); + + return 0; +} + + + +/* ------------------------ Sensor detection ------------------------ */ +/** + * Detect presence of a supported sensor. + * + * As a sideeffect will leave set I2C slave address. + * The detection is done in the order BMP085, MPL3115, MPL115 and stops after + * first sensor beeing found. + * + * @return detected sensor type + */ +enum Sensor_type Detect_sensor_type(void) +{ + if(BMP085_detect()) + return Sensor_BMP085; + + else if(MPL3115_detect()) + return Sensor_MPL3115; + + else if(MPL115_detect()) + return Sensor_MPL115; + + return Sensor_none; +} /* ------------------------ Common functionality ------------------------ */ @@ -975,10 +1409,6 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) double temp = 0.0; int result = 0; - DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf, method = %d", - abs_pressure, - config_normalize); - if (config_normalize >= MSLP_DEU_WETT) { result = get_reference_temperature(&temp); @@ -996,7 +1426,7 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) case MSLP_INTERNATIONAL: mean = abs_pressure / \ - pow(1.0 - 0.0065*config_altitude/288.15, 0.0065*0.0289644/(8.31447*0.0065)); + pow(1.0 - 0.0065*config_altitude/288.15, 9.80665*0.0289644/(8.31447*0.0065)); break; case MSLP_DEU_WETT: @@ -1019,6 +1449,11 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) break; } + DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf hPa, method = %d, meanPressure = %lf hPa", + abs_pressure, + config_normalize, + mean); + return mean; } @@ -1047,7 +1482,7 @@ static int collectd_barometer_config (const char *key, const char *value) if (oversampling_tmp < 1 || oversampling_tmp > 1024) { WARNING ("barometer: collectd_barometer_config: invalid oversampling: %d." \ - " Allowed values are 1 to 1024 (for MPL115) or 128 (for MPL3115).", + " Allowed values are 1 to 1024 (for MPL115) or 1 to 128 (for MPL3115) or 1 to 8 (for BMP085).", oversampling_tmp); return 1; } @@ -1103,7 +1538,7 @@ static int collectd_barometer_shutdown(void) { DEBUG ("barometer: collectd_barometer_shutdown"); - if(!is_MPL3115) + if(sensor_type == Sensor_MPL115) { averaging_delete (&pressure_averaging); averaging_delete (&temperature_averaging); @@ -1268,6 +1703,69 @@ static int MPL3115_collectd_barometer_read (void) /** + * Plugin read callback for BMP085. + * + * Dispatching will create values: + * - /barometer-bmp085/pressure-normalized + * - /barometer-bmp085/pressure-absolute + * - /barometer-bmp085/temperature + * + * @return Zero when successful. + */ +static int BMP085_collectd_barometer_read (void) +{ + int result = 0; + + double pressure = 0.0; + double temperature = 0.0; + double norm_pressure = 0.0; + + value_list_t vl = VALUE_LIST_INIT; + value_t values[1]; + + DEBUG("barometer: BMP085_collectd_barometer_read"); + + if (!configured) + { + return -1; + } + + result = BMP085_read(&pressure, &temperature); + if(result) + return result; + + norm_pressure = abs_to_mean_sea_level_pressure(pressure); + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, "bmp085", sizeof (vl.plugin_instance)); + + vl.values_len = 1; + vl.values = values; + + /* dispatch normalized air pressure */ + sstrncpy (vl.type, "pressure", sizeof (vl.type)); + sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance)); + values[0].gauge = norm_pressure; + plugin_dispatch_values (&vl); + + /* dispatch absolute air pressure */ + sstrncpy (vl.type, "pressure", sizeof (vl.type)); + sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance)); + values[0].gauge = pressure; + plugin_dispatch_values (&vl); + + /* dispatch sensor temperature */ + sstrncpy (vl.type, "temperature", sizeof (vl.type)); + sstrncpy (vl.type_instance, "", sizeof (vl.type_instance)); + values[0].gauge = temperature; + plugin_dispatch_values (&vl); + + return 0; +} + + +/** * Initialization callback * * Check config, initialize I2C bus access, conversion coefficients and averaging @@ -1313,28 +1811,26 @@ static int collectd_barometer_init (void) return -1; } - if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) - { - ERROR("barometer: collectd_barometer_init problem setting i2c slave address to 0x%02X: %s", - MPL115_I2C_ADDRESS, - sstrerror (errno, errbuf, sizeof (errbuf))); - return -1; - } - - /* detect sensor type - MPL115 or MPL3115 */ - is_MPL3115 = MPL3115_detect(); + /* detect sensor type - this will also set slave address */ + sensor_type = Detect_sensor_type(); /* init correct sensor type */ - if(is_MPL3115) /* MPL3115 */ + switch(sensor_type) + { +/* MPL3115 */ + case Sensor_MPL3115: { MPL3115_adjust_oversampling(); - + if(MPL3115_init_sensor()) return -1; - + plugin_register_read ("barometer", MPL3115_collectd_barometer_read); } - else /* MPL115 */ + break; + +/* MPL115 */ + case Sensor_MPL115: { if (averaging_create (&pressure_averaging, config_oversample)) { @@ -1350,9 +1846,29 @@ static int collectd_barometer_init (void) if (MPL115_read_coeffs() < 0) return -1; - + plugin_register_read ("barometer", MPL115_collectd_barometer_read); } + break; + +/* BMP085 */ + case Sensor_BMP085: + { + BMP085_adjust_oversampling(); + + if (BMP085_read_coeffs() < 0) + return -1; + + plugin_register_read ("barometer", BMP085_collectd_barometer_read); + } + break; + +/* anything else -> error */ + default: + ERROR("barometer: collectd_barometer_init - no supported sensor found"); + return -1; + } + configured = 1; return 0; diff --git a/src/battery.c b/src/battery.c index 185442c1..9b060dd9 100644 --- a/src/battery.c +++ b/src/battery.c @@ -416,10 +416,13 @@ static int sysfs_file_to_buffer(char const *dir, /* {{{ */ if (fgets (buffer, buffer_size, fp) == NULL) { - char errbuf[1024]; status = errno; - WARNING ("battery plugin: fgets failed: %s", - sstrerror (status, errbuf, sizeof (errbuf))); + if (status != ENODEV) + { + char errbuf[1024]; + WARNING ("battery plugin: fgets (%s) failed: %s", filename, + sstrerror (status, errbuf, sizeof (errbuf))); + } fclose (fp); return status; } @@ -513,13 +516,15 @@ static int read_sysfs_callback (char const *dir, /* {{{ */ v *= -1.0; battery_submit (plugin_instance, "power", v * SYSFS_FACTOR); } + if (sysfs_file_to_gauge (dir, power_supply, "current_now", &v) == 0) + { + if (discharging) + v *= -1.0; + battery_submit (plugin_instance, "current", v * SYSFS_FACTOR); + } if (sysfs_file_to_gauge (dir, power_supply, "voltage_now", &v) == 0) battery_submit (plugin_instance, "voltage", v * SYSFS_FACTOR); -#if 0 - if (sysfs_file_to_gauge (dir, power_supply, "voltage_min_design", &v) == 0) - battery_submit (plugin_instance, "voltage", v * SYSFS_FACTOR); -#endif return (0); } /* }}} int read_sysfs_callback */ diff --git a/src/bind.c b/src/bind.c index 59eb2491..2ad50f10 100644 --- a/src/bind.c +++ b/src/bind.c @@ -109,6 +109,7 @@ static int global_server_stats = 1; static int global_zone_maint_stats = 1; static int global_resolver_stats = 0; static int global_memory_stats = 1; +static int timeout = -1; static cb_view_t *views = NULL; static size_t views_num = 0; @@ -266,7 +267,7 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */ if (type_instance) { sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - replace_special (vl.plugin_instance, sizeof (vl.plugin_instance)); + replace_special (vl.type_instance, sizeof (vl.type_instance)); } plugin_dispatch_values(&vl); } /* }}} void submit */ @@ -369,9 +370,11 @@ static int bind_xml_read_derive (xmlDoc *doc, xmlNode *node, /* {{{ */ { ERROR ("bind plugin: Parsing string \"%s\" to derive value failed.", str_ptr); + xmlFree(str_ptr); return (-1); } + xmlFree(str_ptr); *ret_value = value.derive; return (0); } /* }}} int bind_xml_read_derive */ @@ -714,25 +717,40 @@ static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */ int i; size_t j; - path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx); - if (path_obj == NULL) + if (version >= 3) { - ERROR ("bind plugin: xmlXPathEvalExpression failed."); - return (-1); + char *n = (char *) xmlGetProp (node, BAD_CAST "name"); + char *c = (char *) xmlGetProp (node, BAD_CAST "rdataclass"); + if (n && c) + { + zone_name = (char *) xmlMalloc(strlen(n) + strlen(c) + 2); + snprintf(zone_name, strlen(n) + strlen(c) + 2, "%s/%s", n, c); + } + xmlFree(n); + xmlFree(c); } - - for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++) + else { - zone_name = (char *) xmlNodeListGetString (doc, - path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1); - if (zone_name != NULL) - break; + path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx); + if (path_obj == NULL) + { + ERROR ("bind plugin: xmlXPathEvalExpression failed."); + return (-1); + } + + for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++) + { + zone_name = (char *) xmlNodeListGetString (doc, + path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1); + if (zone_name != NULL) + break; + } + xmlXPathFreeObject (path_obj); } if (zone_name == NULL) { ERROR ("bind plugin: Could not determine zone name."); - xmlXPathFreeObject (path_obj); return (-1); } @@ -746,10 +764,7 @@ static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */ zone_name = NULL; if (j >= views->zones_num) - { - xmlXPathFreeObject (path_obj); return (0); - } zone_name = view->zones[j]; @@ -768,14 +783,31 @@ static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */ ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-zone-%s", view->name, zone_name); - bind_parse_generic_value_list (/* xpath = */ "counters", + if (version == 3) + { + list_info_ptr_t list_info = + { + plugin_instance, + /* type = */ "dns_qtype" + }; + bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='rcode']", /* callback = */ bind_xml_table_callback, /* user_data = */ &table_ptr, doc, path_ctx, current_time, DS_TYPE_COUNTER); + bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='qtype']", + /* callback = */ bind_xml_list_callback, + /* user_data = */ &list_info, + doc, path_ctx, current_time, DS_TYPE_COUNTER); + } + else + { + bind_parse_generic_value_list (/* xpath = */ "counters", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, path_ctx, current_time, DS_TYPE_COUNTER); + } } /* }}} */ - xmlXPathFreeObject (path_obj); - return (0); } /* }}} int bind_xml_stats_handle_zone */ @@ -968,8 +1000,7 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */ doc, path_ctx, current_time, DS_TYPE_GAUGE); } /* }}} */ - // v3 does not provide per-zone stats any more - if (version < 3 && view->zones_num > 0) + if (view->zones_num > 0) bind_xml_stats_search_zones (version, doc, path_ctx, node, view, current_time); @@ -1695,6 +1726,8 @@ static int bind_config (oconfig_item_t *ci) /* {{{ */ bind_config_add_view (child); else if (strcasecmp ("ParseTime", child->key) == 0) cf_util_get_boolean (child, &config_parse_time); + else if (strcasecmp ("Timeout", child->key) == 0) + cf_util_get_int (child, &timeout); else { WARNING ("bind plugin: Unknown configuration option " @@ -1724,6 +1757,11 @@ static int bind_init (void) /* {{{ */ curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL); curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L); +#ifdef HAVE_CURLOPT_TIMEOUT_MS + curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, (timeout >= 0) ? + (long) timeout : CDTIME_T_TO_MS(plugin_get_interval())); +#endif + return (0); } /* }}} int bind_init */ diff --git a/src/ceph.c b/src/ceph.c new file mode 100644 index 00000000..56e349c3 --- /dev/null +++ b/src/ceph.c @@ -0,0 +1,1580 @@ +/** + * collectd - src/ceph.c + * Copyright (C) 2011 New Dream Network + * + * 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: + * Colin McCabe + * Dennis Zou + * Dan Ryder + **/ + +#define _DEFAULT_SOURCE +#define _BSD_SOURCE + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include +#include +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RETRY_AVGCOUNT -1 + +#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) +# define HAVE_YAJL_V2 1 +#endif + +#define RETRY_ON_EINTR(ret, expr) \ + while(1) { \ + ret = expr; \ + if(ret >= 0) \ + break; \ + ret = -errno; \ + if(ret != -EINTR) \ + break; \ + } + +/** Timeout interval in seconds */ +#define CEPH_TIMEOUT_INTERVAL 1 + +/** Maximum path length for a UNIX domain socket on this system */ +#define UNIX_DOMAIN_SOCK_PATH_MAX (sizeof(((struct sockaddr_un*)0)->sun_path)) + +/** Yajl callback returns */ +#define CEPH_CB_CONTINUE 1 +#define CEPH_CB_ABORT 0 + +#if HAVE_YAJL_V2 +typedef size_t yajl_len_t; +#else +typedef unsigned int yajl_len_t; +#endif + +/** Number of types for ceph defined in types.db */ +#define CEPH_DSET_TYPES_NUM 3 +/** ceph types enum */ +enum ceph_dset_type_d +{ + DSET_LATENCY = 0, + DSET_BYTES = 1, + DSET_RATE = 2, + DSET_TYPE_UNFOUND = 1000 +}; + +/** Valid types for ceph defined in types.db */ +const char * ceph_dset_types [CEPH_DSET_TYPES_NUM] = + {"ceph_latency", "ceph_bytes", "ceph_rate"}; + +/******* ceph_daemon *******/ +struct ceph_daemon +{ + /** Version of the admin_socket interface */ + uint32_t version; + /** daemon name **/ + char name[DATA_MAX_NAME_LEN]; + + /** Path to the socket that we use to talk to the ceph daemon */ + char asok_path[UNIX_DOMAIN_SOCK_PATH_MAX]; + + /** Number of counters */ + int ds_num; + /** Track ds types */ + uint32_t *ds_types; + /** Track ds names to match with types */ + char **ds_names; + + /** + * Keep track of last data for latency values so we can calculate rate + * since last poll. + */ + struct last_data **last_poll_data; + /** index of last poll data */ + int last_idx; +}; + +/******* JSON parsing *******/ +typedef int (*node_handler_t)(void *, const char*, const char*); + +/** Track state and handler while parsing JSON */ +struct yajl_struct +{ + node_handler_t handler; + void * handler_arg; + struct { + char key[DATA_MAX_NAME_LEN]; + int key_len; + } state[YAJL_MAX_DEPTH]; + int depth; +}; +typedef struct yajl_struct yajl_struct; + +enum perfcounter_type_d +{ + PERFCOUNTER_LATENCY = 0x4, PERFCOUNTER_DERIVE = 0x8, +}; + +/** Give user option to use default (long run = since daemon started) avg */ +static int long_run_latency_avg = 0; + +/** + * Give user option to use default type for special cases - + * filestore.journal_wr_bytes is currently only metric here. Ceph reports the + * type as a sum/count pair and will calculate it the same as a latency value. + * All other "bytes" metrics (excluding the used/capacity bytes for the OSD) + * use the DERIVE type. Unless user specifies to use given type, convert this + * metric to use DERIVE. + */ +static int convert_special_metrics = 1; + +/** Array of daemons to monitor */ +static struct ceph_daemon **g_daemons = NULL; + +/** Number of elements in g_daemons */ +static int g_num_daemons = 0; + +/** + * A set of data that we build up in memory while parsing the JSON. + */ +struct values_tmp +{ + /** ceph daemon we are processing data for*/ + struct ceph_daemon *d; + /** track avgcount across counters for avgcount/sum latency pairs */ + uint64_t avgcount; + /** current index of counters - used to get type of counter */ + int index; + /** do we already have an avgcount for latency pair */ + int avgcount_exists; + /** + * similar to index, but current index of latency type counters - + * used to get last poll data of counter + */ + int latency_index; + /** + * values list - maintain across counters since + * host/plugin/plugin instance are always the same + */ + value_list_t vlist; +}; + +/** + * A set of count/sum pairs to keep track of latency types and get difference + * between this poll data and last poll data. + */ +struct last_data +{ + char ds_name[DATA_MAX_NAME_LEN]; + double last_sum; + uint64_t last_count; +}; + +/******* network I/O *******/ +enum cstate_t +{ + CSTATE_UNCONNECTED = 0, + CSTATE_WRITE_REQUEST, + CSTATE_READ_VERSION, + CSTATE_READ_AMT, + CSTATE_READ_JSON, +}; + +enum request_type_t +{ + ASOK_REQ_VERSION = 0, + ASOK_REQ_DATA = 1, + ASOK_REQ_SCHEMA = 2, + ASOK_REQ_NONE = 1000, +}; + +struct cconn +{ + /** The Ceph daemon that we're talking to */ + struct ceph_daemon *d; + + /** Request type */ + uint32_t request_type; + + /** The connection state */ + enum cstate_t state; + + /** The socket we use to talk to this daemon */ + int asok; + + /** The amount of data remaining to read / write. */ + uint32_t amt; + + /** Length of the JSON to read */ + uint32_t json_len; + + /** Buffer containing JSON data */ + unsigned char *json; + + /** Keep data important to yajl processing */ + struct yajl_struct yajl; +}; + +static int ceph_cb_null(void *ctx) +{ + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_boolean(void *ctx, int bool_val) +{ + return CEPH_CB_CONTINUE; +} + +static int +ceph_cb_number(void *ctx, const char *number_val, yajl_len_t number_len) +{ + yajl_struct *yajl = (yajl_struct*)ctx; + char buffer[number_len+1]; + int i, latency_type = 0, result; + char key[128]; + + memcpy(buffer, number_val, number_len); + buffer[sizeof(buffer) - 1] = 0; + + ssnprintf(key, yajl->state[0].key_len, "%s", yajl->state[0].key); + for(i = 1; i < yajl->depth; i++) + { + if((i == yajl->depth-1) && ((strcmp(yajl->state[i].key,"avgcount") == 0) + || (strcmp(yajl->state[i].key,"sum") == 0))) + { + if(convert_special_metrics) + { + /** + * Special case for filestore:JournalWrBytes. For some reason, + * Ceph schema encodes this as a count/sum pair while all + * other "Bytes" data (excluding used/capacity bytes for OSD + * space) uses a single "Derive" type. To spare further + * confusion, keep this KPI as the same type of other "Bytes". + * Instead of keeping an "average" or "rate", use the "sum" in + * the pair and assign that to the derive value. + */ + if((strcmp(yajl->state[i-1].key, "journal_wr_bytes") == 0) && + (strcmp(yajl->state[i-2].key,"filestore") == 0) && + (strcmp(yajl->state[i].key,"avgcount") == 0)) + { + DEBUG("ceph plugin: Skipping avgcount for filestore.JournalWrBytes"); + yajl->depth = (yajl->depth - 1); + return CEPH_CB_CONTINUE; + } + } + //probably a avgcount/sum pair. if not - we'll try full key later + latency_type = 1; + break; + } + strncat(key, ".", 1); + strncat(key, yajl->state[i].key, yajl->state[i].key_len+1); + } + + result = yajl->handler(yajl->handler_arg, buffer, key); + + if((result == RETRY_AVGCOUNT) && latency_type) + { + strncat(key, ".", 1); + strncat(key, yajl->state[yajl->depth-1].key, + yajl->state[yajl->depth-1].key_len+1); + result = yajl->handler(yajl->handler_arg, buffer, key); + } + + if(result == -ENOMEM) + { + ERROR("ceph plugin: memory allocation failed"); + return CEPH_CB_ABORT; + } + + yajl->depth = (yajl->depth - 1); + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_string(void *ctx, const unsigned char *string_val, + yajl_len_t string_len) +{ + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_start_map(void *ctx) +{ + return CEPH_CB_CONTINUE; +} + +static int +ceph_cb_map_key(void *ctx, const unsigned char *key, yajl_len_t string_len) +{ + yajl_struct *yajl = (yajl_struct*)ctx; + + if((yajl->depth+1) >= YAJL_MAX_DEPTH) + { + ERROR("ceph plugin: depth exceeds max, aborting."); + return CEPH_CB_ABORT; + } + + char buffer[string_len+1]; + + memcpy(buffer, key, string_len); + buffer[sizeof(buffer) - 1] = 0; + + snprintf(yajl->state[yajl->depth].key, sizeof(buffer), "%s", buffer); + yajl->state[yajl->depth].key_len = sizeof(buffer); + yajl->depth = (yajl->depth + 1); + + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_end_map(void *ctx) +{ + yajl_struct *yajl = (yajl_struct*)ctx; + + yajl->depth = (yajl->depth - 1); + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_start_array(void *ctx) +{ + return CEPH_CB_CONTINUE; +} + +static int ceph_cb_end_array(void *ctx) +{ + return CEPH_CB_CONTINUE; +} + +static yajl_callbacks callbacks = { + ceph_cb_null, + ceph_cb_boolean, + NULL, + NULL, + ceph_cb_number, + ceph_cb_string, + ceph_cb_start_map, + ceph_cb_map_key, + ceph_cb_end_map, + ceph_cb_start_array, + ceph_cb_end_array +}; + +static void ceph_daemon_print(const struct ceph_daemon *d) +{ + DEBUG("ceph plugin: name=%s, asok_path=%s", d->name, d->asok_path); +} + +static void ceph_daemons_print(void) +{ + int i; + for(i = 0; i < g_num_daemons; ++i) + { + ceph_daemon_print(g_daemons[i]); + } +} + +static void ceph_daemon_free(struct ceph_daemon *d) +{ + int i = 0; + for(; i < d->last_idx; i++) + { + sfree(d->last_poll_data[i]); + } + sfree(d->last_poll_data); + d->last_poll_data = NULL; + d->last_idx = 0; + for(i = 0; i < d->ds_num; i++) + { + sfree(d->ds_names[i]); + } + sfree(d->ds_types); + sfree(d->ds_names); + sfree(d); +} + +/** + * Compact ds name by removing special characters and trimming length to + * DATA_MAX_NAME_LEN if necessary + */ +static void compact_ds_name(char *source, char *dest) +{ + int keys_num = 0, i; + char *save_ptr = NULL, *tmp_ptr = source; + char *keys[16]; + char len_str[3]; + char tmp[DATA_MAX_NAME_LEN]; + size_t key_chars_remaining = (DATA_MAX_NAME_LEN-1); + int reserved = 0; + int offset = 0; + memset(tmp, 0, sizeof(tmp)); + if(source == NULL || dest == NULL || source[0] == '\0' || dest[0] != '\0') + { + return; + } + size_t src_len = strlen(source); + snprintf(len_str, sizeof(len_str), "%zu", src_len); + unsigned char append_status = 0x0; + append_status |= (source[src_len - 1] == '-') ? 0x1 : 0x0; + append_status |= (source[src_len - 1] == '+') ? 0x2 : 0x0; + while ((keys[keys_num] = strtok_r(tmp_ptr, ":_-+", &save_ptr)) != NULL) + { + tmp_ptr = NULL; + /** capitalize 1st char **/ + keys[keys_num][0] = toupper(keys[keys_num][0]); + keys_num++; + if(keys_num >= 16) + { + break; + } + } + /** concatenate each part of source string **/ + for(i = 0; i < keys_num; i++) + { + strncat(tmp, keys[i], key_chars_remaining); + key_chars_remaining -= strlen(keys[i]); + } + tmp[DATA_MAX_NAME_LEN - 1] = '\0'; + /** to coordinate limitation of length of type_instance + * we will truncate ds_name + * when the its length is more than + * DATA_MAX_NAME_LEN + */ + if(strlen(tmp) > DATA_MAX_NAME_LEN - 1) + { + append_status |= 0x4; + /** we should reserve space for + * len_str + */ + reserved += 2; + } + if(append_status & 0x1) + { + /** we should reserve space for + * "Minus" + */ + reserved += 5; + } + if(append_status & 0x2) + { + /** we should reserve space for + * "Plus" + */ + reserved += 4; + } + snprintf(dest, DATA_MAX_NAME_LEN - reserved, "%s", tmp); + offset = strlen(dest); + switch (append_status) + { + case 0x1: + memcpy(dest + offset, "Minus", 5); + break; + case 0x2: + memcpy(dest + offset, "Plus", 5); + break; + case 0x4: + memcpy(dest + offset, len_str, 2); + break; + case 0x5: + memcpy(dest + offset, "Minus", 5); + memcpy(dest + offset + 5, len_str, 2); + break; + case 0x6: + memcpy(dest + offset, "Plus", 4); + memcpy(dest + offset + 4, len_str, 2); + break; + default: + break; + } +} + +/** + * Parse key to remove "type" if this is for schema and initiate compaction + */ +static int parse_keys(const char *key_str, char *ds_name) +{ + char *ptr, *rptr; + size_t ds_name_len = 0; + /** + * allow up to 100 characters before compaction - compact_ds_name will not + * allow more than DATA_MAX_NAME_LEN chars + */ + int max_str_len = 100; + char tmp_ds_name[max_str_len]; + memset(tmp_ds_name, 0, sizeof(tmp_ds_name)); + if(ds_name == NULL || key_str == NULL || key_str[0] == '\0' || + ds_name[0] != '\0') + { + return -1; + } + if((ptr = strchr(key_str, '.')) == NULL + || (rptr = strrchr(key_str, '.')) == NULL) + { + memcpy(tmp_ds_name, key_str, max_str_len - 1); + goto compact; + } + + ds_name_len = (rptr - ptr) > max_str_len ? max_str_len : (rptr - ptr); + if((ds_name_len == 0) || strncmp(rptr + 1, "type", 4)) + { /** copy whole key **/ + memcpy(tmp_ds_name, key_str, max_str_len - 1); + } + else + {/** more than two keys **/ + memcpy(tmp_ds_name, key_str, ((rptr - key_str) > (max_str_len - 1) ? + (max_str_len - 1) : (rptr - key_str))); + } + + compact: compact_ds_name(tmp_ds_name, ds_name); + return 0; +} + +/** + * while parsing ceph admin socket schema, save counter name and type for later + * data processing + */ +static int ceph_daemon_add_ds_entry(struct ceph_daemon *d, const char *name, + int pc_type) +{ + uint32_t type; + char ds_name[DATA_MAX_NAME_LEN]; + memset(ds_name, 0, sizeof(ds_name)); + + if(convert_special_metrics) + { + /** + * Special case for filestore:JournalWrBytes. For some reason, Ceph + * schema encodes this as a count/sum pair while all other "Bytes" data + * (excluding used/capacity bytes for OSD space) uses a single "Derive" + * type. To spare further confusion, keep this KPI as the same type of + * other "Bytes". Instead of keeping an "average" or "rate", use the + * "sum" in the pair and assign that to the derive value. + */ + if((strcmp(name,"filestore.journal_wr_bytes.type") == 0)) + { + pc_type = 10; + } + } + + d->ds_names = realloc(d->ds_names, sizeof(char *) * (d->ds_num + 1)); + if(!d->ds_names) + { + return -ENOMEM; + } + + d->ds_types = realloc(d->ds_types, sizeof(uint32_t) * (d->ds_num + 1)); + if(!d->ds_types) + { + return -ENOMEM; + } + + d->ds_names[d->ds_num] = malloc(sizeof(char) * DATA_MAX_NAME_LEN); + if(!d->ds_names[d->ds_num]) + { + return -ENOMEM; + } + + type = (pc_type & PERFCOUNTER_DERIVE) ? DSET_RATE : + ((pc_type & PERFCOUNTER_LATENCY) ? DSET_LATENCY : DSET_BYTES); + d->ds_types[d->ds_num] = type; + + if(parse_keys(name, ds_name)) + { + return 1; + } + + sstrncpy(d->ds_names[d->ds_num], ds_name, DATA_MAX_NAME_LEN -1); + d->ds_num = (d->ds_num + 1); + + return 0; +} + +/******* ceph_config *******/ +static int cc_handle_str(struct oconfig_item_s *item, char *dest, int dest_len) +{ + const char *val; + if(item->values_num != 1) + { + return -ENOTSUP; + } + if(item->values[0].type != OCONFIG_TYPE_STRING) + { + return -ENOTSUP; + } + val = item->values[0].value.string; + if(snprintf(dest, dest_len, "%s", val) > (dest_len - 1)) + { + ERROR("ceph plugin: configuration parameter '%s' is too long.\n", + item->key); + return -ENAMETOOLONG; + } + return 0; +} + +static int cc_handle_bool(struct oconfig_item_s *item, int *dest) +{ + if(item->values_num != 1) + { + return -ENOTSUP; + } + + if(item->values[0].type != OCONFIG_TYPE_BOOLEAN) + { + return -ENOTSUP; + } + + *dest = (item->values[0].value.boolean) ? 1 : 0; + return 0; +} + +static int cc_add_daemon_config(oconfig_item_t *ci) +{ + int ret, i; + struct ceph_daemon *array, *nd, cd; + memset(&cd, 0, sizeof(struct ceph_daemon)); + + if((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING("ceph plugin: `Daemon' blocks need exactly one string " + "argument."); + return (-1); + } + + ret = cc_handle_str(ci, cd.name, DATA_MAX_NAME_LEN); + if(ret) + { + return ret; + } + + for(i=0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if(strcasecmp("SocketPath", child->key) == 0) + { + ret = cc_handle_str(child, cd.asok_path, sizeof(cd.asok_path)); + if(ret) + { + return ret; + } + } + else + { + WARNING("ceph plugin: ignoring unknown option %s", child->key); + } + } + if(cd.name[0] == '\0') + { + ERROR("ceph plugin: you must configure a daemon name.\n"); + return -EINVAL; + } + else if(cd.asok_path[0] == '\0') + { + ERROR("ceph plugin(name=%s): you must configure an administrative " + "socket path.\n", cd.name); + return -EINVAL; + } + else if(!((cd.asok_path[0] == '/') || + (cd.asok_path[0] == '.' && cd.asok_path[1] == '/'))) + { + ERROR("ceph plugin(name=%s): administrative socket paths must begin " + "with '/' or './' Can't parse: '%s'\n", cd.name, cd.asok_path); + return -EINVAL; + } + + array = realloc(g_daemons, + sizeof(struct ceph_daemon *) * (g_num_daemons + 1)); + if(array == NULL) + { + /* The positive return value here indicates that this is a + * runtime error, not a configuration error. */ + return ENOMEM; + } + g_daemons = (struct ceph_daemon**) array; + nd = malloc(sizeof(struct ceph_daemon)); + if(!nd) + { + return ENOMEM; + } + memcpy(nd, &cd, sizeof(struct ceph_daemon)); + g_daemons[g_num_daemons++] = nd; + return 0; +} + +static int ceph_config(oconfig_item_t *ci) +{ + int ret, i; + + for(i = 0; i < ci->children_num; ++i) + { + oconfig_item_t *child = ci->children + i; + if(strcasecmp("Daemon", child->key) == 0) + { + ret = cc_add_daemon_config(child); + if(ret == ENOMEM) + { + ERROR("ceph plugin: Couldn't allocate memory"); + return ret; + } + else if(ret) + { + //process other daemons and ignore this one + continue; + } + } + else if(strcasecmp("LongRunAvgLatency", child->key) == 0) + { + ret = cc_handle_bool(child, &long_run_latency_avg); + if(ret) + { + return ret; + } + } + else if(strcasecmp("ConvertSpecialMetricTypes", child->key) == 0) + { + ret = cc_handle_bool(child, &convert_special_metrics); + if(ret) + { + return ret; + } + } + else + { + WARNING("ceph plugin: ignoring unknown option %s", child->key); + } + } + return 0; +} + +/** + * Parse JSON and get error message if present + */ +static int +traverse_json(const unsigned char *json, uint32_t json_len, yajl_handle hand) +{ + yajl_status status = yajl_parse(hand, json, json_len); + unsigned char *msg; + + switch(status) + { + case yajl_status_error: + msg = yajl_get_error(hand, /* verbose = */ 1, + /* jsonText = */ (unsigned char *) json, + (unsigned int) json_len); + ERROR ("ceph plugin: yajl_parse failed: %s", msg); + yajl_free_error(hand, msg); + return 1; + case yajl_status_client_canceled: + return 1; + default: + return 0; + } +} + +/** + * Add entry for each counter while parsing schema + */ +static int +node_handler_define_schema(void *arg, const char *val, const char *key) +{ + struct ceph_daemon *d = (struct ceph_daemon *) arg; + int pc_type; + pc_type = atoi(val); + return ceph_daemon_add_ds_entry(d, key, pc_type); +} + +/** + * Latency counter does not yet have an entry in last poll data - add it. + */ +static int add_last(struct ceph_daemon *d, const char *ds_n, double cur_sum, + uint64_t cur_count) +{ + d->last_poll_data[d->last_idx] = malloc(1 * sizeof(struct last_data)); + if(!d->last_poll_data[d->last_idx]) + { + return -ENOMEM; + } + sstrncpy(d->last_poll_data[d->last_idx]->ds_name,ds_n, + sizeof(d->last_poll_data[d->last_idx]->ds_name)); + d->last_poll_data[d->last_idx]->last_sum = cur_sum; + d->last_poll_data[d->last_idx]->last_count = cur_count; + d->last_idx = (d->last_idx + 1); + return 0; +} + +/** + * Update latency counter or add new entry if it doesn't exist + */ +static int update_last(struct ceph_daemon *d, const char *ds_n, int index, + double cur_sum, uint64_t cur_count) +{ + if((d->last_idx > index) && (strcmp(d->last_poll_data[index]->ds_name, ds_n) == 0)) + { + d->last_poll_data[index]->last_sum = cur_sum; + d->last_poll_data[index]->last_count = cur_count; + return 0; + } + + if(!d->last_poll_data) + { + d->last_poll_data = malloc(1 * sizeof(struct last_data *)); + if(!d->last_poll_data) + { + return -ENOMEM; + } + } + else + { + struct last_data **tmp_last = realloc(d->last_poll_data, + ((d->last_idx+1) * sizeof(struct last_data *))); + if(!tmp_last) + { + return -ENOMEM; + } + d->last_poll_data = tmp_last; + } + return add_last(d, ds_n, cur_sum, cur_count); +} + +/** + * If using index guess failed (shouldn't happen, but possible if counters + * get rearranged), resort to searching for counter name + */ +static int backup_search_for_last_avg(struct ceph_daemon *d, const char *ds_n) +{ + int i = 0; + for(; i < d->last_idx; i++) + { + if(strcmp(d->last_poll_data[i]->ds_name, ds_n) == 0) + { + return i; + } + } + return -1; +} + +/** + * Calculate average b/t current data and last poll data + * if last poll data exists + */ +static double get_last_avg(struct ceph_daemon *d, const char *ds_n, int index, + double cur_sum, uint64_t cur_count) +{ + double result = -1.1, sum_delt = 0.0; + uint64_t count_delt = 0; + int tmp_index = 0; + if(d->last_idx > index) + { + if(strcmp(d->last_poll_data[index]->ds_name, ds_n) == 0) + { + tmp_index = index; + } + //test previous index + else if((index > 0) && (strcmp(d->last_poll_data[index-1]->ds_name, ds_n) == 0)) + { + tmp_index = (index - 1); + } + else + { + tmp_index = backup_search_for_last_avg(d, ds_n); + } + + if((tmp_index > -1) && (cur_count > d->last_poll_data[tmp_index]->last_count)) + { + sum_delt = (cur_sum - d->last_poll_data[tmp_index]->last_sum); + count_delt = (cur_count - d->last_poll_data[tmp_index]->last_count); + result = (sum_delt / count_delt); + } + } + + if(result == -1.1) + { + result = NAN; + } + if(update_last(d, ds_n, tmp_index, cur_sum, cur_count) == -ENOMEM) + { + return -ENOMEM; + } + return result; +} + +/** + * If using index guess failed, resort to searching for counter name + */ +static uint32_t backup_search_for_type(struct ceph_daemon *d, char *ds_name) +{ + int idx = 0; + for(; idx < d->ds_num; idx++) + { + if(strcmp(d->ds_names[idx], ds_name) == 0) + { + return d->ds_types[idx]; + } + } + return DSET_TYPE_UNFOUND; +} + +/** + * Process counter data and dispatch values + */ +static int node_handler_fetch_data(void *arg, const char *val, const char *key) +{ + value_t uv; + double tmp_d; + uint64_t tmp_u; + struct values_tmp *vtmp = (struct values_tmp*) arg; + uint32_t type = DSET_TYPE_UNFOUND; + int index = vtmp->index; + + char ds_name[DATA_MAX_NAME_LEN]; + memset(ds_name, 0, sizeof(ds_name)); + + if(parse_keys(key, ds_name)) + { + return 1; + } + + if(index >= vtmp->d->ds_num) + { + //don't overflow bounds of array + index = (vtmp->d->ds_num - 1); + } + + /** + * counters should remain in same order we parsed schema... we maintain the + * index variable to keep track of current point in list of counters. first + * use index to guess point in array for retrieving type. if that doesn't + * work, use the old way to get the counter type + */ + if(strcmp(ds_name, vtmp->d->ds_names[index]) == 0) + { + //found match + type = vtmp->d->ds_types[index]; + } + else if((index > 0) && (strcmp(ds_name, vtmp->d->ds_names[index-1]) == 0)) + { + //try previous key + type = vtmp->d->ds_types[index-1]; + } + + if(type == DSET_TYPE_UNFOUND) + { + //couldn't find right type by guessing, check the old way + type = backup_search_for_type(vtmp->d, ds_name); + } + + switch(type) + { + case DSET_LATENCY: + if(vtmp->avgcount_exists == -1) + { + sscanf(val, "%" PRIu64, &vtmp->avgcount); + vtmp->avgcount_exists = 0; + //return after saving avgcount - don't dispatch value + //until latency calculation + return 0; + } + else + { + double sum, result; + sscanf(val, "%lf", &sum); + + if(vtmp->avgcount == 0) + { + vtmp->avgcount = 1; + } + + /** User wants latency values as long run avg */ + if(long_run_latency_avg) + { + result = (sum / vtmp->avgcount); + } + else + { + result = get_last_avg(vtmp->d, ds_name, vtmp->latency_index, sum, vtmp->avgcount); + if(result == -ENOMEM) + { + return -ENOMEM; + } + } + + uv.gauge = result; + vtmp->avgcount_exists = -1; + vtmp->latency_index = (vtmp->latency_index + 1); + } + break; + case DSET_BYTES: + sscanf(val, "%lf", &tmp_d); + uv.gauge = tmp_d; + break; + case DSET_RATE: + sscanf(val, "%" PRIu64, &tmp_u); + uv.derive = tmp_u; + break; + case DSET_TYPE_UNFOUND: + default: + ERROR("ceph plugin: ds %s was not properly initialized.", ds_name); + return -1; + } + + sstrncpy(vtmp->vlist.type, ceph_dset_types[type], sizeof(vtmp->vlist.type)); + sstrncpy(vtmp->vlist.type_instance, ds_name, sizeof(vtmp->vlist.type_instance)); + vtmp->vlist.values = &uv; + vtmp->vlist.values_len = 1; + + vtmp->index = (vtmp->index + 1); + plugin_dispatch_values(&vtmp->vlist); + + return 0; +} + +static int cconn_connect(struct cconn *io) +{ + struct sockaddr_un address; + int flags, fd, err; + if(io->state != CSTATE_UNCONNECTED) + { + ERROR("ceph plugin: cconn_connect: io->state != CSTATE_UNCONNECTED"); + return -EDOM; + } + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd < 0) + { + int err = -errno; + ERROR("ceph plugin: cconn_connect: socket(PF_UNIX, SOCK_STREAM, 0) " + "failed: error %d", err); + return err; + } + memset(&address, 0, sizeof(struct sockaddr_un)); + address.sun_family = AF_UNIX; + snprintf(address.sun_path, sizeof(address.sun_path), "%s", + io->d->asok_path); + RETRY_ON_EINTR(err, + connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un))); + if(err < 0) + { + ERROR("ceph plugin: cconn_connect: connect(%d) failed: error %d", + fd, err); + return err; + } + + flags = fcntl(fd, F_GETFL, 0); + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) + { + err = -errno; + ERROR("ceph plugin: cconn_connect: fcntl(%d, O_NONBLOCK) error %d", + fd, err); + return err; + } + io->asok = fd; + io->state = CSTATE_WRITE_REQUEST; + io->amt = 0; + io->json_len = 0; + io->json = NULL; + return 0; +} + +static void cconn_close(struct cconn *io) +{ + io->state = CSTATE_UNCONNECTED; + if(io->asok != -1) + { + int res; + RETRY_ON_EINTR(res, close(io->asok)); + } + io->asok = -1; + io->amt = 0; + io->json_len = 0; + sfree(io->json); + io->json = NULL; +} + +/* Process incoming JSON counter data */ +static int +cconn_process_data(struct cconn *io, yajl_struct *yajl, yajl_handle hand) +{ + int ret; + struct values_tmp *vtmp = calloc(1, sizeof(struct values_tmp) * 1); + if(!vtmp) + { + return -ENOMEM; + } + + vtmp->vlist = (value_list_t)VALUE_LIST_INIT; + sstrncpy(vtmp->vlist.host, hostname_g, sizeof(vtmp->vlist.host)); + sstrncpy(vtmp->vlist.plugin, "ceph", sizeof(vtmp->vlist.plugin)); + sstrncpy(vtmp->vlist.plugin_instance, io->d->name, sizeof(vtmp->vlist.plugin_instance)); + + vtmp->d = io->d; + vtmp->avgcount_exists = -1; + vtmp->latency_index = 0; + vtmp->index = 0; + yajl->handler_arg = vtmp; + ret = traverse_json(io->json, io->json_len, hand); + sfree(vtmp); + return ret; +} + +/** + * Initiate JSON parsing and print error if one occurs + */ +static int cconn_process_json(struct cconn *io) +{ + if((io->request_type != ASOK_REQ_DATA) && + (io->request_type != ASOK_REQ_SCHEMA)) + { + return -EDOM; + } + + int result = 1; + yajl_handle hand; + yajl_status status; + + hand = yajl_alloc(&callbacks, +#if HAVE_YAJL_V2 + /* alloc funcs = */ NULL, +#else + /* alloc funcs = */ NULL, NULL, +#endif + /* context = */ (void *)(&io->yajl)); + + if(!hand) + { + ERROR ("ceph plugin: yajl_alloc failed."); + return ENOMEM; + } + + io->yajl.depth = 0; + + switch(io->request_type) + { + case ASOK_REQ_DATA: + io->yajl.handler = node_handler_fetch_data; + result = cconn_process_data(io, &io->yajl, hand); + break; + case ASOK_REQ_SCHEMA: + //init daemon specific variables + io->d->ds_num = 0; + io->d->last_idx = 0; + io->d->last_poll_data = NULL; + io->yajl.handler = node_handler_define_schema; + io->yajl.handler_arg = io->d; + result = traverse_json(io->json, io->json_len, hand); + break; + } + + if(result) + { + goto done; + } + +#if HAVE_YAJL_V2 + status = yajl_complete_parse(hand); +#else + status = yajl_parse_complete(hand); +#endif + + if (status != yajl_status_ok) + { + unsigned char *errmsg = yajl_get_error (hand, /* verbose = */ 0, + /* jsonText = */ NULL, /* jsonTextLen = */ 0); + ERROR ("ceph plugin: yajl_parse_complete failed: %s", + (char *) errmsg); + yajl_free_error (hand, errmsg); + yajl_free (hand); + return 1; + } + + done: + yajl_free (hand); + return result; +} + +static int cconn_validate_revents(struct cconn *io, int revents) +{ + if(revents & POLLERR) + { + ERROR("ceph plugin: cconn_validate_revents(name=%s): got POLLERR", + io->d->name); + return -EIO; + } + switch (io->state) + { + case CSTATE_WRITE_REQUEST: + return (revents & POLLOUT) ? 0 : -EINVAL; + case CSTATE_READ_VERSION: + case CSTATE_READ_AMT: + case CSTATE_READ_JSON: + return (revents & POLLIN) ? 0 : -EINVAL; + default: + ERROR("ceph plugin: cconn_validate_revents(name=%s) got to " + "illegal state on line %d", io->d->name, __LINE__); + return -EDOM; + } +} + +/** Handle a network event for a connection */ +static int cconn_handle_event(struct cconn *io) +{ + int ret; + switch (io->state) + { + case CSTATE_UNCONNECTED: + ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal " + "state on line %d", io->d->name, __LINE__); + + return -EDOM; + case CSTATE_WRITE_REQUEST: + { + char cmd[32]; + snprintf(cmd, sizeof(cmd), "%s%d%s", "{ \"prefix\": \"", + io->request_type, "\" }\n"); + size_t cmd_len = strlen(cmd); + RETRY_ON_EINTR(ret, + write(io->asok, ((char*)&cmd) + io->amt, cmd_len - io->amt)); + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%d)", + io->d->name, io->state, io->amt, ret); + if(ret < 0) + { + return ret; + } + io->amt += ret; + if(io->amt >= cmd_len) + { + io->amt = 0; + switch (io->request_type) + { + case ASOK_REQ_VERSION: + io->state = CSTATE_READ_VERSION; + break; + default: + io->state = CSTATE_READ_AMT; + break; + } + } + return 0; + } + case CSTATE_READ_VERSION: + { + RETRY_ON_EINTR(ret, + read(io->asok, ((char*)(&io->d->version)) + io->amt, + sizeof(io->d->version) - io->amt)); + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + io->d->name, io->state, ret); + if(ret < 0) + { + return ret; + } + io->amt += ret; + if(io->amt >= sizeof(io->d->version)) + { + io->d->version = ntohl(io->d->version); + if(io->d->version != 1) + { + ERROR("ceph plugin: cconn_handle_event(name=%s) not " + "expecting version %d!", io->d->name, io->d->version); + return -ENOTSUP; + } + DEBUG("ceph plugin: cconn_handle_event(name=%s): identified as " + "version %d", io->d->name, io->d->version); + io->amt = 0; + cconn_close(io); + io->request_type = ASOK_REQ_SCHEMA; + } + return 0; + } + case CSTATE_READ_AMT: + { + RETRY_ON_EINTR(ret, + read(io->asok, ((char*)(&io->json_len)) + io->amt, + sizeof(io->json_len) - io->amt)); + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + io->d->name, io->state, ret); + if(ret < 0) + { + return ret; + } + io->amt += ret; + if(io->amt >= sizeof(io->json_len)) + { + io->json_len = ntohl(io->json_len); + io->amt = 0; + io->state = CSTATE_READ_JSON; + io->json = calloc(1, io->json_len + 1); + if(!io->json) + { + ERROR("ceph plugin: error callocing io->json"); + return -ENOMEM; + } + } + return 0; + } + case CSTATE_READ_JSON: + { + RETRY_ON_EINTR(ret, + read(io->asok, io->json + io->amt, io->json_len - io->amt)); + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + io->d->name, io->state, ret); + if(ret < 0) + { + return ret; + } + io->amt += ret; + if(io->amt >= io->json_len) + { + ret = cconn_process_json(io); + if(ret) + { + return ret; + } + cconn_close(io); + io->request_type = ASOK_REQ_NONE; + } + return 0; + } + default: + ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal " + "state on line %d", io->d->name, __LINE__); + return -EDOM; + } +} + +static int cconn_prepare(struct cconn *io, struct pollfd* fds) +{ + int ret; + if(io->request_type == ASOK_REQ_NONE) + { + /* The request has already been serviced. */ + return 0; + } + else if((io->request_type == ASOK_REQ_DATA) && (io->d->ds_num == 0)) + { + /* If there are no counters to report on, don't bother + * connecting */ + return 0; + } + + switch (io->state) + { + case CSTATE_UNCONNECTED: + ret = cconn_connect(io); + if(ret > 0) + { + return -ret; + } + else if(ret < 0) + { + return ret; + } + fds->fd = io->asok; + fds->events = POLLOUT; + return 1; + case CSTATE_WRITE_REQUEST: + fds->fd = io->asok; + fds->events = POLLOUT; + return 1; + case CSTATE_READ_VERSION: + case CSTATE_READ_AMT: + case CSTATE_READ_JSON: + fds->fd = io->asok; + fds->events = POLLIN; + return 1; + default: + ERROR("ceph plugin: cconn_prepare(name=%s) got to illegal state " + "on line %d", io->d->name, __LINE__); + return -EDOM; + } +} + +/** Returns the difference between two struct timevals in milliseconds. + * On overflow, we return max/min int. + */ +static int milli_diff(const struct timeval *t1, const struct timeval *t2) +{ + int64_t ret; + int sec_diff = t1->tv_sec - t2->tv_sec; + int usec_diff = t1->tv_usec - t2->tv_usec; + ret = usec_diff / 1000; + ret += (sec_diff * 1000); + return (ret > INT_MAX) ? INT_MAX : ((ret < INT_MIN) ? INT_MIN : (int)ret); +} + +/** This handles the actual network I/O to talk to the Ceph daemons. + */ +static int cconn_main_loop(uint32_t request_type) +{ + int i, ret, some_unreachable = 0; + struct timeval end_tv; + struct cconn io_array[g_num_daemons]; + + DEBUG("ceph plugin: entering cconn_main_loop(request_type = %d)", request_type); + + /* create cconn array */ + memset(io_array, 0, sizeof(io_array)); + for(i = 0; i < g_num_daemons; ++i) + { + io_array[i].d = g_daemons[i]; + io_array[i].request_type = request_type; + io_array[i].state = CSTATE_UNCONNECTED; + } + + /** Calculate the time at which we should give up */ + gettimeofday(&end_tv, NULL); + end_tv.tv_sec += CEPH_TIMEOUT_INTERVAL; + + while (1) + { + int nfds, diff; + struct timeval tv; + struct cconn *polled_io_array[g_num_daemons]; + struct pollfd fds[g_num_daemons]; + memset(fds, 0, sizeof(fds)); + nfds = 0; + for(i = 0; i < g_num_daemons; ++i) + { + struct cconn *io = io_array + i; + ret = cconn_prepare(io, fds + nfds); + if(ret < 0) + { + WARNING("ceph plugin: cconn_prepare(name=%s,i=%d,st=%d)=%d", + io->d->name, i, io->state, ret); + cconn_close(io); + io->request_type = ASOK_REQ_NONE; + some_unreachable = 1; + } + else if(ret == 1) + { + polled_io_array[nfds++] = io_array + i; + } + } + if(nfds == 0) + { + /* finished */ + ret = 0; + goto done; + } + gettimeofday(&tv, NULL); + diff = milli_diff(&end_tv, &tv); + if(diff <= 0) + { + /* Timed out */ + ret = -ETIMEDOUT; + WARNING("ceph plugin: cconn_main_loop: timed out."); + goto done; + } + RETRY_ON_EINTR(ret, poll(fds, nfds, diff)); + if(ret < 0) + { + ERROR("ceph plugin: poll(2) error: %d", ret); + goto done; + } + for(i = 0; i < nfds; ++i) + { + struct cconn *io = polled_io_array[i]; + int revents = fds[i].revents; + if(revents == 0) + { + /* do nothing */ + } + else if(cconn_validate_revents(io, revents)) + { + WARNING("ceph plugin: cconn(name=%s,i=%d,st=%d): " + "revents validation error: " + "revents=0x%08x", io->d->name, i, io->state, revents); + cconn_close(io); + io->request_type = ASOK_REQ_NONE; + some_unreachable = 1; + } + else + { + int ret = cconn_handle_event(io); + if(ret) + { + WARNING("ceph plugin: cconn_handle_event(name=%s," + "i=%d,st=%d): error %d", io->d->name, i, io->state, ret); + cconn_close(io); + io->request_type = ASOK_REQ_NONE; + some_unreachable = 1; + } + } + } + } + done: for(i = 0; i < g_num_daemons; ++i) + { + cconn_close(io_array + i); + } + if(some_unreachable) + { + DEBUG("ceph plugin: cconn_main_loop: some Ceph daemons were unreachable."); + } + else + { + DEBUG("ceph plugin: cconn_main_loop: reached all Ceph daemons :)"); + } + return ret; +} + +static int ceph_read(void) +{ + return cconn_main_loop(ASOK_REQ_DATA); +} + +/******* lifecycle *******/ +static int ceph_init(void) +{ + int ret; + ceph_daemons_print(); + + ret = cconn_main_loop(ASOK_REQ_VERSION); + + return (ret) ? ret : 0; +} + +static int ceph_shutdown(void) +{ + int i; + for(i = 0; i < g_num_daemons; ++i) + { + ceph_daemon_free(g_daemons[i]); + } + sfree(g_daemons); + g_daemons = NULL; + g_num_daemons = 0; + DEBUG("ceph plugin: finished ceph_shutdown"); + return 0; +} + +void module_register(void) +{ + plugin_register_complex_config("ceph", ceph_config); + plugin_register_init("ceph", ceph_init); + plugin_register_read("ceph", ceph_read); + plugin_register_shutdown("ceph", ceph_shutdown); +} diff --git a/src/collectd-exec.pod b/src/collectd-exec.pod index 5f2c687b..10f9f618 100644 --- a/src/collectd-exec.pod +++ b/src/collectd-exec.pod @@ -201,13 +201,13 @@ The data is passed to the executables over C in a format very similar to HTTP: At first there is a "header" with one line per field. Every line consists of a field name, ended by a colon, and the associated value until end-of-line. The "header" is ended by two newlines immediately following another, -i.Ee. an empty line. The rest, basically the "body", is the message of -the notification. +i.e. an empty line. The rest, basically the "body", is the message of the +notification. The following is an example notification passed to a program: Severity: FAILURE - Time: 1200928930 + Time: 1200928930.515 Host: myhost.mydomain.org \n This is a test notification to demonstrate the format @@ -223,7 +223,9 @@ Severity of the notification. May either be B, B, or B. =item B