From: Pierre-Yves Ritschard Date: Mon, 17 Nov 2014 12:54:35 +0000 (+0100) Subject: Merge pull request #808 from landryb/openbsd_build_fixes_2 X-Git-Tag: collectd-5.5.0~147 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=e5a4207a2ac08312bdb4be0082bc4d84327e85e0;hp=8fce2979d3fec72400654ce7986297d7579868c2;p=collectd.git Merge pull request #808 from landryb/openbsd_build_fixes_2 Openbsd build fixes 2 --- diff --git a/AUTHORS b/AUTHORS index 2a76b5b2..b1ebbf23 100644 --- a/AUTHORS +++ b/AUTHORS @@ -121,6 +121,9 @@ Jérôme Renard Kevin Bowling - write_tsdb plugin for http://opentsdb.net/ +Kimo Rosenbaum + - openldap plugin. + Kris Nielander - tail_csv plugin. @@ -151,6 +154,7 @@ Marc Fournier - RPM specfile update. - libmnl support in the netlink plugin. - linux support in the zfs_arc plugin. + - openldap plugin. Marco Chiappero - uptime plugin. @@ -249,7 +253,7 @@ Sven Trenkel - python plugin. Tim Laszlo - - drbd plugin + - drbd plugin. Thomas Meson - Graphite support for the AMQP plugin. @@ -260,6 +264,9 @@ Tomasz Pala Tommie Gannert - PID-file patch. +Vincent Bernat + - smart plugin. + Vincent Stehlé - hddtemp plugin. diff --git a/README b/README index 3e3a030b..3c7a10fb 100644 --- a/README +++ b/README @@ -229,6 +229,9 @@ Features Read onewire sensors using the owcapu library of the owfs project. Please read in collectd.conf(5) why this plugin is experimental. + - openldap + Read monitoring information from OpenLDAP's cn=Monitor subtree. + - openvpn RX and TX of each client in openvpn-status.log (status-version 2). @@ -293,6 +296,10 @@ Features to have its measurements fed to collectd. This includes multimeters, sound level meters, thermometers, and much more. + - smart + Collect SMART statistics, notably load cycle count, temperature + and bad sectors. + - snmp Read values from SNMP (Simple Network Management Protocol) enabled network devices such as switches, routers, thermometers, rack monitoring @@ -604,6 +611,10 @@ Prerequisites particular. + * libatasmart (optional) + Used by the `smart' plugin. + + * libclntsh (optional) Used by the `oracle' plugin. @@ -650,6 +661,10 @@ Prerequisites libjvm” below. (and others) + * libldap (optional) + Used by the `openldap' plugin. + + * liblvm2 (optional) Used by the `lvm' plugin. diff --git a/configure.ac b/configure.ac index c6204cf2..765770b9 100644 --- a/configure.ac +++ b/configure.ac @@ -2247,6 +2247,64 @@ AC_SUBST(JAVA_LIBS) AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes") # }}} +# --with-libldap {{{ +AC_ARG_WITH(libldap, [AS_HELP_STRING([--with-libldap@<:@=PREFIX@:>@], [Path to libldap.])], +[ + if test "x$withval" = "xyes" + then + with_libldap="yes" + else if test "x$withval" = "xno" + then + with_libldap="no" + else + with_libldap="yes" + LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS -I$withval/include" + LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS -L$withval/lib" + fi; fi +], +[with_libldap="yes"]) + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" + +CPPFLAGS="$CPPFLAGS $LIBLDAP_CPPFLAGS" +LDFLAGS="$LDFLAGS $LIBLDAP_LDFLAGS" + +if test "x$with_libldap" = "xyes" +then + if test "x$LIBLDAP_CPPFLAGS" != "x" + then + AC_MSG_NOTICE([libldap CPPFLAGS: $LIBLDAP_CPPFLAGS]) + fi + AC_CHECK_HEADERS(ldap.h, + [with_libldap="yes"], + [with_libldap="no ('ldap.h' not found)"]) +fi +if test "x$with_libldap" = "xyes" +then + if test "x$LIBLDAP_LDFLAGS" != "x" + then + AC_MSG_NOTICE([libldap LDFLAGS: $LIBLDAP_LDFLAGS]) + fi + AC_CHECK_LIB(ldap, ldap_initialize, + [with_libldap="yes"], + [with_libldap="no (symbol 'ldap_initialize' not found)"]) + +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" + +if test "x$with_libldap" = "xyes" +then + BUILD_WITH_LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS" + BUILD_WITH_LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS" + AC_SUBST(BUILD_WITH_LIBLDAP_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBLDAP_LDFLAGS) +fi +AM_CONDITIONAL(BUILD_WITH_LIBLDAP, test "x$with_libldap" = "xyes") +# }}} + # --with-liblvm2app {{{ with_liblvm2app_cppflags="" with_liblvm2app_ldflags="" @@ -4897,6 +4955,62 @@ then fi # }}} +# --with-libatasmart {{{ +with_libatasmart_cppflags="" +with_libatasmart_ldflags="" +AC_ARG_WITH(libatasmart, [AS_HELP_STRING([--with-libatasmart@<:@=PREFIX@:>@], [Path to libatasmart.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + with_libatasmart_cppflags="-I$withval/include" + with_libatasmart_ldflags="-L$withval/lib" + with_libatasmart="yes" + else + with_libatasmart="$withval" + fi +], +[ + if test "x$ac_system" = "xLinux" + then + with_libatasmart="yes" + else + with_libatasmart="no (Linux only library)" + fi +]) +if test "x$with_libatasmart" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags" + + AC_CHECK_HEADERS(atasmart.h, [with_libatasmart="yes"], [with_libatasmart="no (atasmart.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libatasmart" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags" + LDFLAGS="$LDFLAGS $with_libatasmart_ldflags" + + AC_CHECK_LIB(atasmart, sk_disk_open, [with_libatasmart="yes"], [with_libatasmart="no (Symbol 'sk_disk_open' not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_libatasmart" = "xyes" +then + BUILD_WITH_LIBATASMART_CPPFLAGS="$with_libatasmart_cppflags" + BUILD_WITH_LIBATASMART_LDFLAGS="$with_libatasmart_ldflags" + BUILD_WITH_LIBATASMART_LIBS="-latasmart" + AC_SUBST(BUILD_WITH_LIBATASMART_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBATASMART_LDFLAGS) + AC_SUBST(BUILD_WITH_LIBATASMART_LIBS) + AC_DEFINE(HAVE_LIBATASMART, 1, [Define if libatasmart is present and usable.]) +fi +AM_CONDITIONAL(BUILD_WITH_LIBATASMART, test "x$with_libatasmart" = "xyes") +# }}} + PKG_CHECK_MODULES([LIBNOTIFY], [libnotify], [with_libnotify="yes"], [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then @@ -5459,6 +5573,7 @@ AC_PLUGIN([numa], [$plugin_numa], [NUMA virtual memory statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) AC_PLUGIN([olsrd], [yes], [olsrd statistics]) AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics]) +AC_PLUGIN([openldap], [$with_libldap], [OpenLDAP statistics]) AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics]) AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) @@ -5478,6 +5593,7 @@ AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin]) AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics]) AC_PLUGIN([serial], [$plugin_serial], [serial port traffic]) AC_PLUGIN([sigrok], [$with_libsigrok], [sigrok acquisition sources]) +AC_PLUGIN([smart], [$with_libatasmart], [SMART statistics]) AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin]) AC_PLUGIN([statsd], [yes], [StatsD plugin]) AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics]) @@ -5698,6 +5814,7 @@ Configuration: Libraries: intel mic . . . . . . $with_mic libaquaero5 . . . . . $with_libaquaero5 + libatasmart . . . . . $with_libatasmart libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi libcredis . . . . . . $with_libcredis @@ -5710,6 +5827,7 @@ Configuration: libjvm . . . . . . . $with_java libkstat . . . . . . $with_kstat libkvm . . . . . . . $with_libkvm + libldap . . . . . . . $with_libldap liblvm2app . . . . . $with_liblvm2app libmemcached . . . . $with_libmemcached libmnl . . . . . . . $with_libmnl @@ -5824,6 +5942,7 @@ Configuration: nut . . . . . . . . . $enable_nut olsrd . . . . . . . . $enable_olsrd onewire . . . . . . . $enable_onewire + openldap . . . . . . $enable_openldap openvpn . . . . . . . $enable_openvpn oracle . . . . . . . $enable_oracle perl . . . . . . . . $enable_perl @@ -5842,6 +5961,7 @@ Configuration: sensors . . . . . . . $enable_sensors serial . . . . . . . $enable_serial sigrok . . . . . . . $enable_sigrok + smart . . . . . . . . $enable_smart snmp . . . . . . . . $enable_snmp statsd . . . . . . . $enable_statsd swap . . . . . . . . $enable_swap diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index 19305924..23fbae24 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -47,6 +47,7 @@ %{?el6:%global _has_libmodbus 1} %{?el6:%global _has_libudev 1} %{?el6:%global _has_iproute 1} +%{?el6:%global _has_atasmart 1} %{?el7:%global _has_libyajl 1} %{?el7:%global _has_recent_libpcap 1} @@ -59,6 +60,7 @@ %{?el7:%global _has_varnish4 1} %{?el7:%global _has_broken_libmemcached 1} %{?el7:%global _has_iproute 1} +%{?el7:%global _has_atasmart 1} # plugins enabled by default %define with_aggregation 0%{!?_without_aggregation:1} @@ -120,6 +122,7 @@ %define with_numa 0%{!?_without_numa:1} %define with_nut 0%{!?_without_nut:1} %define with_olsrd 0%{!?_without_olsrd:1} +%define with_openldap 0%{!?_without_openldap:1} %define with_openvpn 0%{!?_without_openvpn:1} %define with_perl 0%{!?_without_perl:1} %define with_pinba 0%{!?_without_pinba:1} @@ -133,6 +136,7 @@ %define with_rrdtool 0%{!?_without_rrdtool:1} %define with_sensors 0%{!?_without_sensors:1} %define with_serial 0%{!?_without_serial:1} +%define with_smart 0%{!?_without_smart:0%{?_has_atasmart}} %define with_snmp 0%{!?_without_snmp:1} %define with_statsd 0%{!?_without_statsd:1} %define with_swap 0%{!?_without_swap:1} @@ -533,6 +537,16 @@ BuildRequires: nut-devel This plugin for collectd provides Network UPS Tools support. %endif +%if %{with_openldap} +%package openldap +Summary: Openldap plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: openldap-devel +%description openldap +This plugin reads monitoring information from OpenLDAP's cn=Monitor subtree. +%endif + %if %{with_perl} %package perl Summary: Perl plugin for collectd @@ -650,6 +664,17 @@ measurements fed to collectd. This includes multimeters, sound level meters, thermometers, and much more. %endif +%if %{with_smart} +%package smart +Summary: SMART plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: libatasmart-devel +%description smart +Collect SMART statistics, notably load cycle count, temperature and bad +sectors. +%endif + %if %{with_snmp} %package snmp Summary: SNMP plugin for collectd @@ -1164,6 +1189,12 @@ Development files for libcollectdclient %define _with_onewire --disable-onewire %endif +%if %{with_openldap} +%define _with_openldap --enable-openldap +%else +%define _with_openldap --disable-openldap +%endif + %if %{with_openvpn} %define _with_openvpn --enable-openvpn %else @@ -1276,6 +1307,12 @@ Development files for libcollectdclient %define _with_sigrok --disable-sigrok %endif +%if %{with_smart} +%define _with_smart --enable-smart +%else +%define _with_smart --disable-smart +%endif + %if %{with_snmp} %define _with_snmp --enable-snmp %else @@ -1529,6 +1566,7 @@ Development files for libcollectdclient %{?_with_notify_email} \ %{?_with_nut} \ %{?_with_onewire} \ + %{?_with_openldap} \ %{?_with_oracle} \ %{?_with_perl} \ %{?_with_pf} \ @@ -1542,6 +1580,7 @@ Development files for libcollectdclient %{?_with_rrdtool} \ %{?_with_sensors} \ %{?_with_sigrok} \ + %{?_with_smart} \ %{?_with_snmp} \ %{?_with_tape} \ %{?_with_tokyotyrant} \ @@ -2043,6 +2082,11 @@ fi %{_libdir}/%{name}/nut.so %endif +%if %{with_openldap} +%files openldap +%{_libdir}/%{name}/openldap.so +%endif + %if %{with_perl} %files perl %doc perl-examples/* @@ -2100,6 +2144,11 @@ fi %{_libdir}/%{name}/sigrok.so %endif +%if %{with_smart} +%files smart +%{_libdir}/%{name}/smart.so +%endif + %if %{with_snmp} %files snmp %{_mandir}/man5/collectd-snmp.5* @@ -2145,7 +2194,7 @@ fi %changelog # * TODO 5.5.0-1 # - New upstream version -# - New plugins enabled by default: drbd, log_logstash, write_tsdb +# - New plugins enabled by default: drbd, log_logstash, write_tsdb, smart, openldap # - New plugins disabled by default: barometer, write_kafka # - Enable zfs_arc, now supported on Linux # - Install disk plugin in an dedicated package, as it depends on libudev diff --git a/contrib/upstart.collectd.conf b/contrib/upstart.collectd.conf index 1c7fd9c7..c175ee68 100644 --- a/contrib/upstart.collectd.conf +++ b/contrib/upstart.collectd.conf @@ -32,7 +32,7 @@ stop on runlevel [!2345] env DAEMON=/usr/sbin/collectd # Tell upstart to watch for forking when tracking the pid for us. -expect fork +expect stop # prevent thrashing - 10 restarts in 5 seconds respawn diff --git a/src/Makefile.am b/src/Makefile.am index b8aab9a6..04c77a3b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -728,6 +728,14 @@ onewire_la_LIBADD = $(BUILD_WITH_LIBOWCAPI_LIBS) onewire_la_LDFLAGS = $(PLUGIN_LDFLAGS) endif +if BUILD_PLUGIN_OPENLDAP +pkglib_LTLIBRARIES += openldap.la +openldap_la_SOURCES = openldap.c +openldap_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBLDAP_LDFLAGS) +openldap_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBLDAP_CPPFLAGS) +openldap_la_LIBADD = -lldap +endif + if BUILD_PLUGIN_OPENVPN pkglib_LTLIBRARIES += openvpn.la openvpn_la_SOURCES = openvpn.c @@ -885,6 +893,17 @@ sigrok_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBSIGROK_LDFLAGS) sigrok_la_LIBADD = -lsigrok endif +if BUILD_PLUGIN_SMART +if BUILD_WITH_LIBUDEV +pkglib_LTLIBRARIES += smart.la +smart_la_SOURCES = smart.c \ + utils_ignorelist.c utils_ignorelist.h +smart_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBATASMART_CPPFLAGS) +smart_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBATASMART_LDFLAGS) +smart_la_LIBADD = $(BUILD_WITH_LIBATASMART_LIBS) -ludev +endif +endif + if BUILD_PLUGIN_SNMP pkglib_LTLIBRARIES += snmp.la snmp_la_SOURCES = snmp.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index fabf6344..84410e02 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -150,6 +150,7 @@ #@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut #@BUILD_PLUGIN_OLSRD_TRUE@LoadPlugin olsrd #@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire +#@BUILD_PLUGIN_OPENLDAP_TRUE@LoadPlugin openldap #@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn #@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle #@BUILD_PLUGIN_PERL_TRUE@ @@ -171,6 +172,7 @@ #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors #@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial #@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok +#@BUILD_PLUGIN_SMART_TRUE@LoadPlugin smart #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp #@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap @@ -767,6 +769,17 @@ # IgnoreSelected false # +# +# +# URL "ldap://localhost:389" +# StartTLS false +# VerifyHost true +# CACert "/path/to/ca.crt" +# Timeout -1 +# Version 3 +# +# + # # StatusFile "/etc/openvpn/openvpn-status.log" # ImprovedNamingSchema false @@ -971,6 +984,11 @@ # # +# +# Disk "/^[hs]d[a-f][0-9]?$/" +# IgnoreSelected false +# + # # # Type "voltage" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index da2030dd..69c922b0 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -9,17 +9,17 @@ collectd.conf - Configuration for the system statistics collection daemon B Interval 3600 ValuesPercentage true - + LoadPlugin ping Host "example.org" @@ -4245,6 +4245,70 @@ short: If it works for you: Great! But keep in mind that the config I change, though this is unlikely. Oh, and if you want to help improving this plugin, just send a short notice to the mailing list. ThanksE:) +=head2 Plugin C + +To use the C plugin you first need to configure the I +server correctly. The backend database C needs to be loaded and +working. See slapd-monitor(5) for the details. + +The configuration of the C plugin consists of one or more B +blocks. Each block requires one string argument as the instance name. For +example: + + + + URL "ldap://localhost/" + + + URL "ldaps://localhost/" + + + +The instance name will be used as the I. To emulate the old +(versionE4) behavior, you can use an empty string (""). In order for the +plugin to work correctly, each instance name must be unique. This is not +enforced by the plugin and it is your responsibility to ensure it is. + +The following options are accepted within each B block: + +=over 4 + +=item B I + +Sets the URL to use to connect to the I server. This option is +I. + +=item B B + +Defines whether TLS must be used when connecting to the I server. +Disabled by default. + +=item B B + +Enables or disables peer host name verification. If enabled, the plugin checks +if the C or a C field of the SSL +certificate matches the host name provided by the B option. If this +identity check fails, the connection is aborted. Enabled by default. + +=item B I + +File that holds one or more SSL certificates. If you want to use TLS/SSL you +may possibly need this option. What CA certificates are checked by default +depends on the distribution you use and can be changed with the usual ldap +client configuration mechanisms. See ldap.conf(5) for the details. + +=item B I + +Sets the timeout value for ldap operations. Defaults to B<-1> which results in +an infinite timeout. + +=item B I + +An integer which sets the LDAP protocol version number to use when connecting +to the I server. Defaults to B<3> for using I. + +=back + =head2 Plugin C The OpenVPN plugin reads a status file maintained by OpenVPN and gathers @@ -5639,6 +5703,40 @@ measurements are discarded. =back +=head2 Plugin C + +The C plugin collects SMART information from physical +disks. Values collectd include temperature, power cycle count, poweron +time and bad sectors. Also, all SMART attributes are collected along +with the normalized current value, the worst value, the threshold and +a human readable value. + +Using the following two options you can ignore some disks or configure the +collection only of specific disks. + +=over 4 + +=item B I + +Select the disk I. Whether it is collected or ignored depends on the +B setting, see below. As with other plugins that use the +daemon's ignorelist functionality, a string that starts and ends with a slash +is interpreted as a regular expression. Examples: + + Disk "sdd" + Disk "/hda[34]/" + +=item B B|B + +Sets whether selected disks, i.Ee. the ones matches by any of the B +statements, are ignored or if all other disks are ignored. The behavior +(hopefully) is intuitive: If no B option is configured, all disks are +collected. If at least one B option is given and no B or +set to B, B matching disks will be collected. If B +is set to B, all disks are collected B the ones matched. + +=back + =head2 Plugin C Since the configuration of the C is a little more complicated than @@ -7010,6 +7108,20 @@ Service name or port number to connect to. Defaults to C<5555>. Specify the protocol to use when communicating with I. Defaults to B. +=item B B|B + +If set to B and B is set to B, +events will be batched in memory and flushed at +regular intervals or when B is exceeded. + +Notifications are not batched and sent as soon as possible. + +Defaults to false + +=item B I + +Maximum payload size for a riemann packet. Defaults to 8192 + =item B B|B If set to B (the default), convert counter values to rates. If set to diff --git a/src/daemon/collectd.c b/src/daemon/collectd.c index 8c4d6e66..9cba9132 100644 --- a/src/daemon/collectd.c +++ b/src/daemon/collectd.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -191,7 +192,7 @@ static int change_basedir (const char *orig_dir) sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } - + dirlen = strlen (dir); while ((dirlen > 0) && (dir[dirlen - 1] == '/')) dir[--dirlen] = '\0'; @@ -270,7 +271,7 @@ static void update_kstat (void) static void exit_usage (int status) { printf ("Usage: "PACKAGE" [OPTIONS]\n\n" - + "Available options:\n" " General:\n" " -C Configuration file.\n" @@ -413,6 +414,72 @@ static int pidfile_remove (void) } /* static int pidfile_remove (const char *file) */ #endif /* COLLECT_DAEMON */ +int notify_upstart (void) +{ + const char *upstart_job = getenv("UPSTART_JOB"); + + if (upstart_job == NULL) + return 0; + + if (strcmp(upstart_job, "collectd") != 0) + return 0; + + WARNING ("supervised by upstart, will stop to signal readyness"); + raise(SIGSTOP); + unsetenv("UPSTART_JOB"); + + return 1; +} + +int notify_systemd (void) +{ + int fd = -1; + const char *notifysocket = getenv("NOTIFY_SOCKET"); + struct sockaddr_un su; + struct iovec iov; + struct msghdr hdr; + + if (notifysocket == NULL) + return 0; + + if ((strchr("@/", notifysocket[0])) == NULL || + strlen(notifysocket) < 2) + return 0; + + WARNING ("supervised by systemd, will signal readyness"); + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + WARNING ("cannot contact systemd socket %s", notifysocket); + return 0; + } + + bzero(&su, sizeof(su)); + su.sun_family = AF_UNIX; + sstrncpy (su.sun_path, notifysocket, sizeof(su.sun_path)); + + if (notifysocket[0] == '@') + su.sun_path[0] = 0; + + bzero(&iov, sizeof(iov)); + iov.iov_base = "READY=1"; + iov.iov_len = strlen("READY=1"); + + bzero(&hdr, sizeof(hdr)); + hdr.msg_name = &su; + hdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + + strlen(notifysocket); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + unsetenv("NOTIFY_SOCKET"); + if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) { + WARNING ("cannot send notification to systemd"); + close(fd); + return 0; + } + close(fd); + return 1; +} + int main (int argc, char **argv) { struct sigaction sig_int_action; @@ -529,7 +596,11 @@ int main (int argc, char **argv) sig_chld_action.sa_handler = SIG_IGN; sigaction (SIGCHLD, &sig_chld_action, NULL); - if (daemonize) + /* + * Only daemonize if we're not being supervised + * by upstart or systemd. + */ + if (daemonize && notify_upstart() == 0 && notify_systemd() == 0) { if ((pid = fork ()) == -1) { diff --git a/src/openldap.c b/src/openldap.c new file mode 100644 index 00000000..212310b6 --- /dev/null +++ b/src/openldap.c @@ -0,0 +1,682 @@ +/** + * collectd - src/openldap.c + * Copyright (C) 2011 Kimo Rosenbaum + * Copyright (C) 2014 Marc Fournier + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kimo Rosenbaum + * Marc Fournier + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#include +#include + +struct ldap_s /* {{{ */ +{ + char *name; + + char *cacert; + char *host; + int state; + _Bool starttls; + int timeout; + char *url; + _Bool verifyhost; + int version; + + LDAP *ld; +}; +typedef struct ldap_s ldap_t; /* }}} */ + +static void ldap_free (ldap_t *st) /* {{{ */ +{ + if (st == NULL) + return; + + sfree (st->cacert); + sfree (st->host); + sfree (st->name); + sfree (st->url); + if (st->ld) + ldap_memfree (st->ld); + sfree (st); +} /* }}} void ldap_free */ + +/* initialize ldap for each host */ +static int ldap_init_host (ldap_t *st) /* {{{ */ +{ + LDAP *ld; + int rc; + rc = ldap_initialize (&ld, st->url); + if (rc != LDAP_SUCCESS) + { + ERROR ("openldap plugin: ldap_initialize failed: %s", + ldap_err2string (rc)); + st->state = 0; + ldap_unbind_ext_s (ld, NULL, NULL); + return (-1); + } + + st->ld = ld; + + ldap_set_option (st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version); + + ldap_set_option (st->ld, LDAP_OPT_TIMEOUT, + &(const struct timeval){st->timeout, 0}); + + if (st->cacert != NULL) + ldap_set_option (st->ld, LDAP_OPT_X_TLS_CACERTFILE, st->cacert); + + if (st->verifyhost == 0) + { + int never = LDAP_OPT_X_TLS_NEVER; + ldap_set_option (st->ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &never); + } + + if (st->starttls != 0) + { + rc = ldap_start_tls_s (ld, NULL, NULL); + if (rc != LDAP_SUCCESS) + { + ERROR ("openldap plugin: Failed to start tls on %s: %s", + st->url, ldap_err2string (rc)); + st->state = 0; + ldap_unbind_ext_s (st->ld, NULL, NULL); + return (-1); + } + } + + struct berval cred; + cred.bv_val = ""; + cred.bv_len = 0; + + rc = ldap_sasl_bind_s (st->ld, NULL, NULL, &cred, NULL, NULL, NULL); + if (rc != LDAP_SUCCESS) + { + ERROR ("openldap plugin: Failed to bind to %s: %s", + st->url, ldap_err2string (rc)); + st->state = 0; + ldap_unbind_ext_s (st->ld, NULL, NULL); + return (-1); + } + else + { + DEBUG ("openldap plugin: Successfully connected to %s", + st->url); + st->state = 1; + return (0); + } +} /* }}} static ldap_init_host */ + +static void ldap_submit_value (const char *type, const char *type_instance, /* {{{ */ + value_t value, ldap_t *st) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &value; + vl.values_len = 1; + + if ((st->host == NULL) + || (strcmp ("", st->host) == 0) + || (strcmp ("localhost", st->host) == 0)) + { + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + } + else + { + sstrncpy (vl.host, st->host, sizeof (vl.host)); + } + + sstrncpy (vl.plugin, "openldap", sizeof (vl.plugin)); + if (st->name != NULL) + sstrncpy (vl.plugin_instance, st->name, + sizeof (vl.plugin_instance)); + + sstrncpy (vl.type, type, sizeof (vl.type)); + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* }}} void ldap_submit_value */ + +static void ldap_submit_derive (const char *type, const char *type_instance, /* {{{ */ + derive_t d, ldap_t *st) +{ + value_t v; + v.derive = d; + ldap_submit_value (type, type_instance, v, st); +} /* }}} void ldap_submit_derive */ + +static void ldap_submit_gauge (const char *type, const char *type_instance, /* {{{ */ + gauge_t g, ldap_t *st) +{ + value_t v; + v.gauge = g; + ldap_submit_value (type, type_instance, v, st); +} /* }}} void ldap_submit_gauge */ + +static int ldap_read_host (user_data_t *ud) /* {{{ */ +{ + ldap_t *st; + LDAPMessage *e, *result; + char *dn; + int rc; + int status; + + char *attrs[9] = { "monitorCounter", + "monitorOpCompleted", + "monitorOpInitiated", + "monitoredInfo", + "olmBDBEntryCache", + "olmBDBDNCache", + "olmBDBIDLCache", + "namingContexts", + NULL }; + + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("openldap plugin: ldap_read_host: Invalid user data."); + return (-1); + } + + st = (ldap_t *) ud->data; + + status = ldap_init_host (st); + if (status != 0) + return (-1); + + rc = ldap_search_ext_s (st->ld, "cn=Monitor", LDAP_SCOPE_SUBTREE, + "(|(!(cn=* *))(cn=Database*))", attrs, 0, + NULL, NULL, NULL, 0, &result); + + if (rc != LDAP_SUCCESS) + { + ERROR ("openldap plugin: Failed to execute search: %s", + ldap_err2string (rc)); + ldap_msgfree (result); + ldap_unbind_ext_s (st->ld, NULL, NULL); + return (-1); + } + + for (e = ldap_first_entry (st->ld, result); e != NULL; + e = ldap_next_entry (st->ld, e)) + { + if ((dn = ldap_get_dn (st->ld, e)) != NULL) + { + unsigned long long counter = 0; + unsigned long long opc = 0; + unsigned long long opi = 0; + unsigned long long info = 0; + + struct berval counter_data; + struct berval opc_data; + struct berval opi_data; + struct berval info_data; + struct berval olmbdb_data; + struct berval nc_data; + + struct berval **counter_list; + struct berval **opc_list; + struct berval **opi_list; + struct berval **info_list; + struct berval **olmbdb_list; + struct berval **nc_list; + + if ((counter_list = ldap_get_values_len (st->ld, e, + "monitorCounter")) != NULL) + { + counter_data = *counter_list[0]; + counter = atoll (counter_data.bv_val); + } + + if ((opc_list = ldap_get_values_len (st->ld, e, + "monitorOpCompleted")) != NULL) + { + opc_data = *opc_list[0]; + opc = atoll (opc_data.bv_val); + } + + if ((opi_list = ldap_get_values_len (st->ld, e, + "monitorOpInitiated")) != NULL) + { + opi_data = *opi_list[0]; + opi = atoll (opi_data.bv_val); + } + + if ((info_list = ldap_get_values_len (st->ld, e, + "monitoredInfo")) != NULL) + { + info_data = *info_list[0]; + info = atoll (info_data.bv_val); + } + + if (strcmp (dn, "cn=Total,cn=Connections,cn=Monitor") + == 0) + { + ldap_submit_derive ("total_connections", NULL, + counter, st); + } + else if (strcmp (dn, + "cn=Current,cn=Connections,cn=Monitor") + == 0) + { + ldap_submit_gauge ("current_connections", NULL, + counter, st); + } + else if (strcmp (dn, + "cn=Operations,cn=Monitor") == 0) + { + ldap_submit_derive ("operations", + "completed", opc, st); + ldap_submit_derive ("operations", + "initiated", opi, st); + } + else if (strcmp (dn, + "cn=Bind,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "bind-completed", opc, st); + ldap_submit_derive ("operations", + "bind-initiated", opi, st); + } + else if (strcmp (dn, + "cn=UnBind,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "unbind-completed", opc, st); + ldap_submit_derive ("operations", + "unbind-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Search,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "search-completed", opc, st); + ldap_submit_derive ("operations", + "search-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Compare,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "compare-completed", opc, st); + ldap_submit_derive ("operations", + "compare-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Modify,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "modify-completed", opc, st); + ldap_submit_derive ("operations", + "modify-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Modrdn,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "modrdn-completed", opc, st); + ldap_submit_derive ("operations", + "modrdn-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Add,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "add-completed", opc, st); + ldap_submit_derive ("operations", + "add-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Delete,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "delete-completed", opc, st); + ldap_submit_derive ("operations", + "delete-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Abandon,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "abandon-completed", opc, st); + ldap_submit_derive ("operations", + "abandon-initiated", opi, st); + } + else if (strcmp (dn, + "cn=Extended,cn=Operations,cn=Monitor") + == 0) + { + ldap_submit_derive ("operations", + "extended-completed", opc, st); + ldap_submit_derive ("operations", + "extended-initiated", opi, st); + } + else if ((strncmp (dn, "cn=Database", 11) == 0) + && ((nc_list = ldap_get_values_len + (st->ld, e, "namingContexts")) != NULL)) + { + nc_data = *nc_list[0]; + char typeinst[DATA_MAX_NAME_LEN]; + + if ((olmbdb_list = ldap_get_values_len (st->ld, e, + "olmBDBEntryCache")) != NULL) + { + olmbdb_data = *olmbdb_list[0]; + ssnprintf (typeinst, sizeof (typeinst), + "bdbentrycache-%s", nc_data.bv_val); + ldap_submit_gauge ("cache_size", typeinst, + atoll (olmbdb_data.bv_val), st); + ldap_value_free_len (olmbdb_list); + } + + if ((olmbdb_list = ldap_get_values_len (st->ld, e, + "olmBDBDNCache")) != NULL) + { + olmbdb_data = *olmbdb_list[0]; + ssnprintf (typeinst, sizeof (typeinst), + "bdbdncache-%s", nc_data.bv_val); + ldap_submit_gauge ("cache_size", typeinst, + atoll (olmbdb_data.bv_val), st); + ldap_value_free_len (olmbdb_list); + } + + if ((olmbdb_list = ldap_get_values_len (st->ld, e, + "olmBDBIDLCache")) != NULL) + { + olmbdb_data = *olmbdb_list[0]; + ssnprintf (typeinst, sizeof (typeinst), + "bdbidlcache-%s", nc_data.bv_val); + ldap_submit_gauge ("cache_size", typeinst, + atoll (olmbdb_data.bv_val), st); + ldap_value_free_len (olmbdb_list); + } + + ldap_value_free_len (nc_list); + } + else if (strcmp (dn, + "cn=Bytes,cn=Statistics,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "statistics-bytes", + counter, st); + } + else if (strcmp (dn, + "cn=PDU,cn=Statistics,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "statistics-pdu", + counter, st); + } + else if (strcmp (dn, + "cn=Entries,cn=Statistics,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "statistics-entries", + counter, st); + } + else if (strcmp (dn, + "cn=Referrals,cn=Statistics,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "statistics-referrals", + counter, st); + } + else if (strcmp (dn, + "cn=Open,cn=Threads,cn=Monitor") + == 0) + { + ldap_submit_gauge ("threads", "threads-open", + info, st); + } + else if (strcmp (dn, + "cn=Starting,cn=Threads,cn=Monitor") + == 0) + { + ldap_submit_gauge ("threads", "threads-starting", + info, st); + } + else if (strcmp (dn, + "cn=Active,cn=Threads,cn=Monitor") + == 0) + { + ldap_submit_gauge ("threads", "threads-active", + info, st); + } + else if (strcmp (dn, + "cn=Pending,cn=Threads,cn=Monitor") + == 0) + { + ldap_submit_gauge ("threads", "threads-pending", + info, st); + } + else if (strcmp (dn, + "cn=Backload,cn=Threads,cn=Monitor") + == 0) + { + ldap_submit_gauge ("threads", "threads-backload", + info, st); + } + else if (strcmp (dn, + "cn=Read,cn=Waiters,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "waiters-read", + counter, st); + } + else if (strcmp (dn, + "cn=Write,cn=Waiters,cn=Monitor") + == 0) + { + ldap_submit_derive ("derive", "waiters-write", + counter, st); + } + + ldap_value_free_len (counter_list); + ldap_value_free_len (opc_list); + ldap_value_free_len (opi_list); + ldap_value_free_len (info_list); + } + + ldap_memfree (dn); + } + + ldap_msgfree (result); + ldap_unbind_ext_s (st->ld, NULL, NULL); + return (0); +} /* }}} int ldap_read_host */ + +/* Configuration handling functions {{{ + * + * + * + * URL "ldap://localhost" + * ... + * + * + */ + +static int ldap_config_add (oconfig_item_t *ci) /* {{{ */ +{ + ldap_t *st; + int i; + int status; + + st = malloc (sizeof (*st)); + if (st == NULL) + { + ERROR ("openldap plugin: malloc failed."); + return (-1); + } + memset (st, 0, sizeof (*st)); + + status = cf_util_get_string (ci, &st->name); + if (status != 0) + { + sfree (st); + return (status); + } + + st->starttls = 0; + st->timeout = -1; + st->verifyhost = 1; + st->version = LDAP_VERSION3; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("CACert", child->key) == 0) + status = cf_util_get_string (child, &st->cacert); + else if (strcasecmp ("StartTLS", child->key) == 0) + status = cf_util_get_boolean (child, &st->starttls); + else if (strcasecmp ("Timeout", child->key) == 0) + status = cf_util_get_int (child, &st->timeout); + else if (strcasecmp ("URL", child->key) == 0) + status = cf_util_get_string (child, &st->url); + else if (strcasecmp ("VerifyHost", child->key) == 0) + status = cf_util_get_boolean (child, &st->verifyhost); + else if (strcasecmp ("Version", child->key) == 0) + status = cf_util_get_int (child, &st->version); + else + { + WARNING ("openldap plugin: Option `%s' not allowed here.", + child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check if struct is complete.. */ + if ((status == 0) && (st->url == NULL)) + { + ERROR ("openldap plugin: Instance `%s': " + "No URL has been configured.", + st->name); + status = -1; + } + + /* Check if URL is valid */ + if ((status == 0) && (st->url != NULL)) + { + LDAPURLDesc *ludpp; + int rc; + + if ((rc = ldap_url_parse (st->url, &ludpp)) != 0) + { + ERROR ("openldap plugin: Instance `%s': " + "Invalid URL: `%s'", + st->name, st->url); + status = -1; + } + else + { + st->host = strdup (ludpp->lud_host); + } + + ldap_free_urldesc (ludpp); + } + + if (status == 0) + { + user_data_t ud; + char callback_name[3*DATA_MAX_NAME_LEN]; + + memset (&ud, 0, sizeof (ud)); + ud.data = st; + + memset (callback_name, 0, sizeof (callback_name)); + ssnprintf (callback_name, sizeof (callback_name), + "openldap/%s/%s", + (st->host != NULL) ? st->host : hostname_g, + (st->name != NULL) ? st->name : "default"), + + status = plugin_register_complex_read (/* group = */ NULL, + /* name = */ callback_name, + /* callback = */ ldap_read_host, + /* interval = */ NULL, + /* user_data = */ &ud); + } + + if (status != 0) + { + ldap_free (st); + return (-1); + } + + return (0); +} /* }}} int ldap_config_add */ + +static int ldap_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + int status = 0; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Instance", child->key) == 0) + ldap_config_add (child); + else + WARNING ("openldap plugin: The configuration option " + "\"%s\" is not allowed here. Did you " + "forget to add an block " + "around the configuration?", + child->key); + } /* for (ci->children) */ + + return (status); +} /* }}} int ldap_config */ + +/* }}} End of configuration handling functions */ + +static int ldap_init (void) /* {{{ */ +{ + /* Initialize LDAP library while still single-threaded as recommended in + * ldap_initialize(3) */ + int debug_level; + ldap_get_option (NULL, LDAP_OPT_DEBUG_LEVEL, &debug_level); + return (0); +} /* }}} int ldap_init */ + +void module_register (void) /* {{{ */ +{ + plugin_register_complex_config ("openldap", ldap_config); + plugin_register_init ("openldap", ldap_init); +} /* }}} void module_register */ diff --git a/src/smart.c b/src/smart.c new file mode 100644 index 00000000..3b113bd3 --- /dev/null +++ b/src/smart.c @@ -0,0 +1,269 @@ +/** + * collectd - src/smart.c + * Copyright (C) 2014 Vincent Bernat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Vincent Bernat + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_ignorelist.h" + +#include +#include + +static const char *config_keys[] = +{ + "Disk", + "IgnoreSelected" +}; + +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static ignorelist_t *ignorelist = NULL; + +static int smart_config (const char *key, const char *value) +{ + if (ignorelist == NULL) + ignorelist = ignorelist_create (/* invert = */ 1); + if (ignorelist == NULL) + return (1); + + if (strcasecmp ("Disk", key) == 0) + { + ignorelist_add (ignorelist, value); + } + else if (strcasecmp ("IgnoreSelected", key) == 0) + { + int invert = 1; + if (IS_TRUE (value)) + invert = 0; + ignorelist_set_invert (ignorelist, invert); + } + else + { + return (-1); + } + + return (0); +} /* int smart_config */ + +static void smart_submit (const char *dev, char *type, char *type_inst, double value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = value; + + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "smart", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, type, sizeof (vl.type)); + sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +static void smart_handle_disk_attribute(SkDisk *d, const SkSmartAttributeParsedData *a, + void* userdata) +{ + const char *dev = userdata; + value_t values[4]; + value_list_t vl = VALUE_LIST_INIT; + + if (!a->current_value_valid || !a->worst_value_valid) return; + values[0].gauge = a->current_value; + values[1].gauge = a->worst_value; + values[2].gauge = a->threshold_valid?a->threshold:0; + values[3].gauge = a->pretty_value; + + vl.values = values; + vl.values_len = 4; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "smart", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, "smart_attribute", sizeof (vl.type)); + sstrncpy (vl.type_instance, a->name, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); + + if (a->threshold_valid && a->current_value <= a->threshold) + { + notification_t notif = { NOTIF_WARNING, + cdtime (), + "", + "", + "smart", "", + "smart_attribute", + "", + NULL }; + sstrncpy (notif.host, hostname_g, sizeof (notif.host)); + sstrncpy (notif.plugin_instance, dev, sizeof (notif.plugin_instance)); + sstrncpy (notif.type_instance, a->name, sizeof (notif.type_instance)); + ssnprintf (notif.message, sizeof (notif.message), + "attribute %s is below allowed threshold (%d < %d)", + a->name, a->current_value, a->threshold); + plugin_dispatch_notification (¬if); + } +} + +static void smart_handle_disk (const char *dev) +{ + SkDisk *d = NULL; + SkBool awake = FALSE; + SkBool available = FALSE; + const char *shortname; + const SkSmartParsedData *spd; + uint64_t poweron, powercycles, badsectors, temperature; + + shortname = strrchr(dev, '/'); + if (!shortname) return; + shortname++; + if (ignorelist_match (ignorelist, shortname) != 0) { + DEBUG ("smart plugin: ignoring %s.", dev); + return; + } + + DEBUG ("smart plugin: checking SMART status of %s.", + dev); + + if (sk_disk_open (dev, &d) < 0) + { + ERROR ("smart plugin: unable to open %s.", dev); + return; + } + if (sk_disk_identify_is_available (d, &available) < 0 || !available) + { + DEBUG ("smart plugin: disk %s cannot be identified.", dev); + goto end; + } + if (sk_disk_smart_is_available (d, &available) < 0 || !available) + { + DEBUG ("smart plugin: disk %s has no SMART support.", dev); + goto end; + } + if (sk_disk_check_sleep_mode (d, &awake) < 0 || !awake) + { + DEBUG ("smart plugin: disk %s is sleeping.", dev); + goto end; + } + if (sk_disk_smart_read_data (d) < 0) + { + ERROR ("smart plugin: unable to get SMART data for disk %s.", dev); + goto end; + } + if (sk_disk_smart_parse (d, &spd) < 0) + { + ERROR ("smart plugin: unable to parse SMART data for disk %s.", dev); + goto end; + } + + /* Get some specific values */ + if (sk_disk_smart_get_power_on (d, &poweron) < 0) + { + WARNING ("smart plugin: unable to get milliseconds since power on for %s.", + dev); + } + else + smart_submit (shortname, "smart_poweron", "", poweron / 1000.); + + if (sk_disk_smart_get_power_cycle (d, &powercycles) < 0) + { + WARNING ("smart plugin: unable to get number of power cycles for %s.", + dev); + } + else + smart_submit (shortname, "smart_powercycles", "", powercycles); + + if (sk_disk_smart_get_bad (d, &badsectors) < 0) + { + WARNING ("smart plugin: unable to get number of bad sectors for %s.", + dev); + } + else + smart_submit (shortname, "smart_badsectors", "", badsectors); + + if (sk_disk_smart_get_temperature (d, &temperature) < 0) + { + WARNING ("smart plugin: unable to get temperature for %s.", + dev); + } + else + smart_submit (shortname, "smart_temperature", "", temperature / 1000. - 273.15); + + /* Grab all attributes */ + if (sk_disk_smart_parse_attributes(d, smart_handle_disk_attribute, + (char *)shortname) < 0) + { + ERROR ("smart plugin: unable to handle SMART attributes for %s.", + dev); + } + +end: + sk_disk_free(d); +} + +static int smart_read (void) +{ + struct udev *handle_udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev; + + /* Use udev to get a list of disks */ + handle_udev = udev_new(); + if (!handle_udev) + { + ERROR ("smart plugin: unable to initialize udev."); + return (-1); + } + enumerate = udev_enumerate_new (handle_udev); + udev_enumerate_add_match_subsystem (enumerate, "block"); + udev_enumerate_add_match_property (enumerate, "DEVTYPE", "disk"); + udev_enumerate_scan_devices (enumerate); + devices = udev_enumerate_get_list_entry (enumerate); + udev_list_entry_foreach (dev_list_entry, devices) + { + const char *path, *devpath; + path = udev_list_entry_get_name (dev_list_entry); + dev = udev_device_new_from_syspath (handle_udev, path); + devpath = udev_device_get_devnode (dev); + + /* Query status with libatasmart */ + smart_handle_disk (devpath); + } + + udev_enumerate_unref (enumerate); + udev_unref (handle_udev); + + return (0); +} /* int smart_read */ + +void module_register (void) +{ + plugin_register_config ("smart", smart_config, + config_keys, config_keys_num); + plugin_register_read ("smart", smart_read); +} /* void module_register */ diff --git a/src/types.db b/src/types.db index 64137b07..ec34bd43 100644 --- a/src/types.db +++ b/src/types.db @@ -168,6 +168,11 @@ serial_octets rx:DERIVE:0:U, tx:DERIVE:0:U signal_noise value:GAUGE:U:0 signal_power value:GAUGE:U:0 signal_quality value:GAUGE:0:U +smart_poweron value:GAUGE:0:U +smart_powercycles value:GAUGE:0:U +smart_badsectors value:GAUGE:0:U +smart_temperature value:GAUGE:-300:300 +smart_attribute current:GAUGE:0:255, worst:GAUGE:0:255, threshold:GAUGE:0:255, pretty:GAUGE:0:U snr value:GAUGE:0:U spam_check value:GAUGE:0:U spam_score value:GAUGE:U:U diff --git a/src/write_riemann.c b/src/write_riemann.c index c3740e1d..0a8df6fe 100644 --- a/src/write_riemann.c +++ b/src/write_riemann.c @@ -43,26 +43,30 @@ #define RIEMANN_HOST "localhost" #define RIEMANN_PORT "5555" #define RIEMANN_TTL_FACTOR 2.0 +#define RIEMANN_BATCH_MAX 8192 int write_riemann_threshold_check(const data_set_t *, const value_list_t *, int *); struct riemann_host { char *name; char *event_service_prefix; -#define F_CONNECT 0x01 +#define F_CONNECT 0x01 uint8_t flags; - pthread_mutex_t lock; - _Bool notifications; - _Bool check_thresholds; + pthread_mutex_t lock; + _Bool batch_mode; + _Bool notifications; + _Bool check_thresholds; _Bool store_rates; _Bool always_append_ds; char *node; char *service; _Bool use_tcp; - int s; + int s; double ttl_factor; - - int reference_count; + Msg *batch_msg; + cdtime_t batch_init; + int batch_max; + int reference_count; }; static char **riemann_tags; @@ -651,6 +655,103 @@ static Msg *riemann_value_list_to_protobuf (struct riemann_host const *host, /* return (msg); } /* }}} Msg *riemann_value_list_to_protobuf */ + +/* + * Always call while holding host->lock ! + */ +static int riemann_batch_flush_nolock (cdtime_t timeout, + struct riemann_host *host) +{ + cdtime_t now; + int status = 0; + + if (timeout > 0) { + now = cdtime (); + if ((host->batch_init + timeout) > now) + return status; + } + riemann_send_msg(host, host->batch_msg); + riemann_msg_protobuf_free(host->batch_msg); + + if (host->use_tcp && ((status = riemann_recv_ack(host)) != 0)) + riemann_disconnect (host); + + host->batch_init = cdtime(); + host->batch_msg = NULL; + return status; +} + +static int riemann_batch_flush (cdtime_t timeout, + const char *identifier __attribute__((unused)), + user_data_t *user_data) +{ + struct riemann_host *host; + int status; + + if (user_data == NULL) + return (-EINVAL); + + host = user_data->data; + pthread_mutex_lock (&host->lock); + status = riemann_batch_flush_nolock (timeout, host); + if (status != 0) + ERROR ("write_riemann plugin: riemann_send failed with status %i", + status); + + pthread_mutex_unlock(&host->lock); + return status; +} + +static int riemann_batch_add_value_list (struct riemann_host *host, /* {{{ */ + data_set_t const *ds, + value_list_t const *vl, + int *statuses) +{ + size_t i; + Event **events; + Msg *msg; + size_t len; + int ret; + + msg = riemann_value_list_to_protobuf (host, ds, vl, statuses); + if (msg == NULL) + return -1; + + pthread_mutex_lock(&host->lock); + + if (host->batch_msg == NULL) { + host->batch_msg = msg; + } else { + len = msg->n_events + host->batch_msg->n_events; + events = realloc(host->batch_msg->events, + (len * sizeof(*host->batch_msg->events))); + if (events == NULL) { + pthread_mutex_unlock(&host->lock); + ERROR ("write_riemann plugin: out of memory"); + riemann_msg_protobuf_free (msg); + return -1; + } + host->batch_msg->events = events; + + for (i = host->batch_msg->n_events; i < len; i++) + host->batch_msg->events[i] = msg->events[i - host->batch_msg->n_events]; + + host->batch_msg->n_events = len; + sfree (msg->events); + msg->n_events = 0; + sfree (msg); + } + + len = msg__get_packed_size(host->batch_msg); + ret = 0; + if (len >= host->batch_max) { + ret = riemann_batch_flush_nolock(0, host); + } + + pthread_mutex_unlock(&host->lock); + return ret; +} /* }}} Msg *riemann_batch_add_value_list */ + static int riemann_notification(const notification_t *n, user_data_t *ud) /* {{{ */ { int status; @@ -660,6 +761,9 @@ static int riemann_notification(const notification_t *n, user_data_t *ud) /* {{{ if (!host->notifications) return 0; + /* + * Never batch for notifications, send them ASAP + */ msg = riemann_notification_to_protobuf (host, n); if (msg == NULL) return (-1); @@ -677,23 +781,32 @@ static int riemann_write(const data_set_t *ds, /* {{{ */ const value_list_t *vl, user_data_t *ud) { - int status; + int status = 0; int statuses[vl->values_len]; struct riemann_host *host = ud->data; Msg *msg; if (host->check_thresholds) write_riemann_threshold_check(ds, vl, statuses); - msg = riemann_value_list_to_protobuf (host, ds, vl, statuses); - if (msg == NULL) - return (-1); - status = riemann_send (host, msg); - if (status != 0) - ERROR ("write_riemann plugin: riemann_send failed with status %i", - status); + if (host->use_tcp == 1 && host->batch_mode) { - riemann_msg_protobuf_free (msg); + riemann_batch_add_value_list (host, ds, vl, statuses); + + + } else { + + msg = riemann_value_list_to_protobuf (host, ds, vl, statuses); + if (msg == NULL) + return (-1); + + status = riemann_send (host, msg); + if (status != 0) + ERROR ("write_riemann plugin: riemann_send failed with status %i", + status); + + riemann_msg_protobuf_free (msg); + } return status; } /* }}} int riemann_write */ @@ -742,6 +855,9 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */ host->store_rates = 1; host->always_append_ds = 0; host->use_tcp = 0; + host->batch_mode = 0; + host->batch_max = RIEMANN_BATCH_MAX; /* typical MSS */ + host->batch_init = cdtime(); host->ttl_factor = RIEMANN_TTL_FACTOR; status = cf_util_get_string (ci, &host->name); @@ -775,6 +891,14 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */ status = cf_util_get_boolean(child, &host->check_thresholds); if (status != 0) break; + } else if (strcasecmp ("Batch", child->key) == 0) { + status = cf_util_get_boolean(child, &host->batch_mode); + if (status != 0) + break; + } else if (strcasecmp("BatchMaxSize", child->key) == 0) { + status = cf_util_get_int(child, &host->batch_max); + if (status != 0) + break; } else if (strcasecmp ("Port", child->key) == 0) { status = cf_util_get_service (child, &host->service); if (status != 0) { @@ -859,6 +983,11 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */ pthread_mutex_lock (&host->lock); status = plugin_register_write (callback_name, riemann_write, &ud); + + if (host->use_tcp == 1 && host->batch_mode) { + ud.free_func = NULL; + plugin_register_flush(callback_name, riemann_batch_flush, &ud); + } if (status != 0) WARNING ("write_riemann plugin: plugin_register_write (\"%s\") " "failed with status %i.",