Fabien Wernli <cpan at faxm0dem.org>
- Solaris improvements in the memory and interfaces plugin.
+Fabrice A. Marie <fabrice at kibinlabs.com>
+ - write_sensu plugin.
+
Flavio Stanchina <flavio at stanchina.net>
- mbmon plugin.
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
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).
- 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.
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
- 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:
[have_curlopt_username="yes"],
[have_curlopt_username="no"],
[[#include <curl/curl.h>]])
+ AC_CHECK_DECL(CURLOPT_TIMEOUT_MS,
+ [have_curlopt_timeout="yes"],
+ [have_curlopt_timeout="no"],
+ [[#include <curl/curl.h>]])
fi
fi
if test "x$with_libcurl" = "xyes"
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")
# }}}
plugin_entropy="yes"
plugin_fscache="yes"
plugin_interface="yes"
+ plugin_ipc="yes"
plugin_irq="yes"
plugin_load="yes"
plugin_lvm="yes"
if test "x$ac_system" = "xAIX"
then
plugin_tcpconns="yes"
+ plugin_ipc="yes"
fi
# FreeBSD
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])
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])
gmond . . . . . . . . $enable_gmond
hddtemp . . . . . . . $enable_hddtemp
interface . . . . . . $enable_interface
+ ipc . . . . . . . . . $enable_ipc
ipmi . . . . . . . . $enable_ipmi
iptables . . . . . . $enable_iptables
ipvs . . . . . . . . $enable_ipvs
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
%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}}
%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}
Summary: Statistics collection daemon for filling RRD files
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
%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
%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
%{?_with_gmond} \
%{?_with_hddtemp} \
%{?_with_interface} \
+ %{?_with_ipc} \
%{?_with_ipmi} \
%{?_with_iptables} \
%{?_with_ipvs} \
%{?_with_write_http} \
%{?_with_write_log} \
%{?_with_write_riemann} \
+ %{?_with_write_sensu} \
%{?_with_write_tsdb}
%if %{with_interface}
%{_libdir}/%{name}/interface.so
%endif
+%if %{with_ipc}
+%{_libdir}/%{name}/ipc.so
+%endif
%if %{with_ipvs}
%{_libdir}/%{name}/ipvs.so
%endif
%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
%changelog
# * TODO 5.5.0-1
# - New upstream version
-# - New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart, openldap, redis, write_redis, zookeeper, write_log
+# - New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart, openldap, redis, write_redis, zookeeper, write_log, write_sensu, ipc
# - 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
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
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
_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 */
sfree (st->user);
sfree (st->pass);
sfree (st->cacert);
+ sfree (st->ssl_ciphers);
sfree (st->server);
sfree (st->apache_buffer);
if (st->curl) {
}
memset (st, 0, sizeof (*st));
+ st->timeout = -1;
+
status = cf_util_get_string (ci, &st->name);
if (status != 0)
{
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.",
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 */
static char *verify_peer = NULL;
static char *verify_host = NULL;
static char *cacert = NULL;
+static char *timeout = NULL;
static CURL *curl = NULL;
"Password",
"VerifyPeer",
"VerifyHost",
- "CACert"
+ "CACert",
+ "Timeout",
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
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 */
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 */
#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
/** 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",
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 */
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. */
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.
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;
}
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,
}
-/*
+/**
* Convert raw adc values to real data using the sensor coefficients.
*
* @param adc_pressure adc pressure value to be converted
*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);
}
/**
* 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)
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;
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)
{
}
*temperature += (double)(data[4]) / 256.0;
- DEBUG ("barometer: MPL3115_read, temperature = %lf C", *temperature);
+ DEBUG ("barometer: MPL3115_read - temperature = %lf C", *temperature);
return 0;
}
/* 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",
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 ------------------------ */
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);
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:
break;
}
+ DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf hPa, method = %d, meanPressure = %lf hPa",
+ abs_pressure,
+ config_normalize,
+ mean);
+
return mean;
}
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;
}
{
DEBUG ("barometer: collectd_barometer_shutdown");
- if(!is_MPL3115)
+ if(sensor_type == Sensor_MPL115)
{
averaging_delete (&pressure_averaging);
averaging_delete (&temperature_averaging);
/**
+ * Plugin read callback for BMP085.
+ *
+ * Dispatching will create values:
+ * - <hostname>/barometer-bmp085/pressure-normalized
+ * - <hostname>/barometer-bmp085/pressure-absolute
+ * - <hostname>/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
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))
{
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;
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;
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 */
{
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 */
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);
}
zone_name = NULL;
if (j >= views->zones_num)
- {
- xmlXPathFreeObject (path_obj);
return (0);
- }
zone_name = view->zones[j];
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 */
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);
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 "
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 */
Perl-script every time you want to read a value with the C<exec plugin> (see
L<collectd-exec(5)>) and provides a lot more functionality, too.
-When loading the C<perl plugin>, the B<Globals> option should be enabled.
-Else, the perl plugin will fail to load any Perl modules implemented in C,
-which includes, amongst many others, the B<threads> module used by the plugin
-itself. See the documentation of the B<Globals> option in L<collectd.conf(5)>
-for details.
-
=head1 CONFIGURATION
=over 4
#@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond
#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
+#@BUILD_PLUGIN_IPC_TRUE@@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
#@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
#@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi
#@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
#@BUILD_PLUGIN_OPENLDAP_TRUE@LoadPlugin openldap
#@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn
#@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
-#@BUILD_PLUGIN_PERL_TRUE@<LoadPlugin perl>
-#@BUILD_PLUGIN_PERL_TRUE@ Globals true
-#@BUILD_PLUGIN_PERL_TRUE@</LoadPlugin>
+#@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
#@BUILD_PLUGIN_PINBA_TRUE@LoadPlugin pinba
#@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
#@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql
#@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
#@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
#@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols
-#@BUILD_PLUGIN_PYTHON_TRUE@<LoadPlugin python>
-#@BUILD_PLUGIN_PYTHON_TRUE@ Globals true
-#@BUILD_PLUGIN_PYTHON_TRUE@</LoadPlugin>
+#@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python
#@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis
#@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
#@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
#@BUILD_PLUGIN_WRITE_MONGODB_TRUE@LoadPlugin write_mongodb
#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
#@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
+#@BUILD_PLUGIN_WRITE_SENSU_TRUE@LoadPlugin write_sensu
#@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb
#@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
#@BUILD_PLUGIN_ZFS_ARC_TRUE@LoadPlugin zfs_arc
# Format "Command"
# StoreRates false
# BufferSize 4096
+# LowSpeedLimit 0
+# Timeout 0
# </Node>
#</Plugin>
# Attribute "foo" "bar"
#</Plugin>
+#<Plugin write_sensu>
+# <Node "example">
+# Host "localhost"
+# Port 3030
+# StoreRates true
+# AlwaysAppendDS false
+# Notifications true
+# Metrics true
+# EventServicePrefix ""
+# MetricHandler "influx"
+# MetricHandler "default"
+# NotificationHandler "flapjack"
+# NotificationHandler "howling_monkey"
+# </Node>
+# Tag "foobar"
+# Attribute "foo" "bar"
+#</Plugin>
+
#<Plugin write_tsdb>
# <Node>
# Host "localhost"
influence the way plugins are loaded, e.g.:
<LoadPlugin perl>
- Globals true
Interval 60
</LoadPlugin>
possibly need this option. What CA certificates come bundled with C<libcurl>
and are checked by default depends on the distribution you use.
+=item B<SSLCiphers> I<list of ciphers>
+
+Specifies which ciphers to use in the connection. The list of ciphers
+must specify valid ciphers. See
+L<http://www.openssl.org/docs/apps/ciphers.html> for details.
+
+=item B<Timeout> I<Milliseconds>
+
+The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
+milliseconds. By default, the configured B<Interval> is used to set the
+timeout.
+
=back
=head2 Plugin C<apcups>
possibly need this option. What CA certificates come bundled with C<libcurl>
and are checked by default depends on the distribution you use.
+=item B<Timeout> I<Milliseconds>
+
+The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
+milliseconds. By default, the configured B<Interval> is used to set the
+timeout.
+
=back
=head2 Plugin C<barometer>
-This plugin reads absolute air pressure using digital barometer sensor MPL115A2
-or MPL3115 from Freescale (sensor attached to any I2C bus available in
-the computer, for HW details see
-I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL115A> or
-I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL3115A2>).
-The sensor type - one fo these two - is detected automatically by the plugin
-and indicated in the plugin_instance (typically you will see subdirectory
-"barometer-mpl115" or "barometer-mpl3115").
+This plugin reads absolute air pressure using digital barometer sensor on a I2C
+bus. Supported sensors are:
+
+=over 5
+
+=item I<MPL115A2> from Freescale,
+see L<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL115A>.
+
+
+=item I<MPL3115> from Freescale
+see L<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL3115A2>.
+
+
+=item I<BMP085> from Bosch Sensortec
+
+=back
+
+The sensor type - one of the above - is detected automatically by the plugin
+and indicated in the plugin_instance (you will see subdirectory
+"barometer-mpl115" or "barometer-mpl3115", or "barometer-bmp085"). The order of
+detection is BMP085 -> MPL3115 -> MPL115A2, the first one found will be used
+(only one sensor can be used by the plugin).
The plugin provides absolute barometric pressure, air pressure reduced to sea
level (several possible approximations) and as an auxiliary value also internal
the standard Linux i2c-dev interface (the particular bus driver has to
support the SM Bus command subset).
-The reduction or normalization to mean sea level pressure requires (depedning on
-selected method/approximation) also altitude and reference to temperature sensor(s).
-When multiple temperature sensors are configured the minumum of their values is
-always used (expecting that the warmer ones are affected by e.g. direct sun light
-at that moment).
+The reduction or normalization to mean sea level pressure requires (depending
+on selected method/approximation) also altitude and reference to temperature
+sensor(s). When multiple temperature sensors are configured the minumum of
+their values is always used (expecting that the warmer ones are affected by
+e.g. direct sun light at that moment).
Synopsis:
=item B<Device> I<device>
-Device name of the I2C bus to which the sensor is connected. Note that typically
-you need to have loaded the i2c-dev module.
+The only mandatory configuration parameter.
+
+Device name of the I2C bus to which the sensor is connected. Note that
+typically you need to have loaded the i2c-dev module.
Using i2c-tools you can check/list i2c buses available on your system by:
i2cdetect -l
=item B<Oversampling> I<value>
-For MPL115 this is the size of the averaging window. To filter out sensor noise
-a simple averaging using floating window of configurable size is used. The plugin
-will use average of the last C<value> measurements (value of 1 means no averaging).
-Minimal size is 1, maximal 1024.
+Optional parameter controlling the oversampling/accuracy. Default value
+is 1 providing fastest and least accurate reading.
+
+For I<MPL115> this is the size of the averaging window. To filter out sensor
+noise a simple averaging using floating window of this configurable size is
+used. The plugin will use average of the last C<value> measurements (value of 1
+means no averaging). Minimal size is 1, maximal 1024.
+
+For I<MPL3115> this is the oversampling value. The actual oversampling is
+performed by the sensor and the higher value the higher accuracy and longer
+conversion time (although nothing to worry about in the collectd context).
+Supported values are: 1, 2, 4, 8, 16, 32, 64 and 128. Any other value is
+adjusted by the plugin to the closest supported one.
-For MPL3115 this is the oversampling value. The actual oversampling is performed
-by the sensor and the higher value the higher accuracy and longer conversion time
-(although nothing to worry about in the collectd context). Supported values are:
-1, 2, 4, 8, 16, 32, 64 and 128. Any other value is adjusted by the plugin to
-the closest supported one. Default is 128.
+For I<BMP085> this is the oversampling value. The actual oversampling is
+performed by the sensor and the higher value the higher accuracy and longer
+conversion time (although nothing to worry about in the collectd context).
+Supported values are: 1, 2, 4, 8. Any other value is adjusted by the plugin to
+the closest supported one.
=item B<PressureOffset> I<offset>
-You can further calibrate the sensor by supplying pressure and/or temperature offsets.
-This is added to the measured/caclulated value (i.e. if the measured value is too high
-then use negative offset).
+Optional parameter for MPL3115 only.
+
+You can further calibrate the sensor by supplying pressure and/or temperature
+offsets. This is added to the measured/caclulated value (i.e. if the measured
+value is too high then use negative offset).
In hPa, default is 0.0.
=item B<TemperatureOffset> I<offset>
-You can further calibrate the sensor by supplying pressure and/or temperature offsets.
-This is added to the measured/caclulated value (i.e. if the measured value is too high
-then use negative offset).
+Optional parameter for MPL3115 only.
+
+You can further calibrate the sensor by supplying pressure and/or temperature
+offsets. This is added to the measured/caclulated value (i.e. if the measured
+value is too high then use negative offset).
In C, default is 0.0.
=item B<Normalization> I<method>
-Normalization method - what approximation/model is used to compute mean sea
+Optional parameter, default value is 0.
+
+Normalization method - what approximation/model is used to compute the mean sea
level pressure from the air absolute pressure.
Supported values of the C<method> (integer between from 0 to 2) are:
=over 5
-=item B<0> - no conversion, absolute pressrure is simply copied over. For this method you
+=item B<0> - no conversion, absolute pressure is simply copied over. For this method you
do not need to configure C<Altitude> or C<TemperatureSensor>.
=item B<1> - international formula for conversion ,
-See I<http://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_atmospheric_pressure_variation>.
-For this method you have to configure C<Altitude> but do not need C<TemperatureSensor>
-(uses fixed global temperature average instead).
+See
+L<http://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_atmospheric_pressure_variation>.
+For this method you have to configure C<Altitude> but do not need
+C<TemperatureSensor> (uses fixed global temperature average instead).
=item B<2> - formula as recommended by the Deutsche Wetterdienst (German
Meteorological Service).
-See I<http://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Theorie>
-For this method you have to configure both C<Altitude> and C<TemperatureSensor>.
+See L<http://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Theorie>
+For this method you have to configure both C<Altitude> and
+C<TemperatureSensor>.
=back
=item B<TemperatureSensor> I<reference>
-Temperature sensor which should be used as a reference when normalizing the pressure.
-When specified more sensors a minumum is found and uses each time.
-The temperature reading directly from this pressure sensor/plugin
-is typically not suitable as the pressure sensor
-will be probably inside while we want outside temperature.
-The collectd reference name is something like
+Temperature sensor(s) which should be used as a reference when normalizing the
+pressure using C<Normalization> method 2.
+When specified more sensors a minumum is found and used each time. The
+temperature reading directly from this pressure sensor/plugin is typically not
+suitable as the pressure sensor will be probably inside while we want outside
+temperature. The collectd reference name is something like
<hostname>/<plugin_name>-<plugin_instance>/<type>-<type_instance>
-(<type_instance> is usually omitted when there is just single value type).
-Or you can figure it out from the path of the output data files.
+(<type_instance> is usually omitted when there is just single value type). Or
+you can figure it out from the path of the output data files.
=back
Default: Enabled.
+=item B<Timeout> I<Milliseconds>
+
+The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
+milliseconds. By default, the configured B<Interval> is used to set the
+timeout.
+
=item B<View> I<Name>
Collect statistics about a specific I<"view">. BIND can behave different,
Measure response time for the request. If this setting is enabled, B<Match>
blocks (see below) are optional. Disabled by default.
+Beware that requests will get aborted if they take too long to complete. Adjust
+B<Timeout> accordingly if you expect B<MeasureResponseTime> to report such slow
+requests.
+
=item B<MeasureResponseCode> B<true>|B<false>
Measure response code for the request. If this setting is enabled, B<Match>
B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
optional.
+=item B<Timeout> I<Milliseconds>
+
+The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
+milliseconds. By default, the configured B<Interval> is used to set the
+timeout. Prior to version 5.5.0, there was no timeout and requests could hang
+indefinitely. This legacy behaviour can be achieved by setting the value of
+B<Timeout> to 0.
+
+If B<Timeout> is 0 or bigger than the B<Interval>, keep in mind that each slow
+network connection will stall one read thread. Adjust the B<ReadThreads> global
+setting accordingly to prevent this from blocking other plugins.
+
=back
=head2 Plugin C<curl_json>
=item B<Post> I<Body>
+=item B<Timeout> I<Milliseconds>
+
These options behave exactly equivalent to the appropriate options of the
I<cURL> plugin. Please see there for a detailed description.
=item B<Post> I<Body>
+=item B<Timeout> I<Milliseconds>
+
These options behave exactly equivalent to the appropriate options of the
I<cURL plugin>. Please see there for a detailed description.
possibly need this option. What CA certificates come bundled with C<libcurl>
and are checked by default depends on the distribution you use.
+=item B<Timeout> I<Milliseconds>
+
+The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
+milliseconds. By default, the configured B<Interval> is used to set the
+timeout.
+
=back
=head2 Plugin C<notify_desktop>
amount of time will be lost, for example, if a single statement within the
transaction fails or if the database server crashes.
+=item B<Instance> I<name>
+
+Specify the plugin instance name that should be used instead of the database
+name (which is the default, if this option has not been specified). This
+allows to query multiple databases of the same name on the same host (e.g.
+when running multiple database server versions in parallel).
+
=item B<Host> I<hostname>
Specify the hostname or IP of the PostgreSQL server to connect to. If the
Use I<Password> to authenticate when connecting to I<Redis>.
-=item B<Timeout> I<Timeout in miliseconds>
+=item B<Timeout> I<Milliseconds>
The B<Timeout> option set the socket timeout for node response. Since the Redis
read function is blocking, you should keep this value as low as possible. Keep
Service name or port number to connect to. Defaults to C<27017>.
-=item B<Timeout> I<Timeout>
+=item B<Timeout> I<Milliseconds>
Set the timeout for each operation on I<MongoDB> to I<Timeout> milliseconds.
Setting this option to zero means no timeout, which is the default.
exceed the size of an C<int>, i.e. 2E<nbsp>GByte.
Defaults to C<4096>.
+=item B<LowSpeedLimit> I<Bytes per Second>
+
+Sets the minimal transfer rate in I<Bytes per Second> below which the
+connection with the HTTP server will be considered too slow and aborted. All
+the data submitted over this connection will probably be lost. Defaults to 0,
+which means no minimum transfer rate is enforced.
+
+=item B<Timeout> I<Timeout>
+
+Sets the maximum time in milliseconds given for HTTP POST operations to
+complete. When this limit is reached, the POST operation will be aborted, and
+all the data in the current send buffer will probably be lost. Defaults to 0,
+which means the connection never times out.
+
+The C<write_http> plugin regularly submits the collected values to the HTTP
+server. How frequently this happens depends on how much data you are collecting
+and the size of B<BufferSize>. The optimal value to set B<Timeout> to is
+slightly below this interval, which you can estimate by monitoring the network
+traffic between collectd and the HTTP server.
+
=back
=head2 Plugin C<write_kafka>
connections. Either a service name of a port number may be given. Please note
that numerical port numbers must be given as a string, too.
-=item B<Timeout> I<Timeout in miliseconds>
+=item B<Timeout> I<Milliseconds>
The B<Timeout> option sets the socket connection timeout, in milliseconds.
=back
+=head2 Plugin C<write_sensu>
+
+The I<write_sensu plugin> will send values to I<Sensu>, a powerful stream
+aggregation and monitoring system. The plugin sends I<JSON> encoded data to
+a local I<Sensu> client using a TCP socket.
+
+At the moment, the I<write_sensu plugin> does not send over a collectd_host
+parameter so it is not possible to use one collectd instance as a gateway for
+others. Each collectd host must pair with one I<Sensu> client.
+
+Synopsis:
+
+ <Plugin "write_sensu">
+ <Node "example">
+ Host "localhost"
+ Port "3030"
+ StoreRates true
+ AlwaysAppendDS false
+ MetricHandler "influx"
+ MetricHandler "default"
+ NotificationHandler "flapjack"
+ NotificationHandler "howling_monkey"
+ Notifications true
+ </Node>
+ Tag "foobar"
+ Attribute "foo" "bar"
+ </Plugin>
+
+The following options are understood by the I<write_sensu plugin>:
+
+=over 4
+
+=item E<lt>B<Node> I<Name>E<gt>
+
+The plugin's configuration consists of one or more B<Node> blocks. Each block
+is given a unique I<Name> and specifies one connection to an instance of
+I<Sensu>. Inside the B<Node> block, the following per-connection options are
+understood:
+
+=over 4
+
+=item B<Host> I<Address>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<3030>.
+
+=item B<StoreRates> B<true>|B<false>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.e. as an increasing integer number.
+
+This will be reflected in the C<collectd_data_source_type> tag: If
+B<StoreRates> is enabled, converted values will have "rate" appended to the
+data source type, e.g. C<collectd_data_source_type:derive:rate>.
+
+=item B<AlwaysAppendDS> B<false>|B<true>
+
+If set the B<true>, append the name of the I<Data Source> (DS) to the
+"service", i.e. the field that, together with the "host" field, uniquely
+identifies a metric in I<Sensu>. If set to B<false> (the default), this is
+only done when there is more than one DS.
+
+=item B<Notifications> B<false>|B<true>
+
+If set to B<true>, create I<Sensu> events for notifications. This is B<false>
+by default. At least one of B<Notifications> or B<Metrics> should be enabled.
+
+=item B<Metrics> B<false>|B<true>
+
+If set to B<true>, create I<Sensu> events for metrics. This is B<false>
+by default. At least one of B<Notifications> or B<Metrics> should be enabled.
+
+
+=item B<Separator> I<String>
+
+Sets the separator for I<Sensu> metrics name or checks. Defaults to "/".
+
+=item B<MetricHandler> I<String>
+
+Add a handler that will be set when metrics are sent to I<Sensu>. You can add
+several of them, one per line. Defaults to no handler.
+
+=item B<NotificationHandler> I<String>
+
+Add a handler that will be set when notifications are sent to I<Sensu>. You can
+add several of them, one per line. Defaults to no handler.
+
+=item B<EventServicePrefix> I<String>
+
+Add the given string as a prefix to the event service name.
+If B<EventServicePrefix> not set or set to an empty string (""),
+no prefix will be used.
+
+=back
+
+=item B<Tag> I<String>
+
+Add the given string as an additional tag to the metric being sent to
+I<Sensu>.
+
+=item B<Attribute> I<String> I<String>
+
+Consider the two given strings to be the key and value of an additional
+attribute for each metric being sent out to I<Sensu>.
+
+=back
+
=head2 Plugin C<zookeeper>
The I<zookeeper plugin> will collect statistics from a I<Zookeeper> server
char *post_body;
_Bool response_time;
_Bool response_code;
+ int timeout;
CURL *curl;
char curl_errbuf[CURL_ERROR_SIZE];
if (wp->post_body != NULL)
curl_easy_setopt (wp->curl, CURLOPT_POSTFIELDS, wp->post_body);
+#ifdef HAVE_CURLOPT_TIMEOUT_MS
+ if (wp->timeout >= 0)
+ curl_easy_setopt (wp->curl, CURLOPT_TIMEOUT_MS, (long) wp->timeout);
+ else
+ curl_easy_setopt (wp->curl, CURLOPT_TIMEOUT_MS,
+ CDTIME_T_TO_MS(plugin_get_interval()));
+#endif
+
return (0);
} /* }}} int cc_page_init_curl */
page->verify_host = 1;
page->response_time = 0;
page->response_code = 0;
+ page->timeout = -1;
page->instance = strdup (ci->values[0].value.string);
if (page->instance == NULL)
status = cc_config_append_string ("Header", &page->headers, child);
else if (strcasecmp ("Post", child->key) == 0)
status = cf_util_get_string (child, &page->post_body);
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ status = cf_util_get_int (child, &page->timeout);
else
{
WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
status = curl_easy_perform (wp->curl);
if (status != CURLE_OK)
{
- ERROR ("curl plugin: curl_easy_perform failed with staus %i: %s",
+ ERROR ("curl plugin: curl_easy_perform failed with status %i: %s",
status, wp->curl_errbuf);
return (-1);
}
long response_code = 0;
status = curl_easy_getinfo(wp->curl, CURLINFO_RESPONSE_CODE, &response_code);
if(status != CURLE_OK) {
- ERROR ("curl plugin: Fetching response code failed with staus %i: %s",
+ ERROR ("curl plugin: Fetching response code failed with status %i: %s",
status, wp->curl_errbuf);
} else {
cc_submit_response_code(wp, response_code);
struct curl_slist *headers;
char *post_body;
cdtime_t interval;
+ int timeout;
CURL *curl;
char curl_errbuf[CURL_ERROR_SIZE];
if (db->post_body != NULL)
curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
+#ifdef HAVE_CURLOPT_TIMEOUT_MS
+ if (db->timeout >= 0)
+ curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) db->timeout);
+ else if (db->interval > 0)
+ curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
+ CDTIME_T_TO_MS(db->timeout));
+ else
+ curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
+ CDTIME_T_TO_MS(plugin_get_interval()));
+#endif
+
return (0);
} /* }}} int cj_init_curl */
}
memset (db, 0, sizeof (*db));
+ db->timeout = -1;
+
if (strcasecmp ("URL", ci->key) == 0)
status = cf_util_get_string (ci, &db->url);
else if (strcasecmp ("Sock", ci->key) == 0)
status = cj_config_add_key (db, child);
else if (strcasecmp ("Interval", child->key) == 0)
status = cf_util_get_cdtime(child, &db->interval);
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ status = cf_util_get_int (child, &db->timeout);
else
{
WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
_Bool verify_host;
char *cacert;
char *post_body;
+ int timeout;
struct curl_slist *headers;
cx_namespace_t *namespaces;
if (db->post_body != NULL)
curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
+#ifdef HAVE_CURLOPT_TIMEOUT_MS
+ if (db->timeout >= 0)
+ curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) db->timeout);
+ else
+ curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
+ CDTIME_T_TO_MS(plugin_get_interval()));
+#endif
+
return (0);
} /* }}} int cx_init_curl */
}
memset (db, 0, sizeof (*db));
+ db->timeout = -1;
+
if (strcasecmp ("URL", ci->key) == 0)
{
status = cf_util_get_string (ci, &db->url);
status = cf_util_get_string (child, &db->post_body);
else if (strcasecmp ("Namespace", child->key) == 0)
status = cx_config_add_namespace (db, child);
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ status = cf_util_get_int (child, &db->timeout);
else
{
WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
--- /dev/null
+/**
+ * collectd - src/ipc.c, based on src/memcached.c
+ * Copyright (C) 2010 Andres J. Diaz <ajdiaz@connectical.com>
+ * Copyright (C) 2010 Manuel L. Sanmartin <manuel.luis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Andres J. Diaz <ajdiaz@connectical.com>
+ * Manuel L. Sanmartin <manuel.luis@gmail>
+ **/
+
+/* Many of this code is based on busybox ipc implementation, which is:
+ * (C) Rodney Radford <rradford@mindspring.com> and distributed under GPLv2.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#if KERNEL_LINUX
+ /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
+ /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+ /* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/sem.h>
+# include <sys/msg.h>
+# include <sys/shm.h>
+
+ /* For older kernels the same holds for the defines below */
+# ifndef MSG_STAT
+# define MSG_STAT 11
+# define MSG_INFO 12
+# endif
+
+# ifndef SHM_STAT
+# define SHM_STAT 13
+# define SHM_INFO 14
+ struct shm_info {
+ int used_ids;
+ ulong shm_tot; /* total allocated shm */
+ ulong shm_rss; /* total resident shm */
+ ulong shm_swp; /* total swapped shm */
+ ulong swap_attempts;
+ ulong swap_successes;
+ };
+# endif
+
+# ifndef SEM_STAT
+# define SEM_STAT 18
+# define SEM_INFO 19
+# endif
+
+ /* The last arg of semctl is a union semun, but where is it defined?
+ X/OPEN tells us to define it ourselves, but until recently
+ Linux include files would also define it. */
+# if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+ /* union semun is defined by including <sys/sem.h> */
+# else
+ /* according to X/OPEN we have to define it ourselves */
+ union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+ struct seminfo *__buf;
+ };
+# endif
+static long pagesize_g;
+/* #endif KERNEL_LINUX */
+#elif KERNEL_AIX
+# include <sys/ipc_info.h>
+/* #endif KERNEL_AIX */
+#else
+# error "No applicable input method."
+#endif
+
+__attribute__ ((nonnull(1)))
+static void ipc_submit_g (const char *plugin_instance,
+ const char *type,
+ const char *type_instance,
+ gauge_t 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, "ipc", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, plugin_instance, 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);
+} /* }}} */
+
+#if KERNEL_AIX
+static caddr_t ipc_get_info (cid_t cid, int cmd, int version, int stsize, int *nmemb) /* {{{ */
+{
+ int size = 0;
+ caddr_t buff = NULL;
+
+ if (get_ipc_info(cid, cmd, version, buff, &size) < 0)
+ {
+ if (errno != ENOSPC) {
+ char errbuf[1024];
+ WARNING ("ipc plugin: get_ipc_info: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (NULL);
+ }
+ }
+
+ if (size == 0)
+ return NULL;
+
+ if (size % stsize) {
+ ERROR ("ipc plugin: ipc_get_info: missmatch struct size and buffer size");
+ return (NULL);
+ }
+
+ *nmemb = size / stsize;
+
+ buff = (caddr_t)malloc (size);
+ if (buff == NULL) {
+ ERROR ("ipc plugin: ipc_get_info malloc failed.");
+ return (NULL);
+ }
+
+ if (get_ipc_info(cid, cmd, version, buff, &size) < 0)
+ {
+ char errbuf[1024];
+ WARNING ("ipc plugin: get_ipc_info: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ free(buff);
+ return (NULL);
+ }
+
+ return buff;
+} /* }}} */
+#endif /* KERNEL_AIX */
+
+static int ipc_read_sem (void) /* {{{ */
+{
+#if KERNEL_LINUX
+ struct seminfo seminfo;
+ union semun arg;
+
+ arg.array = (ushort *) (void *) &seminfo;
+
+ if ( semctl(0, 0, SEM_INFO, arg) < 0 )
+ {
+ ERROR("Kernel is not configured for semaphores");
+ return (-1);
+ }
+
+ ipc_submit_g("sem", "count", "arrays", seminfo.semusz);
+ ipc_submit_g("sem", "count", "total", seminfo.semaem);
+
+/* #endif KERNEL_LINUX */
+#elif KERNEL_AIX
+ ipcinfo_sem_t *ipcinfo_sem;
+ unsigned short sem_nsems=0;
+ unsigned short sems=0;
+ int i,n;
+
+ ipcinfo_sem = (ipcinfo_sem_t *)ipc_get_info(0,
+ GET_IPCINFO_SEM_ALL, IPCINFO_SEM_VERSION, sizeof(ipcinfo_sem_t), &n);
+ if (ipcinfo_sem == NULL)
+ return -1;
+
+ for (i=0; i<n; i++) {
+ sem_nsems += ipcinfo_sem[i].sem_nsems;
+ sems++;
+ }
+ free(ipcinfo_sem);
+
+ ipc_submit_g("sem", "count", "arrays", sem_nsems);
+ ipc_submit_g("sem", "count", "total", sems);
+#endif /* KERNEL_AIX */
+
+ return (0);
+}
+/* }}} */
+
+static int ipc_read_shm (void) /* {{{ */
+{
+#if KERNEL_LINUX
+ struct shm_info shm_info;
+
+ if ( shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info) < 0 )
+ {
+ ERROR("Kernel is not configured for shared memory");
+ return (-1);
+ }
+ ipc_submit_g("shm", "segments", NULL, shm_info.used_ids);
+ ipc_submit_g("shm", "bytes", "total", shm_info.shm_tot * pagesize_g);
+ ipc_submit_g("shm", "bytes", "rss", shm_info.shm_rss * pagesize_g);
+ ipc_submit_g("shm", "bytes", "swapped", shm_info.shm_swp * pagesize_g);
+/* #endif KERNEL_LINUX */
+#elif KERNEL_AIX
+ ipcinfo_shm_t *ipcinfo_shm;
+ ipcinfo_shm_t *pshm;
+ unsigned int shm_segments=0;
+ size64_t shm_bytes=0;
+ int i,n;
+
+ ipcinfo_shm = (ipcinfo_shm_t *)ipc_get_info(0,
+ GET_IPCINFO_SHM_ALL, IPCINFO_SHM_VERSION, sizeof(ipcinfo_shm_t), &n);
+ if (ipcinfo_shm == NULL)
+ return -1;
+
+ for (i=0, pshm=ipcinfo_shm; i<n; i++, pshm++) {
+ shm_segments++;
+ shm_bytes += pshm->shm_segsz;
+ }
+ free(ipcinfo_shm);
+
+ ipc_submit_g("shm", "segments", NULL, shm_segments);
+ ipc_submit_g("shm", "bytes", "total", shm_bytes);
+
+#endif /* KERNEL_AIX */
+ return (0);
+}
+/* }}} */
+
+static int ipc_read_msg (void) /* {{{ */
+{
+#if KERNEL_LINUX
+ struct msginfo msginfo;
+
+ if ( msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo) < 0 )
+ {
+ ERROR("Kernel is not configured for message queues");
+ return (-1);
+ }
+ ipc_submit_g("msg", "count", "queues", msginfo.msgmni);
+ ipc_submit_g("msg", "count", "headers", msginfo.msgmap);
+ ipc_submit_g("msg", "count", "space", msginfo.msgtql);
+/* #endif KERNEL_LINUX */
+#elif KERNEL_AIX
+ ipcinfo_msg_t *ipcinfo_msg;
+ uint32_t msg_used_space=0;
+ uint32_t msg_alloc_queues=0;
+ msgqnum32_t msg_qnum=0;
+ int i,n;
+
+ ipcinfo_msg = (ipcinfo_msg_t *)ipc_get_info(0,
+ GET_IPCINFO_MSG_ALL, IPCINFO_MSG_VERSION, sizeof(ipcinfo_msg_t), &n);
+ if (ipcinfo_msg == NULL)
+ return -1;
+
+ for (i=0; i<n; i++) {
+ msg_alloc_queues++;
+ msg_used_space += ipcinfo_msg[i].msg_cbytes;
+ msg_qnum += ipcinfo_msg[i].msg_qnum;
+ }
+ free(ipcinfo_msg);
+
+ ipc_submit_g("msg", "count", "queues", msg_alloc_queues);
+ ipc_submit_g("msg", "count", "headers", msg_qnum);
+ ipc_submit_g("msg", "count", "space", msg_used_space);
+#endif /* KERNEL_AIX */
+ return (0);
+}
+/* }}} */
+
+static int ipc_read (void) /* {{{ */
+{
+ int x = 0;
+ x |= ipc_read_shm();
+ x |= ipc_read_sem();
+ x |= ipc_read_msg();
+
+ return (x);
+}
+/* }}} */
+
+#ifdef KERNEL_LINUX
+static int ipc_init (void) /* {{{ */
+{
+ pagesize_g = sysconf(_SC_PAGESIZE);
+ return (0);
+}
+/* }}} */
+#endif /* KERNEL_LINUX */
+
+void module_register (void) /* {{{ */
+{
+#ifdef KERNEL_LINUX
+ plugin_register_init ("ipc", ipc_init);
+#endif
+ plugin_register_read ("ipc", ipc_read);
+}
+/* }}} */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
static char *verify_peer = NULL;
static char *verify_host = NULL;
static char *cacert = NULL;
+static char *timeout = NULL;
static CURL *curl = NULL;
"Password",
"VerifyPeer",
"VerifyHost",
- "CACert"
+ "CACert",
+ "Timeout"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
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 config */
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);
} /* void init */
/* Description of statistics returned by the recursor: {{{
all-outqueries counts the number of outgoing UDP queries since starting
-answers0-1 counts the number of queries answered within 1 milisecond
+answers0-1 counts the number of queries answered within 1 millisecond
answers100-1000 counts the number of queries answered within 1 second
-answers10-100 counts the number of queries answered within 100 miliseconds
-answers1-10 counts the number of queries answered within 10 miliseconds
+answers10-100 counts the number of queries answered within 100 milliseconds
+answers1-10 counts the number of queries answered within 10 milliseconds
answers-slow counts the number of queries answered after 1 second
cache-entries shows the number of entries in the cache
cache-hits counts the number of cache hits since starting
route_etx value:GAUGE:0:U
route_metric value:GAUGE:0:U
routes value:GAUGE:0:U
+segments value:GAUGE:0:65535
serial_octets rx:DERIVE:0:U, tx:DERIVE:0:U
signal_noise value:GAUGE:U:0
signal_power value:GAUGE:U:0
char *clientkeypass;
long sslversion;
_Bool store_rates;
+ int low_speed_limit;
+ time_t low_speed_time;
+ int timeout;
#define WH_FORMAT_COMMAND 0
#define WH_FORMAT_JSON 1
return (-1);
}
+ if (cb->low_speed_limit > 0 && cb->low_speed_time > 0)
+ {
+ curl_easy_setopt (cb->curl, CURLOPT_LOW_SPEED_LIMIT,
+ (long) (cb->low_speed_limit * cb->low_speed_time));
+ curl_easy_setopt (cb->curl, CURLOPT_LOW_SPEED_TIME,
+ (long) cb->low_speed_time);
+ }
+
+#ifdef HAVE_CURLOPT_TIMEOUT_MS
+ if (cb->timeout > 0)
+ curl_easy_setopt (cb->curl, CURLOPT_TIMEOUT_MS, (long) cb->timeout);
+#endif
+
curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
cb->verify_host = 1;
cb->format = WH_FORMAT_COMMAND;
cb->sslversion = CURL_SSLVERSION_DEFAULT;
+ cb->low_speed_limit = 0;
+ cb->timeout = 0;
pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
cf_util_get_boolean (child, &cb->store_rates);
else if (strcasecmp ("BufferSize", child->key) == 0)
cf_util_get_int (child, &buffer_size);
+ else if (strcasecmp ("LowSpeedLimit", child->key) == 0)
+ cf_util_get_int (child, &cb->low_speed_limit);
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ cf_util_get_int (child, &cb->timeout);
else
{
ERROR ("write_http plugin: Invalid configuration "
return (-1);
}
+ if (cb->low_speed_limit > 0)
+ cb->low_speed_time = CDTIME_T_TO_TIME_T(plugin_get_interval());
+
/* Determine send_buffer_size. */
cb->send_buffer_size = WRITE_HTTP_DEFAULT_BUFFER_SIZE;
if (buffer_size >= 1024)
--- /dev/null
+/**
+ * collectd - src/write_sensu.c
+ * Copyright (C) 2015 Fabrice A. Marie
+ *
+ * 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:
+ * Fabrice A. Marie <fabrice at kibinlabs.com>
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_cache.h"
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stddef.h>
+
+#include <stdlib.h>
+#ifndef HAVE_ASPRINTF
+/*
+ * Uses asprintf() portable implementation from
+ * https://github.com/littlstar/asprintf.c/blob/master/
+ * copyright (c) 2014 joseph werle <joseph.werle@gmail.com> under MIT license.
+ */
+#include <stdio.h>
+#include <stdarg.h>
+
+int vasprintf(char **str, const char *fmt, va_list args) {
+ int size = 0;
+ va_list tmpa;
+ // copy
+ va_copy(tmpa, args);
+ // apply variadic arguments to
+ // sprintf with format to get size
+ size = vsnprintf(NULL, size, fmt, tmpa);
+ // toss args
+ va_end(tmpa);
+ // return -1 to be compliant if
+ // size is less than 0
+ if (size < 0) { return -1; }
+ // alloc with size plus 1 for `\0'
+ *str = (char *) malloc(size + 1);
+ // return -1 to be compliant
+ // if pointer is `NULL'
+ if (NULL == *str) { return -1; }
+ // format string with original
+ // variadic arguments and set new size
+ size = vsprintf(*str, fmt, args);
+ return size;
+}
+
+int asprintf(char **str, const char *fmt, ...) {
+ int size = 0;
+ va_list args;
+ // init variadic argumens
+ va_start(args, fmt);
+ // format and get size
+ size = vasprintf(str, fmt, args);
+ // toss args
+ va_end(args);
+ return size;
+}
+
+#endif
+
+#define SENSU_HOST "localhost"
+#define SENSU_PORT "3030"
+
+struct str_list {
+ int nb_strs;
+ char **strs;
+};
+
+struct sensu_host {
+ char *name;
+ char *event_service_prefix;
+ struct str_list metric_handlers;
+ struct str_list notification_handlers;
+#define F_READY 0x01
+ uint8_t flags;
+ pthread_mutex_t lock;
+ _Bool notifications;
+ _Bool metrics;
+ _Bool store_rates;
+ _Bool always_append_ds;
+ char *separator;
+ char *node;
+ char *service;
+ int s;
+ struct addrinfo *res;
+ int reference_count;
+};
+
+static char *sensu_tags;
+static char **sensu_attrs;
+static size_t sensu_attrs_num;
+static const char *alloc_err ="write_sensu plugin: Unable to alloc memory";
+
+static int add_str_to_list(struct str_list *strs,
+ const char *str_to_add) /* {{{ */
+{
+ char **old_strs_ptr = strs->strs;
+ char *newstr = strdup(str_to_add);
+ if (newstr == NULL) {
+ ERROR(alloc_err);
+ return -1;
+ }
+ strs->strs = realloc(strs->strs, sizeof(char *) *(strs->nb_strs + 1));
+ if (strs->strs == NULL) {
+ strs->strs = old_strs_ptr;
+ free(newstr);
+ ERROR(alloc_err);
+ return -1;
+ }
+ strs->strs[strs->nb_strs] = newstr;
+ strs->nb_strs++;
+ return 0;
+}
+/* }}} int add_str_to_list */
+
+static void free_str_list(struct str_list *strs) /* {{{ */
+{
+ int i;
+ for (i=0; i<strs->nb_strs; i++)
+ free(strs->strs[i]);
+ free(strs->strs);
+}
+/* }}} void free_str_list */
+
+static int sensu_connect(struct sensu_host *host) /* {{{ */
+{
+ int e;
+ struct addrinfo *ai, hints;
+ char const *node;
+ char const *service;
+
+ // Resolve the target if we haven't done already
+ if (!(host->flags & F_READY)) {
+ memset(&hints, 0, sizeof(hints));
+ memset(&service, 0, sizeof(service));
+ host->res = NULL;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+
+ node = (host->node != NULL) ? host->node : SENSU_HOST;
+ service = (host->service != NULL) ? host->service : SENSU_PORT;
+
+ if ((e = getaddrinfo(node, service, &hints, &(host->res))) != 0) {
+ ERROR("write_sensu plugin: Unable to resolve host \"%s\": %s",
+ node, gai_strerror(e));
+ return -1;
+ }
+ DEBUG("write_sensu plugin: successfully resolved host/port: %s/%s",
+ node, service);
+ host->flags |= F_READY;
+ }
+
+ struct linger so_linger;
+ host->s = -1;
+ for (ai = host->res; ai != NULL; ai = ai->ai_next) {
+ // create the socket
+ if ((host->s = socket(ai->ai_family,
+ ai->ai_socktype,
+ ai->ai_protocol)) == -1) {
+ continue;
+ }
+
+ // Set very low close() lingering
+ so_linger.l_onoff = 1;
+ so_linger.l_linger = 3;
+ if (setsockopt(host->s, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) != 0)
+ WARNING("write_sensu plugin: failed to set socket close() lingering");
+
+ // connect the socket
+ if (connect(host->s, ai->ai_addr, ai->ai_addrlen) != 0) {
+ close(host->s);
+ host->s = -1;
+ continue;
+ }
+ DEBUG("write_sensu plugin: connected");
+ break;
+ }
+
+ if (host->s < 0) {
+ WARNING("write_sensu plugin: Unable to connect to sensu client");
+ return -1;
+ }
+ return 0;
+} /* }}} int sensu_connect */
+
+static void sensu_close_socket(struct sensu_host *host) /* {{{ */
+{
+ if (host->s != -1)
+ close(host->s);
+ host->s = -1;
+
+} /* }}} void sensu_close_socket */
+
+static char *build_json_str_list(const char *tag, struct str_list const *list) /* {{{ */
+{
+ int res;
+ char *ret_str;
+ char *temp_str;
+ int i;
+ if (list->nb_strs == 0) {
+ ret_str = malloc(sizeof(char));
+ if (ret_str == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str[0] = '\0';
+ }
+
+ res = asprintf(&temp_str, "\"%s\": [\"%s\"", tag, list->strs[0]);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ for (i=1; i<list->nb_strs; i++) {
+ res = asprintf(&ret_str, "%s, \"%s\"", temp_str, list->strs[i]);
+ free(temp_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ temp_str = ret_str;
+ }
+ res = asprintf(&ret_str, "%s]", temp_str);
+ free(temp_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+
+ return ret_str;
+} /* }}} char *build_json_str_list*/
+
+int sensu_format_name2(char *ret, int ret_len,
+ const char *hostname,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance,
+ const char *separator)
+{
+ char *buffer;
+ size_t buffer_size;
+
+ buffer = ret;
+ buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do { \
+ size_t l = strlen (str); \
+ if (l >= buffer_size) \
+ return (ENOBUFS); \
+ memcpy (buffer, (str), l); \
+ buffer += l; buffer_size -= l; \
+} while (0)
+
+ assert (plugin != NULL);
+ assert (type != NULL);
+
+ APPEND (hostname);
+ APPEND (separator);
+ APPEND (plugin);
+ if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+ {
+ APPEND ("-");
+ APPEND (plugin_instance);
+ }
+ APPEND (separator);
+ APPEND (type);
+ if ((type_instance != NULL) && (type_instance[0] != 0))
+ {
+ APPEND ("-");
+ APPEND (type_instance);
+ }
+ assert (buffer_size > 0);
+ buffer[0] = 0;
+
+#undef APPEND
+ return (0);
+} /* int sensu_format_name2 */
+
+static void in_place_replace_sensu_name_reserved(char *orig_name) /* {{{ */
+{
+ int i;
+ int len=strlen(orig_name);
+ for (i=0; i<len; i++) {
+ // some plugins like ipmi generate special characters in metric name
+ switch(orig_name[i]) {
+ case '(': orig_name[i] = '_'; break;
+ case ')': orig_name[i] = '_'; break;
+ case ' ': orig_name[i] = '_'; break;
+ case '"': orig_name[i] = '_'; break;
+ case '\'': orig_name[i] = '_'; break;
+ case '+': orig_name[i] = '_'; break;
+ }
+ }
+} /* }}} char *replace_sensu_name_reserved */
+
+static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
+ data_set_t const *ds,
+ value_list_t const *vl, size_t index,
+ gauge_t const *rates,
+ int status)
+{
+ char name_buffer[5 * DATA_MAX_NAME_LEN];
+ char service_buffer[6 * DATA_MAX_NAME_LEN];
+ int i;
+ char *ret_str;
+ char *temp_str;
+ char *value_str;
+ int res;
+ // First part of the JSON string
+ const char *part1 = "{\"name\": \"collectd\", \"type\": \"metric\"";
+
+ char *handlers_str = build_json_str_list("handlers", &(host->metric_handlers));
+ if (handlers_str == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+
+ // incorporate the handlers
+ if (strlen(handlers_str) == 0) {
+ free(handlers_str);
+ ret_str = strdup(part1);
+ if (ret_str == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ }
+ else {
+ res = asprintf(&ret_str, "%s, %s", part1, handlers_str);
+ free(handlers_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ }
+
+ // incorporate the plugin name information
+ res = asprintf(&temp_str, "%s, \"collectd_plugin\": \"%s\"", ret_str, vl->plugin);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ // incorporate the plugin type
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_type\": \"%s\"", ret_str, vl->type);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ // incorporate the plugin instance if any
+ if (vl->plugin_instance[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_instance\": \"%s\"", ret_str, vl->plugin_instance);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the plugin type instance if any
+ if (vl->type_instance[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_type_instance\": \"%s\"", ret_str, vl->type_instance);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the data source type
+ if ((ds->ds[index].type != DS_TYPE_GAUGE) && (rates != NULL)) {
+ char ds_type[DATA_MAX_NAME_LEN];
+ ssnprintf (ds_type, sizeof (ds_type), "%s:rate", DS_TYPE_TO_STRING(ds->ds[index].type));
+ res = asprintf(&temp_str, "%s, \"collectd_data_source_type\": \"%s\"", ret_str, ds_type);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ } else {
+ res = asprintf(&temp_str, "%s, \"collectd_data_source_type\": \"%s\"", ret_str, DS_TYPE_TO_STRING(ds->ds[index].type));
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the data source name
+ res = asprintf(&temp_str, "%s, \"collectd_data_source_name\": \"%s\"", ret_str, ds->ds[index].name);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ // incorporate the data source index
+ {
+ char ds_index[DATA_MAX_NAME_LEN];
+ ssnprintf (ds_index, sizeof (ds_index), "%zu", index);
+ res = asprintf(&temp_str, "%s, \"collectd_data_source_index\": %s", ret_str, ds_index);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // add key value attributes from config if any
+ for (i = 0; i < sensu_attrs_num; i += 2) {
+ res = asprintf(&temp_str, "%s, \"%s\": \"%s\"", ret_str, sensu_attrs[i], sensu_attrs[i+1]);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate sensu tags from config if any
+ if (strlen(sensu_tags) != 0) {
+ res = asprintf(&temp_str, "%s, %s", ret_str, sensu_tags);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // calculate the value and set to a string
+ if (ds->ds[index].type == DS_TYPE_GAUGE) {
+ double tmp_v = (double) vl->values[index].gauge;
+ res = asprintf(&value_str, "%.8f", tmp_v, sensu_tags);
+ if (res == -1) {
+ free(ret_str);
+ ERROR(alloc_err);
+ return NULL;
+ }
+ } else if (rates != NULL) {
+ double tmp_v = (double) rates[index];
+ res = asprintf(&value_str, "%.8f", tmp_v, sensu_tags);
+ if (res == -1) {
+ free(ret_str);
+ ERROR(alloc_err);
+ return NULL;
+ }
+ } else {
+ int64_t tmp_v;
+ if (ds->ds[index].type == DS_TYPE_DERIVE)
+ tmp_v = (int64_t) vl->values[index].derive;
+ else if (ds->ds[index].type == DS_TYPE_ABSOLUTE)
+ tmp_v = (int64_t) vl->values[index].absolute;
+ else
+ tmp_v = (int64_t) vl->values[index].counter;
+ res = asprintf(&value_str, "%lld", tmp_v, sensu_tags);
+ if (res == -1) {
+ free(ret_str);
+ ERROR(alloc_err);
+ return NULL;
+ }
+ }
+
+ // Generate the full service name
+ sensu_format_name2(name_buffer, sizeof(name_buffer),
+ vl->host, vl->plugin, vl->plugin_instance,
+ vl->type, vl->type_instance, host->separator);
+ if (host->always_append_ds || (ds->ds_num > 1)) {
+ if (host->event_service_prefix == NULL)
+ ssnprintf(service_buffer, sizeof(service_buffer), "%s.%s",
+ name_buffer, ds->ds[index].name);
+ else
+ ssnprintf(service_buffer, sizeof(service_buffer), "%s%s.%s",
+ host->event_service_prefix, name_buffer, ds->ds[index].name);
+ } else {
+ if (host->event_service_prefix == NULL)
+ sstrncpy(service_buffer, name_buffer, sizeof(service_buffer));
+ else
+ ssnprintf(service_buffer, sizeof(service_buffer), "%s%s",
+ host->event_service_prefix, name_buffer);
+ }
+
+ // Replace collectd sensor name reserved characters so that time series DB is happy
+ in_place_replace_sensu_name_reserved(service_buffer);
+
+ // finalize the buffer by setting the output and closing curly bracket
+ res = asprintf(&temp_str, "%s, \"output\": \"%s %s %ld\"}\n", ret_str, service_buffer, value_str, CDTIME_T_TO_TIME_T(vl->time));
+ free(ret_str);
+ free(value_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ DEBUG("write_sensu plugin: Successfully created json for metric: "
+ "host = \"%s\", service = \"%s\"",
+ vl->host, service_buffer);
+ return ret_str;
+} /* }}} char *sensu_value_to_json */
+
+/*
+ * Uses replace_str2() implementation from
+ * http://creativeandcritical.net/str-replace-c/
+ * copyright (c) Laird Shaw, under public domain.
+ */
+char *replace_str(const char *str, const char *old, /* {{{ */
+ const char *new)
+{
+ char *ret, *r;
+ const char *p, *q;
+ size_t oldlen = strlen(old);
+ size_t count = strlen(new);
+ size_t retlen = count;
+ size_t newlen = count;
+ int samesize = (oldlen == newlen);
+
+ if (!samesize) {
+ for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen)
+ count++;
+ /* This is undefined if p - str > PTRDIFF_MAX */
+ retlen = p - str + strlen(p) + count * (newlen - oldlen);
+ } else
+ retlen = strlen(str);
+
+ ret = malloc(retlen + 1);
+ if (ret == NULL)
+ return NULL;
+ // added to original: not optimized, but keeps valgrind happy.
+ memset(ret, 0, retlen + 1);
+
+ r = ret;
+ p = str;
+ while (1) {
+ /* If the old and new strings are different lengths - in other
+ * words we have already iterated through with strstr above,
+ * and thus we know how many times we need to call it - then we
+ * can avoid the final (potentially lengthy) call to strstr,
+ * which we already know is going to return NULL, by
+ * decrementing and checking count.
+ */
+ if (!samesize && !count--)
+ break;
+ /* Otherwise i.e. when the old and new strings are the same
+ * length, and we don't know how many times to call strstr,
+ * we must check for a NULL return here (we check it in any
+ * event, to avoid further conditions, and because there's
+ * no harm done with the check even when the old and new
+ * strings are different lengths).
+ */
+ if ((q = strstr(p, old)) == NULL)
+ break;
+ /* This is undefined if q - p > PTRDIFF_MAX */
+ ptrdiff_t l = q - p;
+ memcpy(r, p, l);
+ r += l;
+ memcpy(r, new, newlen);
+ r += newlen;
+ p = q + oldlen;
+ }
+ strncpy(r, p, strlen(p));
+
+ return ret;
+} /* }}} char *replace_str */
+
+static char *replace_json_reserved(const char *message) /* {{{ */
+{
+ char *msg = replace_str(message, "\\", "\\\\");
+ if (msg == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ char *tmp = replace_str(msg, "\"", "\\\"");
+ free(msg);
+ if (tmp == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ msg = replace_str(tmp, "\n", "\\\n");
+ free(tmp);
+ if (msg == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ return msg;
+} /* }}} char *replace_json_reserved */
+
+static char *sensu_notification_to_json(struct sensu_host *host, /* {{{ */
+ notification_t const *n)
+{
+ char service_buffer[6 * DATA_MAX_NAME_LEN];
+ char const *severity;
+ notification_meta_t *meta;
+ char *ret_str;
+ char *temp_str;
+ int status;
+ int i;
+ int res;
+ // add the severity/status
+ switch (n->severity) {
+ case NOTIF_OKAY:
+ severity = "OK";
+ status = 0;
+ break;
+ case NOTIF_WARNING:
+ severity = "WARNING";
+ status = 1;
+ break;
+ case NOTIF_FAILURE:
+ severity = "CRITICAL";
+ status = 2;
+ break;
+ default:
+ severity = "UNKNOWN";
+ status = 3;
+ }
+ res = asprintf(&temp_str, "{\"status\": %d", status);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ // incorporate the timestamp
+ res = asprintf(&temp_str, "%s, \"timestamp\": %ld", ret_str, CDTIME_T_TO_TIME_T(n->time));
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ char *handlers_str = build_json_str_list("handlers", &(host->notification_handlers));
+ if (handlers_str == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ // incorporate the handlers
+ if (strlen(handlers_str) != 0) {
+ res = asprintf(&temp_str, "%s, %s", ret_str, handlers_str);
+ free(ret_str);
+ free(handlers_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ } else {
+ free(handlers_str);
+ }
+
+ // incorporate the plugin name information if any
+ if (n->plugin[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin\": \"%s\"", ret_str, n->plugin);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the plugin type if any
+ if (n->type[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_type\": \"%s\"", ret_str, n->type);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the plugin instance if any
+ if (n->plugin_instance[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_instance\": \"%s\"", ret_str, n->plugin_instance);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the plugin type instance if any
+ if (n->type_instance[0] != 0) {
+ res = asprintf(&temp_str, "%s, \"collectd_plugin_type_instance\": \"%s\"", ret_str, n->type_instance);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // add key value attributes from config if any
+ for (i = 0; i < sensu_attrs_num; i += 2) {
+ res = asprintf(&temp_str, "%s, \"%s\": \"%s\"", ret_str, sensu_attrs[i], sensu_attrs[i+1]);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate sensu tags from config if any
+ if (strlen(sensu_tags) != 0) {
+ res = asprintf(&temp_str, "%s, %s", ret_str, sensu_tags);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // incorporate the service name
+ sensu_format_name2(service_buffer, sizeof(service_buffer),
+ /* host */ "", n->plugin, n->plugin_instance,
+ n->type, n->type_instance, host->separator);
+ // replace sensu event name chars that are considered illegal
+ in_place_replace_sensu_name_reserved(service_buffer);
+ res = asprintf(&temp_str, "%s, \"name\": \"%s\"", ret_str, &service_buffer[1]);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ // incorporate the check output
+ if (n->message[0] != 0) {
+ char *msg = replace_json_reserved(n->message);
+ if (msg == NULL) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ res = asprintf(&temp_str, "%s, \"output\": \"%s - %s\"", ret_str, severity, msg);
+ free(ret_str);
+ free(msg);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+
+ // Pull in values from threshold and add extra attributes
+ for (meta = n->meta; meta != NULL; meta = meta->next) {
+ if (strcasecmp("CurrentValue", meta->name) == 0 && meta->type == NM_TYPE_DOUBLE) {
+ res = asprintf(&temp_str, "%s, \"current_value\": \"%.8f\"", ret_str, meta->nm_value.nm_double);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+ if (meta->type == NM_TYPE_STRING) {
+ res = asprintf(&temp_str, "%s, \"%s\": \"%s\"", ret_str, meta->name, meta->nm_value.nm_string);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+ }
+ }
+
+ // close the curly bracket
+ res = asprintf(&temp_str, "%s}\n", ret_str);
+ free(ret_str);
+ if (res == -1) {
+ ERROR(alloc_err);
+ return NULL;
+ }
+ ret_str = temp_str;
+
+ DEBUG("write_sensu plugin: Successfully created JSON for notification: "
+ "host = \"%s\", service = \"%s\", state = \"%s\"",
+ n->host, service_buffer, severity);
+ return ret_str;
+} /* }}} char *sensu_notification_to_json */
+
+static int sensu_send_msg(struct sensu_host *host, const char *msg) /* {{{ */
+{
+ int status = 0;
+ size_t buffer_len;
+
+ status = sensu_connect(host);
+ if (status != 0)
+ return status;
+
+ buffer_len = strlen(msg);
+
+ status = (int) swrite(host->s, msg, buffer_len);
+ sensu_close_socket(host);
+
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("write_sensu plugin: Sending to Sensu at %s:%s failed: %s",
+ (host->node != NULL) ? host->node : SENSU_HOST,
+ (host->service != NULL) ? host->service : SENSU_PORT,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ return 0;
+} /* }}} int sensu_send_msg */
+
+
+static int sensu_send(struct sensu_host *host, char const *msg) /* {{{ */
+{
+ int status = 0;
+
+ status = sensu_send_msg(host, msg);
+ if (status != 0) {
+ host->flags &= ~F_READY;
+ if (host->res != NULL) {
+ freeaddrinfo(host->res);
+ host->res = NULL;
+ }
+ return status;
+ }
+
+ return 0;
+} /* }}} int sensu_send */
+
+
+static int sensu_write(const data_set_t *ds, /* {{{ */
+ const value_list_t *vl,
+ user_data_t *ud)
+{
+ int status = 0;
+ int statuses[vl->values_len];
+ struct sensu_host *host = ud->data;
+ gauge_t *rates = NULL;
+ int i;
+ char *msg;
+
+ pthread_mutex_lock(&host->lock);
+ memset(statuses, 0, vl->values_len * sizeof(*statuses));
+
+ if (host->store_rates) {
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ ERROR("write_sensu plugin: uc_get_rate failed.");
+ pthread_mutex_unlock(&host->lock);
+ return -1;
+ }
+ }
+ for (i = 0; i < (size_t) vl->values_len; i++) {
+ msg = sensu_value_to_json(host, ds, vl, (int) i, rates, statuses[i]);
+ if (msg == NULL) {
+ sfree(rates);
+ pthread_mutex_unlock(&host->lock);
+ return -1;
+ }
+ status = sensu_send(host, msg);
+ free(msg);
+ if (status != 0) {
+ ERROR("write_sensu plugin: sensu_send failed with status %i", status);
+ pthread_mutex_unlock(&host->lock);
+ sfree(rates);
+ return status;
+ }
+ }
+ sfree(rates);
+ pthread_mutex_unlock(&host->lock);
+ return status;
+} /* }}} int sensu_write */
+
+static int sensu_notification(const notification_t *n, user_data_t *ud) /* {{{ */
+{
+ int status;
+ struct sensu_host *host = ud->data;
+ char *msg;
+
+ pthread_mutex_lock(&host->lock);
+
+ msg = sensu_notification_to_json(host, n);
+ if (msg == NULL) {
+ pthread_mutex_unlock(&host->lock);
+ return -1;
+ }
+
+ status = sensu_send(host, msg);
+ free(msg);
+ if (status != 0)
+ ERROR("write_sensu plugin: sensu_send failed with status %i", status);
+ pthread_mutex_unlock(&host->lock);
+
+ return status;
+} /* }}} int sensu_notification */
+
+static void sensu_free(void *p) /* {{{ */
+{
+ struct sensu_host *host = p;
+
+ if (host == NULL)
+ return;
+
+ pthread_mutex_lock(&host->lock);
+
+ host->reference_count--;
+ if (host->reference_count > 0) {
+ pthread_mutex_unlock(&host->lock);
+ return;
+ }
+
+ sensu_close_socket(host);
+ if (host->res != NULL) {
+ freeaddrinfo(host->res);
+ host->res = NULL;
+ }
+ sfree(host->service);
+ sfree(host->event_service_prefix);
+ sfree(host->name);
+ sfree(host->node);
+ sfree(host->separator);
+ free_str_list(&(host->metric_handlers));
+ free_str_list(&(host->notification_handlers));
+ pthread_mutex_destroy(&host->lock);
+ sfree(host);
+} /* }}} void sensu_free */
+
+
+static int sensu_config_node(oconfig_item_t *ci) /* {{{ */
+{
+ struct sensu_host *host = NULL;
+ int status = 0;
+ int i;
+ oconfig_item_t *child;
+ char callback_name[DATA_MAX_NAME_LEN];
+ user_data_t ud;
+
+ if ((host = calloc(1, sizeof(*host))) == NULL) {
+ ERROR("write_sensu plugin: calloc failed.");
+ return ENOMEM;
+ }
+ pthread_mutex_init(&host->lock, NULL);
+ host->reference_count = 1;
+ host->node = NULL;
+ host->service = NULL;
+ host->notifications = 0;
+ host->metrics = 0;
+ host->store_rates = 1;
+ host->always_append_ds = 0;
+ host->metric_handlers.nb_strs = 0;
+ host->metric_handlers.strs = NULL;
+ host->notification_handlers.nb_strs = 0;
+ host->notification_handlers.strs = NULL;
+ host->separator = strdup("/");
+ if (host->separator == NULL) {
+ ERROR(alloc_err);
+ sensu_free(host);
+ return -1;
+ }
+
+ status = cf_util_get_string(ci, &host->name);
+ if (status != 0) {
+ WARNING("write_sensu plugin: Required host name is missing.");
+ sensu_free(host);
+ return -1;
+ }
+
+ for (i = 0; i < ci->children_num; i++) {
+ child = &ci->children[i];
+ status = 0;
+
+ if (strcasecmp("Host", child->key) == 0) {
+ status = cf_util_get_string(child, &host->node);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("Notifications", child->key) == 0) {
+ status = cf_util_get_boolean(child, &host->notifications);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("Metrics", child->key) == 0) {
+ status = cf_util_get_boolean(child, &host->metrics);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("EventServicePrefix", child->key) == 0) {
+ status = cf_util_get_string(child, &host->event_service_prefix);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("Separator", child->key) == 0) {
+ status = cf_util_get_string(child, &host->separator);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("MetricHandler", child->key) == 0) {
+ char *temp_str = NULL;
+ status = cf_util_get_string(child, &temp_str);
+ if (status != 0)
+ break;
+ status = add_str_to_list(&(host->metric_handlers), temp_str);
+ free(temp_str);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("NotificationHandler", child->key) == 0) {
+ char *temp_str = NULL;
+ status = cf_util_get_string(child, &temp_str);
+ if (status != 0)
+ break;
+ status = add_str_to_list(&(host->notification_handlers), temp_str);
+ free(temp_str);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("Port", child->key) == 0) {
+ status = cf_util_get_service(child, &host->service);
+ if (status != 0) {
+ ERROR("write_sensu plugin: Invalid argument "
+ "configured for the \"Port\" "
+ "option.");
+ break;
+ }
+ } else if (strcasecmp("StoreRates", child->key) == 0) {
+ status = cf_util_get_boolean(child, &host->store_rates);
+ if (status != 0)
+ break;
+ } else if (strcasecmp("AlwaysAppendDS", child->key) == 0) {
+ status = cf_util_get_boolean(child,
+ &host->always_append_ds);
+ if (status != 0)
+ break;
+ } else {
+ WARNING("write_sensu plugin: ignoring unknown config "
+ "option: \"%s\"", child->key);
+ }
+ }
+ if (status != 0) {
+ sensu_free(host);
+ return status;
+ }
+
+ if (host->metrics && (host->metric_handlers.nb_strs == 0)) {
+ sensu_free(host);
+ WARNING("write_sensu plugin: metrics enabled but no MetricHandler defined. Giving up.");
+ return -1;
+ }
+
+ if (host->notifications && (host->notification_handlers.nb_strs == 0)) {
+ sensu_free(host);
+ WARNING("write_sensu plugin: notifications enabled but no NotificationHandler defined. Giving up.");
+ return -1;
+ }
+
+ if ((host->notification_handlers.nb_strs > 0) && (host->notifications == 0)) {
+ WARNING("write_sensu plugin: NotificationHandler given so forcing notifications to be enabled");
+ host->notifications = 1;
+ }
+
+ if ((host->metric_handlers.nb_strs > 0) && (host->metrics == 0)) {
+ WARNING("write_sensu plugin: MetricHandler given so forcing metrics to be enabled");
+ host->metrics = 1;
+ }
+
+ if (!(host->notifications || host->metrics)) {
+ WARNING("write_sensu plugin: neither metrics nor notifications enabled. Giving up.");
+ sensu_free(host);
+ return -1;
+ }
+
+ ssnprintf(callback_name, sizeof(callback_name), "write_sensu/%s", host->name);
+ ud.data = host;
+ ud.free_func = sensu_free;
+
+ pthread_mutex_lock(&host->lock);
+
+ if (host->metrics) {
+ status = plugin_register_write(callback_name, sensu_write, &ud);
+ if (status != 0)
+ WARNING("write_sensu plugin: plugin_register_write (\"%s\") "
+ "failed with status %i.",
+ callback_name, status);
+ else /* success */
+ host->reference_count++;
+ }
+
+ if (host->notifications) {
+ status = plugin_register_notification(callback_name, sensu_notification, &ud);
+ if (status != 0)
+ WARNING("write_sensu plugin: plugin_register_notification (\"%s\") "
+ "failed with status %i.",
+ callback_name, status);
+ else
+ host->reference_count++;
+ }
+
+ if (host->reference_count <= 1) {
+ /* Both callbacks failed => free memory.
+ * We need to unlock here, because sensu_free() will lock.
+ * This is not a race condition, because we're the only one
+ * holding a reference. */
+ pthread_mutex_unlock(&host->lock);
+ sensu_free(host);
+ return -1;
+ }
+
+ host->reference_count--;
+ pthread_mutex_unlock(&host->lock);
+
+ return status;
+} /* }}} int sensu_config_node */
+
+static int sensu_config(oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+ oconfig_item_t *child;
+ int status;
+ struct str_list sensu_tags_arr;
+
+ sensu_tags_arr.nb_strs = 0;
+ sensu_tags_arr.strs = NULL;
+ sensu_tags = malloc(sizeof(char));
+ if (sensu_tags == NULL) {
+ ERROR(alloc_err);
+ return -1;
+ }
+ sensu_tags[0] = '\0';
+
+ for (i = 0; i < ci->children_num; i++) {
+ child = &ci->children[i];
+
+ if (strcasecmp("Node", child->key) == 0) {
+ sensu_config_node(child);
+ } else if (strcasecmp(child->key, "attribute") == 0) {
+ char *key = NULL;
+ char *val = NULL;
+
+ if (child->values_num != 2) {
+ WARNING("sensu attributes need both a key and a value.");
+ free(sensu_tags);
+ return -1;
+ }
+ if (child->values[0].type != OCONFIG_TYPE_STRING ||
+ child->values[1].type != OCONFIG_TYPE_STRING) {
+ WARNING("sensu attribute needs string arguments.");
+ free(sensu_tags);
+ return -1;
+ }
+ if ((key = strdup(child->values[0].value.string)) == NULL) {
+ ERROR(alloc_err);
+ free(sensu_tags);
+ return -1;
+ }
+ if ((val = strdup(child->values[1].value.string)) == NULL) {
+ free(sensu_tags);
+ free(key);
+ ERROR(alloc_err);
+ return -1;
+ }
+ strarray_add(&sensu_attrs, &sensu_attrs_num, key);
+ strarray_add(&sensu_attrs, &sensu_attrs_num, val);
+ DEBUG("write_sensu: got attr: %s => %s", key, val);
+ sfree(key);
+ sfree(val);
+ } else if (strcasecmp(child->key, "tag") == 0) {
+ char *tmp = NULL;
+ status = cf_util_get_string(child, &tmp);
+ if (status != 0)
+ continue;
+
+ status = add_str_to_list(&sensu_tags_arr, tmp);
+ sfree(tmp);
+ if (status != 0)
+ continue;
+ DEBUG("write_sensu plugin: Got tag: %s", tmp);
+ } else {
+ WARNING("write_sensu plugin: Ignoring unknown "
+ "configuration option \"%s\" at top level.",
+ child->key);
+ }
+ }
+ if (sensu_tags_arr.nb_strs > 0) {
+ free(sensu_tags);
+ sensu_tags = build_json_str_list("tags", &sensu_tags_arr);
+ free_str_list(&sensu_tags_arr);
+ if (sensu_tags == NULL) {
+ ERROR(alloc_err);
+ return -1;
+ }
+ }
+ return 0;
+} /* }}} int sensu_config */
+
+void module_register(void)
+{
+ plugin_register_complex_config("write_sensu", sensu_config);
+}
+
+/* vim: set sw=8 sts=8 ts=8 noet : */