David Bacher <drbacher at gmail.com>
- serial plugin.
+Denis Pompilio <denis.pompilio at gmail.com>
+ - Improvements to the write_http plugin.
+
Doug MacEachern <dougm at hyperic.com>
- The `-T' option (config testing mode).
- OpenVPN plugin.
embedded HTTP server, in a format compatible with Prometheus'
collectd_exporter. Thanks to Florian Forster. #1967
+2017-10-06, Version 5.6.3
+ * collectd: support for boolean string config values has been
+ reintroduced. Thanks to Sebastian Harl. #2083, #2098
+ * collectd: The capability checking has been changed to use
+ "cap_get_proc()". Thanks to Marc Fournier. #2151
+ * Documentation: A section documenting ignore lists has been added to
+ collectd.conf(5). Thanks to Florian Forster.
+ * AMQP plugin: The "ExchangeType" option is now also valid for
+ publishers. Thanks to Florian Forster. #2286
+ * Apache, Ascent, BIND, cURL, cURL-JSON, cURL-XML, nginx, Write HTTP
+ plugins: Handling of URLs that redirect elsewhere has been fixed.
+ Thanks to Pavel Rochnyack. #2328
+ * BIND plugin: Fix parsing of the sample time provided by BIND.
+ Previously, the time was assumed to be in the local time zone when in
+ fact it was in UTC. Thanks to Ed Ravin. #1268
+ * BIND plugin: Memory leaks have been fixed. Thanks to Ruben Kerkhof.
+ #2303
+ * Chrony plugin: Build flags have been fixed. Thanks to Thomas Jost and
+ Marc Fournier. #2133
+ * cURL-JSON plugin: The timeout value has been changed to default to the
+ collection interval. This fixes a regression. Thanks to Marc Fournier.
+ * cURL-JSON plugin: Handling of arrays has been fixed. Thanks to Florian
+ Forster. #2266
+ * DBI plugin: Memory leaks at shutdown have been fixes. Thanks to Pavel
+ Rochnyack and Florian Forster.
+ * E-Mail, Exec, UnixSock plugins: Group ID lookup on systems with many
+ groups has been fixed. Thanks to Ruben Kerkhof and Florian Forster.
+ #2208
+ * IPC plugin: A compilation error on AIX has been fixed. Thanks to Pavel
+ Rochnyack. #2305
+ * LogFile plugin: If writing to the file fails, print log messages on
+ "STDERR" instead. Thanks to Marc Fournier.
+ * Log Logstash plugin: If writing the log entry fails, print it to
+ "STDERR" instead. Thanks to Marc Fournier.
+ * memcachec, Tail plugins: A resource leak in the matching
+ infrastructure has been fixed. Thanks to Krzysztof Matczak. #2192
+ * MQTT plugin: Invalid symbols in topic names are now replaced and a
+ resource leak has been fixed. Thanks to Denys Fedoryshchenko. #2123
+ * Network plugin: A potential endless-loop has been fixed. This can be
+ triggered remotely by sending a signed network packet to a server
+ which is not set up to check signatures. Thanks to Marcin Kozlowski
+ and Pavel Rochnyack. #2174, #2233, CVE-2017-7401
+ * Network plugin: A use-after-free has been fixed. Thanks to Pavel
+ Rochnyack. #2375
+ * Notify Email plugin: The plugin is no longer explicitly linked against
+ libssl and libcrypto, relies on libesmtp being linked correctly.
+ Thanks to Marc Fournier. Debian#852924
+ * NTPd plugin: Calculation of loop offset and error has been fixed.
+ Thanks to Neil Wilson. #2188
+ * OpenLDAP plugin: An incorrect use of the ldap library, leading to a
+ crash, has been fixed. Thanks to Marc Fournier. #2331
+ * Perl plugin: A potential double-free has been fixed. Thanks to Florian
+ Forster. #2278
+ * Perl plugin: Print an error when an incorrect configuration is
+ encountered. Thanks to Pavel Rochnyack. #927
+ * RRDtool plugin: Incorrect handling of the flushes timeout option has
+ been fixed. Handling of the "RandomTimeout" has been fixed. Thanks to
+ Pavel Rochnyack. #2363
+ * SMART plugin: Some warning messages have been removed and the code has
+ been cleaned up. Thanks to Florian Forster. #2062
+ * SMART plugin: A check for the "CAP_SYS_RAWIO" capability has been
+ added. Thanks to Marc Fournier.
+ * SNMP plugin: A double free has been fixed. Thanks to Pavel Rochnyack.
+ #2291
+ * Write Graphite plugin: Error handling in the case that calculating a
+ metric's rate fails has been improved. Previously, the raw counter
+ values were sent to Graphite. Thanks to Iain Buclaw. #2209
+ * Write Kafka plugin: A 32 bit random number is now used when formatting
+ a random key. Thanks to Florian Forster. #2074
+
+
2016-11-30, Version 5.6.2
* collectd: A compile error on AIX has been fixed: "MSG_DONTWAIT" is not
available on AIX. Thanks to Chao Yang.
=over 4
-=item B<dispatch>([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None. Dispatch a value list.
+=item B<dispatch>([type][, message][, plugin_instance][, type_instance][, plugin][, host][, time][, severity][, meta]) -> None. Dispatch a notification.
Dispatch this instance to the collectd process. The object has members for each
of the possible arguments for this method. For a detailed explanation of these
The severity of this notification. Assign or compare to I<NOTIF_FAILURE>,
I<NOTIF_WARNING> or I<NOTIF_OKAY>.
+=item meta
+
+These are the meta data for the Notification object.
+It has to be a dictionary of numbers, strings or bools. All keys must be
+strings. I<int> and I<long> objects will be dispatched as signed integers unless
+they are between 2**63 and 2**64-1, which will result in a unsigned integer.
+One of these storage classes can be forced by using the classes
+B<collectd.Signed> and B<collectd.Unsigned>. A meta object received by a
+notification callback will always contain B<Signed> or B<Unsigned> objects.
+
=back
=head1 FUNCTIONS
Community "community_string"
Collect "std_traffic"
Interval 120
+ Timeout 10
+ Retries 1
</Host>
<Host "some.server.mydomain.org">
Address "192.168.0.42"
Community "more_communities"
Collect "powerplus_voltge_input"
Interval 300
+ Timeout 5
+ Retries 5
</Host>
</Plugin>
=head1 CONFIGURATION
Since the aim of the C<snmp plugin> is to provide a generic interface to SNMP,
-it's configuration is not trivial and may take some time.
+its configuration is not trivial and may take some time.
Since the C<Net-SNMP> library is used you can use all the environment variables
that are interpreted by that package. See L<snmpcmd(1)> for more details.
B<Step> of generated RRD files depends on this setting it's wise to select a
reasonable value once and never change it.
+=item B<Timeout> I<Seconds>
+
+How long to wait for a response. The C<Net-SNMP> library default is 1 second.
+
+=item B<Retries> I<Integer>
+
+The number of times that a query should be retried after the Timeout expires.
+The C<Net-SNMP> library default is 5.
+
=back
=head1 SEE ALSO
# Size "+10k"
# Recursive true
# IncludeHidden false
+# RegularOnly true
# #FilesSizeType "bytes"
# #FilesCountType "files"
# #TypeInstance "instance"
#</Plugin>
#<Plugin ipmi>
-# Sensor "some_sensor"
-# Sensor "another_one"
-# IgnoreSelected false
-# NotifySensorAdd false
-# NotifySensorRemove true
-# NotifySensorNotPresent false
+# <Instance "local">
+# Sensor "some_sensor"
+# Sensor "another_one"
+# IgnoreSelected false
+# NotifySensorAdd false
+# NotifySensorRemove true
+# NotifySensorNotPresent false
+# NotifyIPMIConnectionState false
+# SELEnabled false
+# SELClearEvent false
+# </Instance>
+# <Instance "remote">
+# Host "server.example.com"
+# Address "1.2.3.4"
+# Username "user"
+# Password "secret"
+# #AuthType "md5"
+# Sensor "some_sensor"
+# Sensor "another_one"
+# IgnoreSelected false
+# NotifySensorAdd false
+# NotifySensorRemove true
+# NotifySensorNotPresent false
+# NotifyIPMIConnectionState false
+# SELEnabled false
+# SELClearEvent false
+# </Instance>
#</Plugin>
#<Plugin iptables>
# Community "community_string"
# Collect "std_traffic"
# Interval 120
+# Timeout 10
+# Retries 1
# </Host>
# <Host "some.server.mydomain.org">
# Address "192.168.0.42"
# Community "more_communities"
# Collect "powerplus_voltge_input"
# Interval 300
+# Timeout 5
+# Retries 5
# </Host>
#</Plugin>
# CollectManagement false # Varnish 4 only
# CollectSMF false # Varnish 4 only
# CollectVBE false # Varnish 4 only
+# CollectMSE false # Varnish-Plus 4 only
# </Instance>
#</Plugin>
# Header "X-Custom-Header: custom_value"
# SSLVersion "TLSv1"
# Format "Command"
+# Prefix "collectd" # metric prefix, only available for KAIROSDB format
# Attribute "key" "value" # only available for KAIROSDB format
# TTL 0 # data ttl, only available for KAIROSDB format
# Metrics true
"Hidden" files and directories are those, whose name begins with a dot.
Defaults to I<false>, i.e. by default hidden files and directories are ignored.
+=item B<RegularOnly> I<true>|I<false>
+
+Controls whether or not to include only regular files in the count.
+Defaults to I<true>, i.e. by default non regular files are ignored.
+
=item B<FilesSizeType> I<Type>
Sets the type used to dispatch files combined size. Empty value ("") disables
=head2 Plugin C<ipmi>
+The B<ipmi plugin> allows to monitor server platform status using the Intelligent
+Platform Management Interface (IPMI). Local and remote interfaces are supported.
+
+The plugin configuration consists of one or more B<Instance> blocks which
+specify one I<ipmi> connection each. Each block requires one unique string
+argument as the instance name. If instances are not configured, an instance with
+the default option values will be created.
+
+For backwards compatibility, any option other than B<Instance> block will trigger
+legacy config handling and it will be treated as an option within B<Instance>
+block. This support will go away in the next major version of Collectd.
+
+Within the B<Instance> blocks, the following options are allowed:
+
=over 4
+=item B<Address> I<Address>
+
+Hostname or IP to connect to. If not specified, plugin will try to connect to
+local management controller (BMC).
+
+=item B<Username> I<Username>
+
+=item B<Password> I<Password>
+
+The username and the password to use for the connection to remote BMC.
+
+=item B<AuthType> I<MD5>|I<rmcp+>
+
+Forces the authentication type to use for the connection to remote BMC.
+By default most secure type is seleted.
+
+=item B<Host> I<Hostname>
+
+Sets the B<host> field of dispatched values. Defaults to the global hostname
+setting.
+
=item B<Sensor> I<Sensor>
Selects sensors to collect or to ignore, depending on B<IgnoreSelected>.
If you have for example dual power supply and one of them is (un)plugged then
a notification is sent.
+=item B<NotifyIPMIConnectionState> I<true>|I<false>
+
+If a IPMI connection state changes after initialization time of a minute
+a notification is sent. Defaults to B<false>.
+
+=item B<SELEnabled> I<true>|I<false>
+
+If system event log (SEL) is enabled, plugin will listen for sensor threshold
+and discrete events. When event is received the notification is sent.
+Defaults to B<false>.
+
+=item B<SELClearEvent> I<true>|I<false>
+
+If SEL clear event is enabled, plugin will delete event from SEL list after
+it is received and successfully handled. In this case other tools that are
+subscribed for SEL events will receive an empty event.
+Defaults to B<false>.
+
=back
=head2 Plugin C<iptables>
=head2 Plugin C<processes>
-=over 4
+Collects information about processes of local system.
-=item B<Process> I<Name>
+By default, with no process matches configured, only general statistics is
+collected: the number of processes in each state and fork rate.
-Select more detailed statistics of processes matching this name. The statistics
-collected for these selected processes are:
+Process matches can be configured by B<Process> and B<ProcessMatch> options.
+These may also be a block in which further options may be specified.
+
+The statistics collected for matched processes are:
- size of the resident segment size (RSS)
- user- and system-time used
- number of processes
- context switches (under Linux)
- minor and major pagefaults.
-Some platforms have a limit on the length of process names. I<Name> must stay
-below this limit.
+B<Synopsis:>
+
+ <Plugin processes>
+ CollectFileDescriptor true
+ CollectContextSwitch true
+ Process "name"
+ ProcessMatch "name" "regex"
+ <Process "collectd">
+ CollectFileDescriptor false
+ CollectContextSwitch false
+ </Process>
+ <ProcessMatch "name" "regex">
+ CollectFileDescriptor false
+ CollectContextSwitch true
+ </Process>
+ </Plugin>
+
+=over 4
+
+=item B<Process> I<Name>
+
+Select more detailed statistics of processes matching this name.
+
+Some platforms have a limit on the length of process names.
+I<Name> must stay below this limit.
=item B<ProcessMatch> I<name> I<regex>
-Similar to the B<Process> option this allows one to select more detailed
-statistics of processes matching the specified I<regex> (see L<regex(7)> for
-details). The statistics of all matching processes are summed up and
-dispatched to the daemon using the specified I<name> as an identifier. This
-allows one to "group" several processes together. I<name> must not contain
-slashes.
+Select more detailed statistics of processes matching the specified I<regex>
+(see L<regex(7)> for details). The statistics of all matching processes are
+summed up and dispatched to the daemon using the specified I<name> as an
+identifier. This allows one to "group" several processes together.
+I<name> must not contain slashes.
=item B<CollectContextSwitch> I<Boolean>
-Collect context switch of the process.
+Collect the number of context switches for matched processes.
+Disabled by default.
+
+=item B<CollectFileDescriptor> I<Boolean>
+
+Collect number of file descriptors of matched processes.
+Disabled by default.
=item B<CollectMemoryMaps> I<Boolean>
=back
+Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
+B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
+only. Otherwise they set the default value for subsequent matches.
+
=head2 Plugin C<protocols>
Collects a lot of information about various network protocols, such as I<IP>,
CollectManagement false
CollectSMF false
CollectVBE false
+ CollectMSE false
</Instance>
</Plugin>
=item B<CollectSMA> B<true>|B<false>
malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
-component is Solaris specific.
-Note: SMA and SMF share counters, enable only the one used by the Varnish
-instance.
-Only available with Varnish 2.x. False by
-default.
+component is Solaris specific. Note: SMA, SMF and MSE share counters, enable
+only the one used by the Varnish instance. Only available with Varnish 2.x.
+False by default.
=item B<CollectSMS> B<true>|B<false>
=item B<CollectSMF> B<true>|B<false>
file (memory mapped file) storage statistics. Only available with Varnish 4.x.
-Note: SMA and SMF share counters, enable only the one used by the Varnish
-instance.
-Used to be called SM in Varnish 2.x. False by default.
+Note: SMA, SMF and MSE share counters, enable only the one used by the Varnish
+instance. Used to be called SM in Varnish 2.x. False by default.
=item B<CollectManagement> B<true>|B<false>
Memory pool counters. Only available with Varnish 4.x. False by default.
+=item B<CollectMSE> B<true>|B<false>
+
+Varnish Massive Storage Engine 2.0 (MSE2) is an improved storage backend for
+Varnish, replacing the traditional malloc and file storages. Only available
+with Varnish-Plus 4.x. Note: SMA, SMF and MSE share counters, enable only the
+one used by the Varnish instance. False by default.
+
=back
=head2 Plugin C<virt>
Please refer to L<http://kairosdb.github.io/docs/build/html/restapi/AddDataPoints.html?highlight=ttl>
+=item B<Prefix> I<String>
+
+Only available for the KAIROSDB output format.
+
+Sets the metrics prefix I<string>. Defaults to I<collectd>.
+
=item B<Metrics> B<true>|B<false>
Controls whether I<metrics> are POSTed to this location. Defaults to B<true>.
typedef struct {
PluginData data;
+ PyObject *meta; /* dict */
int severity;
char message[NOTIF_MAX_MSG_LEN];
} Notification;
#define CJ_CB_ABORT 0
#define CJ_CB_CONTINUE 1
-static int cj_cb_boolean(void *ctx, int boolVal) {
- cj_advance_array(ctx);
- return CJ_CB_CONTINUE;
-}
-
static int cj_cb_null(void *ctx) {
cj_advance_array(ctx);
return CJ_CB_CONTINUE;
return cj_cb_number(ctx, (const char *)val, len);
} /* int cj_cb_string */
+static int cj_cb_boolean(void *ctx, int boolVal) {
+ if (boolVal)
+ return cj_cb_number(ctx, "1", 1);
+ else
+ return cj_cb_number(ctx, "0", 1);
+} /* int cj_cb_boolean */
+
static int cj_cb_end(void *ctx) {
cj_t *db = (cj_t *)ctx;
memset(&db->state[db->depth], 0, sizeof(db->state[db->depth]));
}
#endif
-ssize_t sread(int fd, void *buf, size_t count) {
+int sread(int fd, void *buf, size_t count) {
char *ptr;
size_t nleft;
ssize_t status;
return status;
if (status == 0) {
- DEBUG("Received EOF from fd %i. "
- "Closing fd and returning error.",
- fd);
- close(fd);
+ DEBUG("Received EOF from fd %i. ", fd);
return -1;
}
return 0;
}
-ssize_t swrite(int fd, const void *buf, size_t count) {
+int swrite(int fd, const void *buf, size_t count) {
const char *ptr;
size_t nleft;
ssize_t status;
*
* DESCRIPTION
* Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
- * to `read(2)'. If EOF is received the file descriptor is closed and an
- * error is returned.
+ * to `read(2)'.
*
* PARAMETERS
* `fd' File descriptor to write to.
* Zero upon success or non-zero if an error occurred. `errno' is set in this
* case.
*/
-ssize_t sread(int fd, void *buf, size_t count);
+int sread(int fd, void *buf, size_t count);
/*
* NAME
* Zero upon success or non-zero if an error occurred. `errno' is set in this
* case.
*/
-ssize_t swrite(int fd, const void *buf, size_t count);
+int swrite(int fd, const void *buf, size_t count);
/*
* NAME
#define FC_RECURSIVE 1
#define FC_HIDDEN 2
+#define FC_REGULAR 4
struct fc_directory_conf_s {
char *path;
return -1;
}
- dir->options = FC_RECURSIVE;
+ dir->options = FC_RECURSIVE | FC_REGULAR;
dir->name = NULL;
dir->plugin_name = strdup("filecount");
status = fc_config_add_dir_option(dir, option, FC_RECURSIVE);
else if (strcasecmp("IncludeHidden", option->key) == 0)
status = fc_config_add_dir_option(dir, option, FC_HIDDEN);
+ else if (strcasecmp("RegularOnly", option->key) == 0)
+ status = fc_config_add_dir_option(dir, option, FC_REGULAR);
else if (strcasecmp("FilesSizeType", option->key) == 0)
status = cf_util_get_string(option, &dir->files_size_type);
else if (strcasecmp("FilesCountType", option->key) == 0)
abs_path, fc_read_dir_callback, dir,
/* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
return status;
- } else if (!S_ISREG(statbuf.st_mode)) {
+ } else if ((dir->options & FC_REGULAR) && !S_ISREG(statbuf.st_mode)) {
return 0;
}
return 0;
}
+ if (!S_ISREG(statbuf.st_mode)) {
+ dir->files_num++;
+ return 0;
+ }
+
if (dir->mtime != 0) {
time_t mtime = dir->now;
_Bool hw_cache_events;
_Bool kernel_pmu_events;
_Bool sw_events;
- char event_list_fn[PATH_MAX];
+ char event_list_fn[PATH_MAX];
char **hw_events;
size_t hw_events_count;
struct eventlist *event_list;
return 0;
}
-static void pmu_submit_counter(int cpu, char *event, counter_t value) {
+static void pmu_submit_counter(int cpu, char *event, counter_t value,
+ meta_data_t *meta) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = &(value_t){.counter = value};
if (cpu == -1) {
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
} else {
+ vl.meta = meta;
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
}
sstrncpy(vl.type, "counter", sizeof(vl.type));
plugin_dispatch_values(&vl);
}
+meta_data_t *pmu_meta_data_create(const struct efd *efd) {
+ meta_data_t *meta = NULL;
+
+ /* create meta data only if value was scaled */
+ if (efd->val[1] == efd->val[2] || !efd->val[2]) {
+ return NULL;
+ }
+
+ meta = meta_data_create();
+ if (meta == NULL) {
+ ERROR(PMU_PLUGIN ": meta_data_create failed.");
+ return NULL;
+ }
+
+ meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]);
+ meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]);
+ meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]);
+
+ return meta;
+}
+
static void pmu_dispatch_data(void) {
struct event *e;
event_enabled++;
+ /* If there are more events than counters, the kernel uses time
+ * multiplexing. With multiplexing, at the end of the run,
+ * the counter is scaled basing on total time enabled vs time running.
+ * final_count = raw_count * time_enabled/time_running
+ */
uint64_t value = event_scaled_value(e, i);
all_value += value;
+ /* get meta data with information about scaling */
+ meta_data_t *meta = pmu_meta_data_create(&e->efd[i]);
+
/* dispatch per CPU value */
- pmu_submit_counter(i, e->event, value);
+ pmu_submit_counter(i, e->event, value, meta);
+
+ meta_data_destroy(meta);
}
if (event_enabled > 0) {
DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
/* dispatch all CPU value */
- pmu_submit_counter(-1, e->event, all_value);
+ pmu_submit_counter(-1, e->event, all_value, NULL);
}
}
}
sfree(g_ctx.hw_events);
g_ctx.hw_events_count = 0;
-
return ret;
}
* Florian octo Forster <octo at collectd.org>
* Peter Holik <peter at holik.at>
* Bruno Prémont <bonbons at linux-vserver.org>
+ * Pavel Rochnyak <pavel2000 ngs.ru>
**/
#include "collectd.h"
#include "plugin.h"
#include "utils_ignorelist.h"
+#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_conn.h>
#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_lan.h>
#include <OpenIPMI/ipmi_posix.h>
#include <OpenIPMI/ipmi_smi.h>
#include <OpenIPMI/ipmiif.h>
+#define ERR_BUF_SIZE 1024
+
/*
* Private data types
*/
struct c_ipmi_sensor_list_s;
typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
+struct c_ipmi_instance_s {
+ char *name;
+ ignorelist_t *ignorelist;
+ _Bool notify_add;
+ _Bool notify_remove;
+ _Bool notify_notpresent;
+ _Bool notify_conn;
+ _Bool sel_enabled;
+ _Bool sel_clear_event;
+
+ char *host;
+ char *connaddr;
+ char *username;
+ char *password;
+ unsigned int authtype;
+
+ _Bool connected;
+ ipmi_con_t *connection;
+ pthread_mutex_t sensor_list_lock;
+ c_ipmi_sensor_list_t *sensor_list;
+
+ _Bool active;
+ pthread_t thread_id;
+ int init_in_progress;
+
+ struct c_ipmi_instance_s *next;
+};
+typedef struct c_ipmi_instance_s c_ipmi_instance_t;
+
struct c_ipmi_sensor_list_s {
ipmi_sensor_id_t sensor_id;
char sensor_name[DATA_MAX_NAME_LEN];
char sensor_type[DATA_MAX_NAME_LEN];
int sensor_not_present;
c_ipmi_sensor_list_t *next;
+ c_ipmi_instance_t *instance;
+ unsigned int use;
};
/*
* Module global variables
*/
-static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static c_ipmi_sensor_list_t *sensor_list = NULL;
-
-static int c_ipmi_init_in_progress = 0;
-static int c_ipmi_active = 0;
-static pthread_t thread_id = (pthread_t)0;
-
-static const char *config_keys[] = {"Sensor", "IgnoreSelected",
- "NotifySensorAdd", "NotifySensorRemove",
- "NotifySensorNotPresent"};
-static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
-
-static ignorelist_t *ignorelist = NULL;
-
-static int c_ipmi_nofiy_add = 0;
-static int c_ipmi_nofiy_remove = 0;
-static int c_ipmi_nofiy_notpresent = 0;
+static os_handler_t *os_handler = NULL;
+static c_ipmi_instance_t *instances = NULL;
/*
* Misc private functions
*/
-static void c_ipmi_error(const char *func, int status) {
- char errbuf[4096] = {0};
+static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
+ char errbuf[ERR_BUF_SIZE] = {0};
- if (IPMI_IS_OS_ERR(status)) {
- sstrerror(IPMI_GET_OS_ERR(status), errbuf, sizeof(errbuf));
- } else if (IPMI_IS_IPMI_ERR(status)) {
- ipmi_get_error_string(IPMI_GET_IPMI_ERR(status), errbuf, sizeof(errbuf));
+ if (IPMI_IS_OS_ERR(status) || IPMI_IS_RMCPP_ERR(status) ||
+ IPMI_IS_IPMI_ERR(status)) {
+ ipmi_get_error_string(status, errbuf, sizeof(errbuf));
}
if (errbuf[0] == 0) {
}
errbuf[sizeof(errbuf) - 1] = 0;
- ERROR("ipmi plugin: %s failed: %s", func, errbuf);
+ ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf);
} /* void c_ipmi_error */
+static void c_ipmi_log(os_handler_t *handler, const char *format,
+ enum ipmi_log_type_e log_type, va_list ap) {
+ char msg[ERR_BUF_SIZE];
+
+ vsnprintf(msg, sizeof(msg), format, ap);
+
+ switch (log_type) {
+ case IPMI_LOG_INFO:
+ INFO("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_WARNING:
+ NOTICE("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_SEVERE:
+ WARNING("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_FATAL:
+ ERROR("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_ERR_INFO:
+ ERROR("ipmi plugin: %s", msg);
+ break;
+#if COLLECT_DEBUG
+ case IPMI_LOG_DEBUG_START:
+ case IPMI_LOG_DEBUG:
+ DEBUG("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_DEBUG_CONT:
+ case IPMI_LOG_DEBUG_END:
+ DEBUG("%s", msg);
+ break;
+#else
+ case IPMI_LOG_DEBUG_START:
+ case IPMI_LOG_DEBUG:
+ case IPMI_LOG_DEBUG_CONT:
+ case IPMI_LOG_DEBUG_END:
+ break;
+#endif
+ }
+} /* void c_ipmi_log */
+
+static notification_t c_ipmi_notification_init(c_ipmi_instance_t const *st,
+ int severity) {
+ notification_t n = {severity, cdtime(), "", "", "ipmi", "", "", "", NULL};
+
+ sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
+ return n;
+} /* notification_t c_ipmi_notification_init */
+
/*
* Sensor handlers
*/
/* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
-static int sensor_list_remove(ipmi_sensor_t *sensor);
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor);
static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
enum ipmi_value_present_e value_present,
unsigned int __attribute__((unused)) raw_value,
- double value,
- ipmi_states_t __attribute__((unused)) * states,
+ double value, ipmi_states_t *states,
void *user_data) {
value_list_t vl = VALUE_LIST_INIT;
- c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
+ c_ipmi_sensor_list_t *list_item = user_data;
+ c_ipmi_instance_t *st = list_item->instance;
+
+ list_item->use--;
if (err != 0) {
- if ((err & 0xff) == IPMI_NOT_PRESENT_CC) {
+ if (IPMI_IS_IPMI_ERR(err) &&
+ IPMI_GET_IPMI_ERR(err) == IPMI_NOT_PRESENT_CC) {
if (list_item->sensor_not_present == 0) {
list_item->sensor_not_present = 1;
- INFO("ipmi plugin: sensor_read_handler: sensor %s "
+ INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` "
"not present.",
- list_item->sensor_name);
+ list_item->sensor_name, st->name);
- if (c_ipmi_nofiy_notpresent) {
- notification_t n = {
- NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
+ if (st->notify_notpresent) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
sstrncpy(n.type_instance, list_item->sensor_name,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
} else if (IPMI_IS_IPMI_ERR(err) &&
IPMI_GET_IPMI_ERR(err) ==
IPMI_NOT_SUPPORTED_IN_PRESENT_STATE_CC) {
- INFO("ipmi plugin: sensor_read_handler: Sensor %s not ready",
- list_item->sensor_name);
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` not ready.",
+ list_item->sensor_name, st->name);
+ } else if (IPMI_IS_IPMI_ERR(err) &&
+ IPMI_GET_IPMI_ERR(err) == IPMI_TIMEOUT_CC) {
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` timed out.",
+ list_item->sensor_name, st->name);
} else {
+ char errbuf[ERR_BUF_SIZE] = {0};
+ ipmi_get_error_string(err, errbuf, sizeof(errbuf) - 1);
+
if (IPMI_IS_IPMI_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with IPMI error %#x.",
- list_item->sensor_name, IPMI_GET_IPMI_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s.",
+ list_item->sensor_name, st->name, errbuf);
else if (IPMI_IS_OS_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with OS error %#x.",
- list_item->sensor_name, IPMI_GET_OS_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s (%#x).",
+ list_item->sensor_name, st->name, errbuf, IPMI_GET_OS_ERR(err));
else if (IPMI_IS_RMCPP_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with RMCPP error %#x.",
- list_item->sensor_name, IPMI_GET_RMCPP_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s.",
+ list_item->sensor_name, st->name, errbuf);
else if (IPMI_IS_SOL_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with RMCPP error %#x.",
- list_item->sensor_name, IPMI_GET_SOL_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s (%#x).",
+ list_item->sensor_name, st->name, errbuf, IPMI_GET_SOL_ERR(err));
else
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with error %#x. of class %#x",
- list_item->sensor_name, err & 0xff, err & 0xffffff00);
- sensor_list_remove(sensor);
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed "
+ "with error %#x. of class %#x",
+ list_item->sensor_name, st->name, err & 0xff, err & 0xffffff00);
}
return;
} else if (list_item->sensor_not_present == 1) {
list_item->sensor_not_present = 0;
- INFO("ipmi plugin: sensor_read_handler: sensor %s present.",
- list_item->sensor_name);
+ INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` present.",
+ list_item->sensor_name, st->name);
- if (c_ipmi_nofiy_notpresent) {
- notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi",
- "", "", "", NULL};
+ if (st->notify_notpresent) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
sstrncpy(n.type_instance, list_item->sensor_name,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
}
if (value_present != IPMI_BOTH_VALUES_PRESENT) {
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
+ INFO("ipmi plugin: sensor_read_handler: Removing sensor `%s` of `%s`, "
"because it provides %s. If you need this sensor, "
"please file a bug report.",
- list_item->sensor_name,
+ list_item->sensor_name, st->name,
(value_present == IPMI_RAW_VALUE_PRESENT) ? "only the raw value"
: "no value");
- sensor_list_remove(sensor);
+ sensor_list_remove(st, sensor);
+ return;
+ }
+
+ if (!ipmi_is_sensor_scanning_enabled(states)) {
+ DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+ "it is in 'scanning disabled' state.",
+ list_item->sensor_name, st->name);
+ return;
+ }
+
+ if (ipmi_is_initial_update_in_progress(states)) {
+ DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+ "it is in 'initial update in progress' state.",
+ list_item->sensor_name, st->name);
return;
}
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
+ if (st->host != NULL)
+ sstrncpy(vl.host, st->host, sizeof(vl.host));
sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
} /* void sensor_read_handler */
-static int sensor_list_add(ipmi_sensor_t *sensor) {
+static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
+ char temp[DATA_MAX_NAME_LEN] = {0};
+ ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+ const char *entity_id_string = ipmi_entity_get_entity_id_string(ent);
+ char sensor_name[DATA_MAX_NAME_LEN] = "";
+ char *sensor_name_ptr;
+
+ if ((buffer == NULL) || (buf_len == 0))
+ return;
+
+ ipmi_sensor_get_name(sensor, temp, sizeof(temp));
+ temp[sizeof(temp) - 1] = 0;
+
+ if (entity_id_string != NULL && strlen(temp))
+ snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
+ else if (entity_id_string != NULL)
+ sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name));
+ else
+ sstrncpy(sensor_name, temp, sizeof(sensor_name));
+
+ if (strlen(temp)) {
+ sstrncpy(temp, sensor_name, sizeof(temp));
+ sensor_name_ptr = strstr(temp, ").");
+ if (sensor_name_ptr != NULL) {
+ /* If name is something like "foo (123).bar",
+ * change that to "bar (123)".
+ * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
+ * `temp' array, which holds a copy of the current `sensor_name'. */
+ char *sensor_id_ptr;
+
+ /* `sensor_name_ptr' points to ").bar". */
+ sensor_name_ptr[1] = 0;
+ /* `temp' holds "foo (123)\0bar\0". */
+ sensor_name_ptr += 2;
+ /* `sensor_name_ptr' now points to "bar". */
+
+ sensor_id_ptr = strstr(temp, "(");
+ if (sensor_id_ptr != NULL) {
+ /* `sensor_id_ptr' now points to "(123)". */
+ snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
+ sensor_id_ptr);
+ }
+ /* else: don't touch sensor_name. */
+ }
+ }
+ sstrncpy(buffer, sensor_name, buf_len);
+}
+
+static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
ipmi_sensor_id_t sensor_id;
c_ipmi_sensor_list_t *list_item;
c_ipmi_sensor_list_t *list_prev;
char buffer[DATA_MAX_NAME_LEN] = {0};
- const char *entity_id_string;
- char sensor_name[DATA_MAX_NAME_LEN];
- char *sensor_name_ptr;
+ char *sensor_name_ptr = buffer;
int sensor_type;
const char *type;
- ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
sensor_id = ipmi_sensor_convert_to_id(sensor);
+ sensor_get_name(sensor, buffer, sizeof(buffer));
+
+ DEBUG("ipmi plugin: sensor_list_add: Found sensor `%s` of `%s`,"
+ " Type: %#x"
+ " Event reading type: %#x"
+ " Direction: %#x"
+ " Event support: %#x",
+ sensor_name_ptr, st->name, ipmi_sensor_get_sensor_type(sensor),
+ ipmi_sensor_get_event_reading_type(sensor),
+ ipmi_sensor_get_sensor_direction(sensor),
+ ipmi_sensor_get_event_support(sensor));
+
+ /* Both `ignorelist' and `sensor_name_ptr' may be NULL. */
+ if (ignorelist_match(st->ignorelist, sensor_name_ptr) != 0)
+ return 0;
- ipmi_sensor_get_name(sensor, buffer, sizeof(buffer));
- buffer[sizeof(buffer) - 1] = 0;
+ /* FIXME: Use rate unit or base unit to scale the value */
- entity_id_string = ipmi_entity_get_entity_id_string(ent);
+ sensor_type = ipmi_sensor_get_sensor_type(sensor);
- if (entity_id_string == NULL)
- sstrncpy(sensor_name, buffer, sizeof(sensor_name));
- else
- snprintf(sensor_name, sizeof(sensor_name), "%s %s", buffer,
- entity_id_string);
-
- sstrncpy(buffer, sensor_name, sizeof(buffer));
- sensor_name_ptr = strstr(buffer, ").");
- if (sensor_name_ptr != NULL) {
- /* If name is something like "foo (123).bar",
- * change that to "bar (123)".
- * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
- * `buffer' array, which holds a copy of the current `sensor_name'. */
- char *sensor_id_ptr;
-
- /* `sensor_name_ptr' points to ").bar". */
- sensor_name_ptr[1] = 0;
- /* `buffer' holds "foo (123)\0bar\0". */
- sensor_name_ptr += 2;
- /* `sensor_name_ptr' now points to "bar". */
-
- sensor_id_ptr = strstr(buffer, "(");
- if (sensor_id_ptr != NULL) {
- /* `sensor_id_ptr' now points to "(123)". */
- snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
- sensor_id_ptr);
- }
- /* else: don't touch sensor_name. */
+ /*
+ * ipmitool/lib/ipmi_sdr.c sdr_sensor_has_analog_reading() has a notice
+ * about 'Threshold sensors' and 'analog readings'. Discrete sensor may
+ * have analog data, but discrete sensors support is not implemented
+ * in Collectd yet.
+ *
+ * ipmi_sensor_id_get_reading() supports only 'Threshold' sensors.
+ * See lib/sensor.c:4842, stand_ipmi_sensor_get_reading() for details.
+ */
+ if (!ipmi_sensor_get_is_readable(sensor)) {
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because it don't readable! Its type: (%#x, %s). ",
+ sensor_name_ptr, st->name, sensor_type,
+ ipmi_sensor_get_sensor_type_string(sensor));
+ return -1;
}
- sensor_name_ptr = sensor_name;
- /* Both `ignorelist' and `plugin_instance' may be NULL. */
- if (ignorelist_match(ignorelist, sensor_name_ptr) != 0)
- return 0;
-
- /* FIXME: Use rate unit or base unit to scale the value */
+ if (ipmi_sensor_get_event_reading_type(sensor) !=
+ IPMI_EVENT_READING_TYPE_THRESHOLD) {
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because it is discrete (%#x)! Its type: (%#x, %s). ",
+ sensor_name_ptr, st->name, sensor_type,
+ ipmi_sensor_get_event_reading_type(sensor),
+ ipmi_sensor_get_sensor_type_string(sensor));
+ return -1;
+ }
- sensor_type = ipmi_sensor_get_sensor_type(sensor);
switch (sensor_type) {
case IPMI_SENSOR_TYPE_TEMPERATURE:
type = "temperature";
break;
default: {
- const char *sensor_type_str;
-
- sensor_type_str = ipmi_sensor_get_sensor_type_string(sensor);
- INFO("ipmi plugin: sensor_list_add: Ignore sensor %s, "
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
"because I don't know how to handle its type (%#x, %s). "
"If you need this sensor, please file a bug report.",
- sensor_name_ptr, sensor_type, sensor_type_str);
+ sensor_name_ptr, st->name, sensor_type,
+ ipmi_sensor_get_sensor_type_string(sensor));
return -1;
}
} /* switch (sensor_type) */
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
list_prev = NULL;
- for (list_item = sensor_list; list_item != NULL;
+ for (list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
break;
} /* for (list_item) */
if (list_item != NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return 0;
}
list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
if (list_item == NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return -1;
}
+ list_item->instance = st;
list_item->sensor_id = ipmi_sensor_convert_to_id(sensor);
if (list_prev != NULL)
list_prev->next = list_item;
else
- sensor_list = list_item;
+ st->sensor_list = list_item;
sstrncpy(list_item->sensor_name, sensor_name_ptr,
sizeof(list_item->sensor_name));
sstrncpy(list_item->sensor_type, type, sizeof(list_item->sensor_type));
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
- if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) {
- notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
+ if (st->notify_add && (st->init_in_progress == 0)) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s added",
return 0;
} /* int sensor_list_add */
-static int sensor_list_remove(ipmi_sensor_t *sensor) {
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
ipmi_sensor_id_t sensor_id;
c_ipmi_sensor_list_t *list_item;
c_ipmi_sensor_list_t *list_prev;
sensor_id = ipmi_sensor_convert_to_id(sensor);
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
list_prev = NULL;
- for (list_item = sensor_list; list_item != NULL;
+ for (list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
break;
} /* for (list_item) */
if (list_item == NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return -1;
}
if (list_prev == NULL)
- sensor_list = list_item->next;
+ st->sensor_list = list_item->next;
else
list_prev->next = list_item->next;
list_prev = NULL;
list_item->next = NULL;
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
- if (c_ipmi_nofiy_remove && c_ipmi_active) {
- notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
- NULL};
+ if (st->notify_remove && st->active) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s removed",
return 0;
} /* int sensor_list_remove */
-static int sensor_list_read_all(void) {
- pthread_mutex_lock(&sensor_list_lock);
+static int sensor_list_read_all(c_ipmi_instance_t *st) {
+ pthread_mutex_lock(&st->sensor_list_lock);
- for (c_ipmi_sensor_list_t *list_item = sensor_list; list_item != NULL;
+ for (c_ipmi_sensor_list_t *list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
+ DEBUG("ipmi plugin: try read sensor `%s` of `%s`, use: %d",
+ list_item->sensor_name, st->name, list_item->use);
+
+ /* Reading already initiated */
+ if (list_item->use)
+ continue;
+
+ list_item->use++;
ipmi_sensor_id_get_reading(list_item->sensor_id, sensor_read_handler,
- /* user data = */ list_item);
+ /* user data = */ (void *)list_item);
} /* for (list_item) */
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return 0;
} /* int sensor_list_read_all */
-static int sensor_list_remove_all(void) {
+static int sensor_list_remove_all(c_ipmi_instance_t *st) {
c_ipmi_sensor_list_t *list_item;
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
- list_item = sensor_list;
- sensor_list = NULL;
+ list_item = st->sensor_list;
+ st->sensor_list = NULL;
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
while (list_item != NULL) {
c_ipmi_sensor_list_t *list_next = list_item->next;
return 0;
} /* int sensor_list_remove_all */
+static int sensor_convert_threshold_severity(enum ipmi_thresh_e severity) {
+ switch (severity) {
+ case IPMI_LOWER_NON_CRITICAL:
+ case IPMI_UPPER_NON_CRITICAL:
+ return NOTIF_OKAY;
+ case IPMI_LOWER_CRITICAL:
+ case IPMI_UPPER_CRITICAL:
+ return NOTIF_WARNING;
+ case IPMI_LOWER_NON_RECOVERABLE:
+ case IPMI_UPPER_NON_RECOVERABLE:
+ return NOTIF_FAILURE;
+ default:
+ return NOTIF_OKAY;
+ } /* switch (severity) */
+} /* int sensor_convert_threshold_severity */
+
+static void add_event_common_data(notification_t *n, ipmi_sensor_t *sensor,
+ enum ipmi_event_dir_e dir,
+ ipmi_event_t *event) {
+ ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+
+ plugin_notification_meta_add_string(n, "entity_name",
+ ipmi_entity_get_entity_id_string(ent));
+ plugin_notification_meta_add_signed_int(n, "entity_id",
+ ipmi_entity_get_entity_id(ent));
+ plugin_notification_meta_add_signed_int(n, "entity_instance",
+ ipmi_entity_get_entity_instance(ent));
+ plugin_notification_meta_add_boolean(n, "assert", dir == IPMI_ASSERTION);
+
+ if (event)
+ plugin_notification_meta_add_signed_int(n, "event_type",
+ ipmi_event_get_type(event));
+} /* void add_event_sensor_meta_data */
+
+static int sensor_threshold_event_handler(
+ ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir,
+ enum ipmi_thresh_e threshold, enum ipmi_event_value_dir_e high_low,
+ enum ipmi_value_present_e value_present, unsigned int raw_value,
+ double value, void *cb_data, ipmi_event_t *event) {
+
+ c_ipmi_instance_t *st = cb_data;
+
+ /* From the IPMI specification Chapter 2: Events.
+ * If a callback handles the event, then all future callbacks called due to
+ * the event will receive a NULL for the event. So be ready to handle a NULL
+ * event in all your event handlers. A NULL may also be passed to an event
+ * handler if the callback was not due to an event. */
+ if (event == NULL)
+ return IPMI_EVENT_NOT_HANDLED;
+
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+ /* offset is a table index and it's represented as enum of strings that are
+ organized in the way - high and low for each threshold severity level */
+ unsigned int offset = (2 * threshold) + high_low;
+ unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+ unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+ const char *event_state =
+ ipmi_get_reading_name(event_type, sensor_type, offset);
+ sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+ if (value_present != IPMI_NO_VALUES_PRESENT)
+ snprintf(n.message, sizeof(n.message),
+ "sensor %s received event: %s, value is %f", n.type_instance,
+ event_state, value);
+ else
+ snprintf(n.message, sizeof(n.message),
+ "sensor %s received event: %s, value not provided",
+ n.type_instance, event_state);
+
+ DEBUG("Threshold event received for sensor %s", n.type_instance);
+
+ sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+ n.severity = sensor_convert_threshold_severity(threshold);
+ n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+ plugin_notification_meta_add_string(&n, "severity",
+ ipmi_get_threshold_string(threshold));
+ plugin_notification_meta_add_string(&n, "direction",
+ ipmi_get_value_dir_string(high_low));
+
+ switch (value_present) {
+ case IPMI_BOTH_VALUES_PRESENT:
+ plugin_notification_meta_add_double(&n, "val", value);
+ /* both values present, so fall-through to add raw value too */
+ case IPMI_RAW_VALUE_PRESENT: {
+ char buf[DATA_MAX_NAME_LEN] = {0};
+ snprintf(buf, sizeof(buf), "0x%2.2x", raw_value);
+ plugin_notification_meta_add_string(&n, "raw", buf);
+ } break;
+ default:
+ break;
+ } /* switch (value_present) */
+
+ add_event_common_data(&n, sensor, dir, event);
+
+ plugin_dispatch_notification(&n);
+ plugin_notification_meta_free(n.meta);
+
+ /* Delete handled ipmi event from the list */
+ if (st->sel_clear_event) {
+ ipmi_event_delete(event, NULL, NULL);
+ return IPMI_EVENT_HANDLED;
+ }
+
+ return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_threshold_event_handler */
+
+static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
+ enum ipmi_event_dir_e dir, int offset,
+ int severity, int prev_severity,
+ void *cb_data, ipmi_event_t *event) {
+
+ c_ipmi_instance_t *st = cb_data;
+
+ /* From the IPMI specification Chapter 2: Events.
+ * If a callback handles the event, then all future callbacks called due to
+ * the event will receive a NULL for the event. So be ready to handle a NULL
+ * event in all your event handlers. A NULL may also be passed to an event
+ * handler if the callback was not due to an event. */
+ if (event == NULL)
+ return IPMI_EVENT_NOT_HANDLED;
+
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+ unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+ unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+ const char *event_state =
+ ipmi_get_reading_name(event_type, sensor_type, offset);
+ sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+ snprintf(n.message, sizeof(n.message), "sensor %s received event: %s",
+ n.type_instance, event_state);
+
+ DEBUG("Discrete event received for sensor %s", n.type_instance);
+
+ sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+ n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+ plugin_notification_meta_add_signed_int(&n, "offset", offset);
+
+ if (severity != -1)
+ plugin_notification_meta_add_signed_int(&n, "severity", severity);
+
+ if (prev_severity != -1)
+ plugin_notification_meta_add_signed_int(&n, "prevseverity", prev_severity);
+
+ add_event_common_data(&n, sensor, dir, event);
+
+ plugin_dispatch_notification(&n);
+ plugin_notification_meta_free(n.meta);
+
+ /* Delete handled ipmi event from the list */
+ if (st->sel_clear_event) {
+ ipmi_event_delete(event, NULL, NULL);
+ return IPMI_EVENT_HANDLED;
+ }
+
+ return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_discrete_event_handler */
+
/*
* Entity handlers
*/
-static void entity_sensor_update_handler(
- enum ipmi_update_e op, ipmi_entity_t __attribute__((unused)) * entity,
- ipmi_sensor_t *sensor, void __attribute__((unused)) * user_data) {
- /* TODO: Ignore sensors we cannot read */
+static void
+entity_sensor_update_handler(enum ipmi_update_e op,
+ ipmi_entity_t __attribute__((unused)) * entity,
+ ipmi_sensor_t *sensor, void *user_data) {
+ c_ipmi_instance_t *st = user_data;
if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
/* Will check for duplicate entries.. */
- sensor_list_add(sensor);
+ sensor_list_add(st, sensor);
+
+ if (st->sel_enabled) {
+ int status = 0;
+ /* register threshold event if threshold sensor support events */
+ if ((ipmi_sensor_get_event_reading_type(sensor) ==
+ IPMI_EVENT_READING_TYPE_THRESHOLD) &&
+ (ipmi_sensor_get_threshold_access(sensor) !=
+ IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
+ status = ipmi_sensor_add_threshold_event_handler(
+ sensor, sensor_threshold_event_handler, st);
+ /* register discrete handler if discrete/specific sensor support events */
+ else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
+ status = ipmi_sensor_add_discrete_event_handler(
+ sensor, sensor_discrete_event_handler, st);
+
+ if (status) {
+ char buf[DATA_MAX_NAME_LEN] = {0};
+ sensor_get_name(sensor, buf, sizeof(buf));
+ ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
+ }
+ }
} else if (op == IPMI_DELETED) {
- sensor_list_remove(sensor);
+ sensor_list_remove(st, sensor);
+
+ if (st->sel_enabled) {
+ if (ipmi_sensor_get_event_reading_type(sensor) ==
+ IPMI_EVENT_READING_TYPE_THRESHOLD)
+ ipmi_sensor_remove_threshold_event_handler(
+ sensor, sensor_threshold_event_handler, st);
+ else
+ ipmi_sensor_remove_discrete_event_handler(
+ sensor, sensor_discrete_event_handler, st);
+ }
}
} /* void entity_sensor_update_handler */
/*
* Domain handlers
*/
-static void domain_entity_update_handler(
- enum ipmi_update_e op, ipmi_domain_t __attribute__((unused)) * domain,
- ipmi_entity_t *entity, void __attribute__((unused)) * user_data) {
+static void
+domain_entity_update_handler(enum ipmi_update_e op,
+ ipmi_domain_t __attribute__((unused)) * domain,
+ ipmi_entity_t *entity, void *user_data) {
int status;
+ c_ipmi_instance_t *st = user_data;
if (op == IPMI_ADDED) {
status = ipmi_entity_add_sensor_update_handler(
- entity, entity_sensor_update_handler, /* user data = */ NULL);
+ entity, entity_sensor_update_handler, /* user data = */ (void *)st);
if (status != 0) {
- c_ipmi_error("ipmi_entity_add_sensor_update_handler", status);
+ c_ipmi_error(st, "ipmi_entity_add_sensor_update_handler", status);
}
} else if (op == IPMI_DELETED) {
status = ipmi_entity_remove_sensor_update_handler(
- entity, entity_sensor_update_handler, /* user data = */ NULL);
+ entity, entity_sensor_update_handler, /* user data = */ (void *)st);
if (status != 0) {
- c_ipmi_error("ipmi_entity_remove_sensor_update_handler", status);
+ c_ipmi_error(st, "ipmi_entity_remove_sensor_update_handler", status);
}
}
} /* void domain_entity_update_handler */
+static void smi_event_handler(ipmi_con_t __attribute__((unused)) * ipmi,
+ const ipmi_addr_t __attribute__((unused)) * addr,
+ unsigned int __attribute__((unused)) addr_len,
+ ipmi_event_t *event, void *cb_data) {
+ unsigned int type = ipmi_event_get_type(event);
+ ipmi_domain_t *domain = cb_data;
+
+ DEBUG("%s: Event received: type %u", __FUNCTION__, type);
+
+ if (type != 0x02)
+ /* It's not a standard IPMI event. */
+ return;
+
+ /* force domain to reread SELs */
+ ipmi_domain_reread_sels(domain, NULL, NULL);
+}
+
static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
unsigned int conn_num,
unsigned int port_num,
int still_connected,
void *user_data) {
- int status;
DEBUG("domain_connection_change_handler (domain = %p, err = %i, "
"conn_num = %u, port_num = %u, still_connected = %i, "
- "user_data = %p);\n",
+ "user_data = %p);",
(void *)domain, err, conn_num, port_num, still_connected, user_data);
- status = ipmi_domain_add_entity_update_handler(
- domain, domain_entity_update_handler, /* user data = */ NULL);
- if (status != 0) {
- c_ipmi_error("ipmi_domain_add_entity_update_handler", status);
+ c_ipmi_instance_t *st = user_data;
+
+ if (err != 0)
+ c_ipmi_error(st, "domain_connection_change_handler", err);
+
+ if (!still_connected) {
+
+ if (st->notify_conn && st->connected && st->init_in_progress == 0) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_FAILURE);
+
+ sstrncpy(n.message, "IPMI connection lost", sizeof(n.plugin));
+
+ plugin_dispatch_notification(&n);
+ }
+
+ st->connected = 0;
+ return;
}
-} /* void domain_connection_change_handler */
-static int thread_init(os_handler_t **ret_os_handler) {
- os_handler_t *os_handler;
- ipmi_con_t *smi_connection = NULL;
- ipmi_domain_id_t domain_id;
- int status;
+ if (st->notify_conn && !st->connected && st->init_in_progress == 0) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
- if (os_handler == NULL) {
- ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
- return -1;
+ sstrncpy(n.message, "IPMI connection restored", sizeof(n.plugin));
+
+ plugin_dispatch_notification(&n);
}
- ipmi_init(os_handler);
+ st->connected = 1;
- status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
- /* user data = */ NULL, &smi_connection);
+ int status = ipmi_domain_add_entity_update_handler(
+ domain, domain_entity_update_handler, /* user data = */ st);
if (status != 0) {
- c_ipmi_error("ipmi_smi_setup_con", status);
- return -1;
+ c_ipmi_error(st, "ipmi_domain_add_entity_update_handler", status);
}
- ipmi_open_option_t open_option[1] = {[0] = {.option = IPMI_OPEN_OPTION_ALL,
- {.ival = 1}}};
+ status = st->connection->add_event_handler(st->connection, smi_event_handler,
+ (void *)domain);
+
+ if (status != 0)
+ c_ipmi_error(st, "Failed to register smi event handler", status);
+} /* void domain_connection_change_handler */
+static int c_ipmi_thread_init(c_ipmi_instance_t *st) {
+ ipmi_domain_id_t domain_id;
+ int status;
+
+ if (st->connaddr != NULL) {
+ status = ipmi_ip_setup_con(
+ &st->connaddr, &(char *){IPMI_LAN_STD_PORT_STR}, 1, st->authtype,
+ (unsigned int)IPMI_PRIVILEGE_USER, st->username, strlen(st->username),
+ st->password, strlen(st->password), os_handler,
+ /* user data = */ NULL, &st->connection);
+ if (status != 0) {
+ c_ipmi_error(st, "ipmi_ip_setup_con", status);
+ return -1;
+ }
+ } else {
+ status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
+ /* user data = */ NULL, &st->connection);
+ if (status != 0) {
+ c_ipmi_error(st, "ipmi_smi_setup_con", status);
+ return -1;
+ }
+ }
+
+ ipmi_open_option_t opts[] = {
+ {.option = IPMI_OPEN_OPTION_ALL, {.ival = 1}},
+#ifdef IPMI_OPEN_OPTION_USE_CACHE
+ /* OpenIPMI-2.0.17 and later: Disable SDR cache in local file */
+ {.option = IPMI_OPEN_OPTION_USE_CACHE, {.ival = 0}},
+#endif
+ };
+
+ /*
+ * NOTE: Domain names must be unique. There is static `domains_list` common
+ * to all threads inside lib/domain.c and some ops are done by name.
+ */
status = ipmi_open_domain(
- "mydomain", &smi_connection, /* num_con = */ 1,
- domain_connection_change_handler, /* user data = */ NULL,
- /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
- sizeof(open_option) / sizeof(open_option[0]), &domain_id);
+ st->name, &st->connection, /* num_con = */ 1,
+ domain_connection_change_handler, /* user data = */ (void *)st,
+ /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, opts,
+ STATIC_ARRAY_SIZE(opts), &domain_id);
if (status != 0) {
- c_ipmi_error("ipmi_open_domain", status);
+ c_ipmi_error(st, "ipmi_open_domain", status);
return -1;
}
- *ret_os_handler = os_handler;
return 0;
-} /* int thread_init */
+} /* int c_ipmi_thread_init */
-static void *thread_main(void __attribute__((unused)) * user_data) {
- int status;
- os_handler_t *os_handler = NULL;
+static void *c_ipmi_thread_main(void *user_data) {
+ c_ipmi_instance_t *st = user_data;
- status = thread_init(&os_handler);
+ int status = c_ipmi_thread_init(st);
if (status != 0) {
- ERROR("ipmi plugin: thread_init failed.\n");
+ ERROR("ipmi plugin: c_ipmi_thread_init failed.");
+ st->active = 0;
return (void *)-1;
}
- while (c_ipmi_active != 0) {
+ while (st->active != 0) {
struct timeval tv = {1, 0};
os_handler->perform_one_op(os_handler, &tv);
}
+ return (void *)0;
+} /* void *c_ipmi_thread_main */
- ipmi_posix_thread_free_os_handler(os_handler);
+static c_ipmi_instance_t *c_ipmi_init_instance() {
+ c_ipmi_instance_t *st;
- return (void *)0;
-} /* void *thread_main */
-
-static int c_ipmi_config(const char *key, const char *value) {
- if (ignorelist == NULL)
- ignorelist = ignorelist_create(/* invert = */ 1);
- if (ignorelist == NULL)
- return 1;
-
- if (strcasecmp("Sensor", key) == 0) {
- ignorelist_add(ignorelist, value);
- } else if (strcasecmp("IgnoreSelected", key) == 0) {
- int invert = 1;
- if (IS_TRUE(value))
- invert = 0;
- ignorelist_set_invert(ignorelist, invert);
- } else if (strcasecmp("NotifySensorAdd", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_add = 1;
- } else if (strcasecmp("NotifySensorRemove", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_remove = 1;
- } else if (strcasecmp("NotifySensorNotPresent", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_notpresent = 1;
- } else {
- return -1;
+ st = calloc(1, sizeof(*st));
+ if (st == NULL) {
+ ERROR("ipmi plugin: calloc failed.");
+ return NULL;
}
- return 0;
-} /* int c_ipmi_config */
+ st->name = strdup("main");
+ if (st->name == NULL) {
+ sfree(st);
+ ERROR("ipmi plugin: strdup() failed.");
+ return NULL;
+ }
-static int c_ipmi_init(void) {
- int status;
+ st->ignorelist = ignorelist_create(/* invert = */ 1);
+ if (st->ignorelist == NULL) {
+ sfree(st->name);
+ sfree(st);
+ ERROR("ipmi plugin: ignorelist_create() failed.");
+ return NULL;
+ }
- /* Don't send `ADD' notifications during startup (~ 1 minute) */
- time_t iv = CDTIME_T_TO_TIME_T(plugin_get_interval());
- c_ipmi_init_in_progress = 1 + (60 / iv);
+ st->sensor_list = NULL;
+ pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
+
+ st->host = NULL;
+ st->connaddr = NULL;
+ st->username = NULL;
+ st->password = NULL;
+ st->authtype = IPMI_AUTHTYPE_DEFAULT;
+
+ st->next = NULL;
- c_ipmi_active = 1;
+ return st;
+} /* c_ipmi_instance_t *c_ipmi_init_instance */
+
+static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
+ if (st == NULL)
+ return;
+
+ assert(st->next == NULL);
+
+ sfree(st->name);
+ sfree(st->host);
+ sfree(st->connaddr);
+ sfree(st->username);
+ sfree(st->password);
+
+ ignorelist_free(st->ignorelist);
+ pthread_mutex_destroy(&st->sensor_list_lock);
+ sfree(st);
+} /* void c_ipmi_free_instance */
+
+static void c_ipmi_add_instance(c_ipmi_instance_t *instance) {
+ if (instances == NULL) {
+ instances = instance;
+ return;
+ }
+
+ c_ipmi_instance_t *last = instances;
+
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = instance;
+} /* void c_ipmi_add_instance */
+
+static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
+ int status = 0;
+ c_ipmi_instance_t *st = c_ipmi_init_instance();
+ if (st == NULL)
+ return ENOMEM;
+
+ if (strcasecmp(ci->key, "Instance") == 0)
+ status = cf_util_get_string(ci, &st->name);
- status = plugin_thread_create(&thread_id, /* attr = */ NULL, thread_main,
- /* user data = */ NULL, "ipmi");
if (status != 0) {
- c_ipmi_active = 0;
- thread_id = (pthread_t)0;
- ERROR("ipmi plugin: pthread_create failed.");
- return -1;
+ c_ipmi_free_instance(st);
+ return status;
}
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Sensor", child->key) == 0)
+ ignorelist_add(st->ignorelist, ci->values[0].value.string);
+ else if (strcasecmp("IgnoreSelected", child->key) == 0) {
+ _Bool t;
+ status = cf_util_get_boolean(child, &t);
+ if (status != 0)
+ break;
+ ignorelist_set_invert(st->ignorelist, /* invert = */ !t);
+ } else if (strcasecmp("NotifyIPMIConnectionState", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_conn);
+ } else if (strcasecmp("NotifySensorAdd", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_add);
+ } else if (strcasecmp("NotifySensorRemove", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_remove);
+ } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_notpresent);
+ } else if (strcasecmp("SELEnabled", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->sel_enabled);
+ } else if (strcasecmp("SELClearEvent", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->sel_clear_event);
+ } else if (strcasecmp("Host", child->key) == 0)
+ status = cf_util_get_string(child, &st->host);
+ else if (strcasecmp("Address", child->key) == 0)
+ status = cf_util_get_string(child, &st->connaddr);
+ else if (strcasecmp("Username", child->key) == 0)
+ status = cf_util_get_string(child, &st->username);
+ else if (strcasecmp("Password", child->key) == 0)
+ status = cf_util_get_string(child, &st->password);
+ else if (strcasecmp("AuthType", child->key) == 0) {
+ char tmp[8];
+ status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
+ if (status != 0)
+ break;
+
+ if (strcasecmp("MD5", tmp) == 0)
+ st->authtype = IPMI_AUTHTYPE_MD5;
+ else if (strcasecmp("rmcp+", tmp) == 0)
+ st->authtype = IPMI_AUTHTYPE_RMCP_PLUS;
+ else
+ WARNING("ipmi plugin: The value \"%s\" is not valid for the "
+ "\"AuthType\" option.",
+ tmp);
+ } else {
+ WARNING("ipmi plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status != 0) {
+ c_ipmi_free_instance(st);
+ return status;
+ }
+
+ c_ipmi_add_instance(st);
+
return 0;
-} /* int c_ipmi_init */
+} /* int c_ipmi_config_add_instance */
+
+static int c_ipmi_config(oconfig_item_t *ci) {
+ _Bool have_instance_block = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Instance", child->key) == 0) {
+ int status = c_ipmi_config_add_instance(child);
+ if (status != 0)
+ return status;
+
+ have_instance_block = 1;
+ } else if (!have_instance_block) {
+ /* Non-instance option: Assume legacy configuration (without <Instance />
+ * blocks) and call c_ipmi_config_add_instance with the <Plugin /> block.
+ */
+ WARNING("ipmi plugin: Legacy configuration found! Please update your "
+ "config file.");
+ return c_ipmi_config_add_instance(ci);
+ } else {
+ WARNING("ipmi plugin: The configuration option "
+ "\"%s\" is not allowed here. Did you "
+ "forget to add an <Instance /> block "
+ "around the configuration?",
+ child->key);
+ return -1;
+ }
+ } /* for (ci->children) */
+
+ return 0;
+} /* int c_ipmi_config */
-static int c_ipmi_read(void) {
- if ((c_ipmi_active == 0) || (thread_id == (pthread_t)0)) {
+static int c_ipmi_read(user_data_t *user_data) {
+ c_ipmi_instance_t *st = user_data->data;
+
+ if (st->active == 0) {
INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
return -1;
}
- sensor_list_read_all();
+ if (st->connected == 0)
+ return 0;
+
+ sensor_list_read_all(st);
- if (c_ipmi_init_in_progress > 0)
- c_ipmi_init_in_progress--;
+ if (st->init_in_progress > 0)
+ st->init_in_progress--;
else
- c_ipmi_init_in_progress = 0;
+ st->init_in_progress = 0;
return 0;
} /* int c_ipmi_read */
+static int c_ipmi_init(void) {
+ c_ipmi_instance_t *st;
+ char callback_name[3 * DATA_MAX_NAME_LEN];
+
+ if (os_handler != NULL) {
+ return 0;
+ }
+
+ os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
+ if (os_handler == NULL) {
+ ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
+ return -1;
+ }
+
+ os_handler->set_log_handler(os_handler, c_ipmi_log);
+
+ if (ipmi_init(os_handler) != 0) {
+ ERROR("ipmi plugin: ipmi_init() failed.");
+ os_handler->free_os_handler(os_handler);
+ return -1;
+ };
+
+ if (instances == NULL) {
+ /* No instances were configured, let's start a default instance. */
+ st = c_ipmi_init_instance();
+ if (st == NULL)
+ return ENOMEM;
+
+ c_ipmi_add_instance(st);
+ }
+
+ /* Don't send `ADD' notifications during startup (~ 1 minute) */
+ int cycles = 1 + (60 / CDTIME_T_TO_TIME_T(plugin_get_interval()));
+
+ st = instances;
+ while (NULL != st) {
+ /* The `st->name` is used as "domain name" for ipmi_open_domain().
+ * That value should be unique, so we do plugin_register_complex_read()
+ * at first as it checks the uniqueness. */
+ snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name);
+
+ user_data_t ud = {
+ .data = st,
+ };
+
+ int status = plugin_register_complex_read(
+ /* group = */ "ipmi",
+ /* name = */ callback_name,
+ /* callback = */ c_ipmi_read,
+ /* interval = */ 0,
+ /* user_data = */ &ud);
+
+ if (status != 0) {
+ st = st->next;
+ continue;
+ }
+
+ st->init_in_progress = cycles;
+ st->active = 1;
+
+ status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
+ c_ipmi_thread_main,
+ /* user data = */ (void *)st, "ipmi");
+
+ if (status != 0) {
+ st->active = 0;
+ st->thread_id = (pthread_t){0};
+
+ plugin_unregister_read(callback_name);
+
+ ERROR("ipmi plugin: pthread_create failed for `%s`.", callback_name);
+ }
+
+ st = st->next;
+ }
+
+ return 0;
+} /* int c_ipmi_init */
+
static int c_ipmi_shutdown(void) {
- c_ipmi_active = 0;
+ c_ipmi_instance_t *st = instances;
+ instances = NULL;
+
+ while (st != NULL) {
+ c_ipmi_instance_t *next = st->next;
+
+ st->next = NULL;
+ st->active = 0;
+
+ if (!pthread_equal(st->thread_id, (pthread_t){0})) {
+ pthread_join(st->thread_id, NULL);
+ st->thread_id = (pthread_t){0};
+ }
+
+ sensor_list_remove_all(st);
+ c_ipmi_free_instance(st);
- if (thread_id != (pthread_t)0) {
- pthread_join(thread_id, NULL);
- thread_id = (pthread_t)0;
+ st = next;
}
- sensor_list_remove_all();
+ os_handler->free_os_handler(os_handler);
+ os_handler = NULL;
return 0;
} /* int c_ipmi_shutdown */
void module_register(void) {
- plugin_register_config("ipmi", c_ipmi_config, config_keys, config_keys_num);
+ plugin_register_complex_config("ipmi", c_ipmi_config);
plugin_register_init("ipmi", c_ipmi_init);
- plugin_register_read("ipmi", c_ipmi_read);
plugin_register_shutdown("ipmi", c_ipmi_shutdown);
} /* void module_register */
state->values = calloc(sizeof(*state->values), state->values_len);
state->values_types = calloc(sizeof(*state->values_types), state->values_len);
if ((state->values == NULL) || (state->values_types == NULL)) {
- free(state->values);
- free(state->values_types);
return ENOMEM;
}
case TYPE_VALUES: {
lcc_value_list_t vl = state;
if (parse_values(payload, sizeof(payload), &vl)) {
+ free(vl.values);
+ free(vl.values_types);
DEBUG("lcc_network_parse(): parse_values failed.\n");
return EINVAL;
}
const size_t len) {
int ret = 0;
pthread_rwlock_rdlock(&self->lock);
- if (swrite(self->sock_fd, msg, len) < 0)
+ if (swrite(self->sock_fd, msg, len) != 0)
ret = -1;
pthread_rwlock_unlock(&self->lock);
return ret;
#define MEMCACHED_CONNECT_TIMEOUT 10000
#define MEMCACHED_IO_TIMEOUT 5000
+struct prev_s {
+ derive_t hits;
+ derive_t gets;
+ derive_t incr_hits;
+ derive_t incr_misses;
+ derive_t decr_hits;
+ derive_t decr_misses;
+};
+
+typedef struct prev_s prev_t;
+
struct memcached_s {
char *name;
char *host;
char *connhost;
char *connport;
int fd;
+ prev_t prev;
};
typedef struct memcached_s memcached_t;
static int memcached_connect_unix(memcached_t *st) {
struct sockaddr_un serv_addr = {0};
- int fd;
serv_addr.sun_family = AF_UNIX;
sstrncpy(serv_addr.sun_path, st->socket, sizeof(serv_addr.sun_path));
/* create our socket descriptor */
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
char errbuf[1024];
ERROR("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
static int memcached_connect_inet(memcached_t *st) {
struct addrinfo *ai_list;
- int status;
int fd = -1;
struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
.ai_flags = AI_ADDRCONFIG,
.ai_socktype = SOCK_STREAM};
- status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
+ int status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
if (status != 0) {
char errbuf[1024];
ERROR("memcached plugin: memcached_connect_inet: "
plugin_dispatch_values(&vl);
}
+static gauge_t calculate_ratio_percent(derive_t part, derive_t total,
+ derive_t *prev_part,
+ derive_t *prev_total) {
+ if ((*prev_part == 0) || (*prev_total == 0) || (part < *prev_part) ||
+ (total < *prev_total)) {
+ *prev_part = part;
+ *prev_total = total;
+ return NAN;
+ }
+
+ derive_t num = part - *prev_part;
+ derive_t denom = total - *prev_total;
+
+ *prev_part = part;
+ *prev_total = total;
+
+ if (denom == 0)
+ return NAN;
+
+ if (num == 0)
+ return 0;
+
+ return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
+static gauge_t calculate_ratio_percent2(derive_t part1, derive_t part2,
+ derive_t *prev1, derive_t *prev2) {
+ if ((*prev1 == 0) || (*prev2 == 0) || (part1 < *prev1) || (part2 < *prev2)) {
+ *prev1 = part1;
+ *prev2 = part2;
+ return NAN;
+ }
+
+ derive_t num = part1 - *prev1;
+ derive_t denom = part2 - *prev2 + num;
+
+ *prev1 = part1;
+ *prev2 = part2;
+
+ if (denom == 0)
+ return NAN;
+
+ if (num == 0)
+ return 0;
+
+ return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
static int memcached_read(user_data_t *user_data) {
char buf[4096];
char *fields[3];
- char *ptr;
char *line;
- char *saveptr;
- int fields_num;
-
- gauge_t bytes_used = NAN;
- gauge_t bytes_total = NAN;
- gauge_t hits = NAN;
- gauge_t gets = NAN;
- gauge_t incr_hits = NAN;
- derive_t incr = 0;
- gauge_t decr_hits = NAN;
- derive_t decr = 0;
+
+ derive_t bytes_used = 0;
+ derive_t bytes_total = 0;
+ derive_t get_hits = 0;
+ derive_t cmd_get = 0;
+ derive_t incr_hits = 0;
+ derive_t incr_misses = 0;
+ derive_t decr_hits = 0;
+ derive_t decr_misses = 0;
derive_t rusage_user = 0;
derive_t rusage_syst = 0;
derive_t octets_rx = 0;
derive_t octets_tx = 0;
- memcached_t *st;
- st = user_data->data;
+ memcached_t *st = user_data->data;
+ prev_t *prev = &st->prev;
/* get data from daemon */
if (memcached_query_daemon(buf, sizeof(buf), st) < 0) {
#define FIELD_IS(cnst) \
(((sizeof(cnst) - 1) == name_len) && (strcmp(cnst, fields[1]) == 0))
- ptr = buf;
- saveptr = NULL;
+ char *ptr = buf;
+ char *saveptr = NULL;
while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
- int name_len;
-
ptr = NULL;
- fields_num = strsplit(line, fields, 3);
- if (fields_num != 3)
+ if (strsplit(line, fields, 3) != 3)
continue;
- name_len = strlen(fields[1]);
+ int name_len = strlen(fields[1]);
if (name_len == 0)
continue;
* CPU time consumed by the memcached process
*/
if (FIELD_IS("rusage_user")) {
- rusage_user = atoll(fields[2]);
+ /* Convert to useconds */
+ rusage_user = atof(fields[2]) * 1000000;
} else if (FIELD_IS("rusage_system")) {
- rusage_syst = atoll(fields[2]);
+ rusage_syst = atof(fields[2]) * 1000000;
}
/*
* Number of bytes used and available (total - used)
*/
else if (FIELD_IS("bytes")) {
- bytes_used = atof(fields[2]);
+ bytes_used = atoll(fields[2]);
} else if (FIELD_IS("limit_maxbytes")) {
- bytes_total = atof(fields[2]);
+ bytes_total = atoll(fields[2]);
}
/*
else if (FIELD_IS("curr_connections")) {
submit_gauge("memcached_connections", "current", atof(fields[2]), st);
} else if (FIELD_IS("listen_disabled_num")) {
- submit_derive("connections", "listen_disabled", atof(fields[2]), st);
+ submit_derive("connections", "listen_disabled", atoll(fields[2]), st);
}
/*
* Total number of connections opened since the server started running
* Report this as connection rate.
*/
else if (FIELD_IS("total_connections")) {
- submit_derive("connections", "opened", atof(fields[2]), st);
+ submit_derive("connections", "opened", atoll(fields[2]), st);
}
/*
const char *name = fields[1] + 4;
submit_derive("memcached_command", name, atoll(fields[2]), st);
if (strcmp(name, "get") == 0)
- gets = atof(fields[2]);
+ cmd_get = atoll(fields[2]);
}
/*
* Increment/Decrement
*/
else if (FIELD_IS("incr_misses")) {
- derive_t incr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "incr_misses", incr_count, st);
- incr += incr_count;
+ incr_misses = atoll(fields[2]);
+ submit_derive("memcached_ops", "incr_misses", incr_misses, st);
} else if (FIELD_IS("incr_hits")) {
- derive_t incr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "incr_hits", incr_count, st);
- incr_hits = atof(fields[2]);
- incr += incr_count;
+ incr_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "incr_hits", incr_hits, st);
} else if (FIELD_IS("decr_misses")) {
- derive_t decr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "decr_misses", decr_count, st);
- decr += decr_count;
+ decr_misses = atoll(fields[2]);
+ submit_derive("memcached_ops", "decr_misses", decr_misses, st);
} else if (FIELD_IS("decr_hits")) {
- derive_t decr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "decr_hits", decr_count, st);
- decr_hits = atof(fields[2]);
- decr += decr_count;
+ decr_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "decr_hits", decr_hits, st);
}
/*
* - evictions
*/
else if (FIELD_IS("get_hits")) {
- submit_derive("memcached_ops", "hits", atoll(fields[2]), st);
- hits = atof(fields[2]);
+ get_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "hits", get_hits, st);
} else if (FIELD_IS("get_misses")) {
submit_derive("memcached_ops", "misses", atoll(fields[2]), st);
} else if (FIELD_IS("evictions")) {
}
} /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
- if (!isnan(bytes_used) && !isnan(bytes_total) && (bytes_used <= bytes_total))
+ if ((bytes_total > 0) && (bytes_used <= bytes_total))
submit_gauge2("df", "cache", bytes_used, bytes_total - bytes_used, st);
if ((rusage_user != 0) || (rusage_syst != 0))
if ((octets_rx != 0) || (octets_tx != 0))
submit_derive2("memcached_octets", NULL, octets_rx, octets_tx, st);
- if (!isnan(gets) && !isnan(hits)) {
- gauge_t rate = NAN;
-
- if (gets != 0.0)
- rate = 100.0 * hits / gets;
-
- submit_gauge("percent", "hitratio", rate, st);
+ if ((cmd_get != 0) && (get_hits != 0)) {
+ gauge_t ratio =
+ calculate_ratio_percent(get_hits, cmd_get, &prev->hits, &prev->gets);
+ submit_gauge("percent", "hitratio", ratio, st);
}
- if (!isnan(incr_hits) && incr != 0) {
- gauge_t incr_rate = 100.0 * incr_hits / incr;
- submit_gauge("percent", "incr_hitratio", incr_rate, st);
- submit_derive("memcached_ops", "incr", incr, st);
+ if ((incr_hits != 0) && (incr_misses != 0)) {
+ gauge_t ratio = calculate_ratio_percent2(
+ incr_hits, incr_misses, &prev->incr_hits, &prev->incr_misses);
+ submit_gauge("percent", "incr_hitratio", ratio, st);
+ submit_derive("memcached_ops", "incr", incr_hits + incr_misses, st);
}
- if (!isnan(decr_hits) && decr != 0) {
- gauge_t decr_rate = 100.0 * decr_hits / decr;
- submit_gauge("percent", "decr_hitratio", decr_rate, st);
- submit_derive("memcached_ops", "decr", decr, st);
+ if ((decr_hits != 0) && (decr_misses != 0)) {
+ gauge_t ratio = calculate_ratio_percent2(
+ decr_hits, decr_misses, &prev->decr_hits, &prev->decr_misses);
+ submit_gauge("percent", "decr_hitratio", ratio, st);
+ submit_derive("memcached_ops", "decr", decr_hits + decr_misses, st);
}
return 0;
assert(st->connhost != NULL);
assert(st->connport != NULL);
+ st->prev.hits = 0;
+ st->prev.gets = 0;
+ st->prev.incr_hits = 0;
+ st->prev.incr_misses = 0;
+ st->prev.decr_hits = 0;
+ st->prev.decr_misses = 0;
+
return 0;
} /* int memcached_set_defaults */
* </Plugin>
*/
static int config_add_instance(oconfig_item_t *ci) {
- memcached_t *st;
int status = 0;
/* Disable automatic generation of default instance in the init callback. */
memcached_have_instances = 1;
- st = calloc(1, sizeof(*st));
+ memcached_t *st = calloc(1, sizeof(*st));
if (st == NULL) {
ERROR("memcached plugin: calloc failed.");
return ENOMEM;
} /* int memcached_config */
static int memcached_init(void) {
- memcached_t *st;
- int status;
if (memcached_have_instances)
return 0;
/* No instances were configured, lets start a default instance. */
- st = calloc(1, sizeof(*st));
+ memcached_t *st = calloc(1, sizeof(*st));
if (st == NULL)
return ENOMEM;
st->name = NULL;
st->fd = -1;
- status = memcached_add_read_callback(st);
+ int status = memcached_add_read_callback(st);
if (status == 0)
memcached_have_instances = 1;
struct timeval time_now;
status = swrite(fd, "D", 1);
- if (status < 0) {
+ if (status != 0) {
ERROR("multimeter plugin: swrite failed.");
return -1;
}
(void *)req_data);
status = swrite(sd, (const char *)&req, REQ_LEN_NOMAC);
- if (status < 0) {
+ if (status != 0) {
DEBUG("`swrite' failed. Closing socket #%i", sd);
close(sd);
sock_descr = sd = -1;
}
dict = PyDict_New(); /* New reference. */
if (value_list->meta) {
- char **table;
+ char **table = NULL;
meta_data_t *meta = value_list->meta;
int num = meta_data_toc(meta, &table);
} else if (type == MD_TYPE_SIGNED_INT) {
if (meta_data_get_signed_int(meta, table[i], &si))
continue;
- temp = PyObject_CallFunctionObjArgs((void *)&SignedType,
- PyLong_FromLongLong(si),
+ PyObject *sival = PyLong_FromLongLong(si); /* New reference */
+ temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
(void *)0); /* New reference. */
PyDict_SetItemString(dict, table[i], temp);
Py_XDECREF(temp);
+ Py_XDECREF(sival);
} else if (type == MD_TYPE_UNSIGNED_INT) {
if (meta_data_get_unsigned_int(meta, table[i], &ui))
continue;
- temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType,
- PyLong_FromUnsignedLongLong(ui),
+ PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
+ temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
(void *)0); /* New reference. */
PyDict_SetItemString(dict, table[i], temp);
Py_XDECREF(temp);
+ Py_XDECREF(uval);
} else if (type == MD_TYPE_DOUBLE) {
if (meta_data_get_double(meta, table[i], &d))
continue;
Notification *n;
CPY_LOCK_THREADS
+ PyObject *dict = PyDict_New(); /* New reference. */
+ for (notification_meta_t *meta = notification->meta; meta != NULL;
+ meta = meta->next) {
+ PyObject *temp = NULL;
+ if (meta->type == NM_TYPE_STRING) {
+ temp = cpy_string_to_unicode_or_bytes(
+ meta->nm_value.nm_string); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ } else if (meta->type == NM_TYPE_SIGNED_INT) {
+ PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
+ temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
+ (void *)0); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ Py_XDECREF(sival);
+ } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
+ PyObject *uval =
+ PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
+ temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
+ (void *)0); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ Py_XDECREF(uval);
+ } else if (meta->type == NM_TYPE_DOUBLE) {
+ temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ } else if (meta->type == NM_TYPE_BOOLEAN) {
+ PyDict_SetItemString(dict, meta->name,
+ meta->nm_value.nm_boolean ? Py_True : Py_False);
+ }
+ }
notify = Notification_New(); /* New reference. */
n = (Notification *)notify;
sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
sstrncpy(n->message, notification->message, sizeof(n->message));
n->severity = notification->severity;
+ Py_CLEAR(n->meta);
+ n->meta = dict; /* Steals a reference. */
ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
(void *)0); /* New reference. */
Py_XDECREF(notify);
for (size_t i = 0; i < ds->ds_num; ++i) {
tuple = PyTuple_New(4);
PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
- PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
- DS_TYPE_TO_STRING(ds->ds[i].type)));
+ PyTuple_SET_ITEM(
+ tuple, 1,
+ cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
PyList_SET_ITEM(list, i, tuple);
register_function(buf, handler,
&(user_data_t){
- .data = c, .free_func = cpy_destroy_user_data,
+ .data = c,
+ .free_func = cpy_destroy_user_data,
});
++cpy_num_callbacks;
/* group = */ "python", buf, cpy_read_callback,
DOUBLE_TO_CDTIME_T(interval),
&(user_data_t){
- .data = c, .free_func = cpy_destroy_user_data,
+ .data = c,
+ .free_func = cpy_destroy_user_data,
});
++cpy_num_callbacks;
return cpy_string_to_unicode_or_bytes(buf);
values = PyTuple_New(ci->values_num); /* New reference. */
for (int i = 0; i < ci->values_num; ++i) {
if (ci->values[i].type == OCONFIG_TYPE_STRING) {
- PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
- ci->values[i].value.string));
+ PyTuple_SET_ITEM(
+ values, i,
+ cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
} else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
PyTuple_SET_ITEM(values, i,
PyFloat_FromDouble(ci->values[i].value.number));
#include "cpython.h"
+typedef struct {
+ int (*add_string)(void *, const char *, const char *);
+ int (*add_signed_int)(void *, const char *, int64_t);
+ int (*add_unsigned_int)(void *, const char *, uint64_t);
+ int (*add_double)(void *, const char *, double);
+ int (*add_boolean)(void *, const char *, _Bool);
+} cpy_build_meta_handler_t;
+
#define FreeAll() \
do { \
PyMem_Free(type); \
return 0;
}
-static meta_data_t *cpy_build_meta(PyObject *meta) {
+static int cpy_build_meta_generic(PyObject *meta,
+ cpy_build_meta_handler_t *meta_func,
+ void *m) {
int s;
- meta_data_t *m = NULL;
PyObject *l;
if ((meta == NULL) || (meta == Py_None))
- return NULL;
+ return -1;
l = PyDict_Items(meta); /* New reference. */
if (!l) {
cpy_log_exception("building meta data");
- return NULL;
+ return -1;
}
s = PyList_Size(l);
if (s <= 0) {
Py_XDECREF(l);
- return NULL;
+ return -1;
}
- m = meta_data_create();
for (int i = 0; i < s; ++i) {
const char *string, *keystring;
PyObject *key, *value, *item, *tmp;
value = PyTuple_GET_ITEM(item, 1);
Py_INCREF(value);
if (value == Py_True) {
- meta_data_add_boolean(m, keystring, 1);
+ meta_func->add_boolean(m, keystring, 1);
} else if (value == Py_False) {
- meta_data_add_boolean(m, keystring, 0);
+ meta_func->add_boolean(m, keystring, 0);
} else if (PyFloat_Check(value)) {
- meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+ meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
} else if (PyObject_TypeCheck(value, &SignedType)) {
long long int lli;
lli = PyLong_AsLongLong(value);
if (!PyErr_Occurred() && (lli == (int64_t)lli))
- meta_data_add_signed_int(m, keystring, lli);
+ meta_func->add_signed_int(m, keystring, lli);
} else if (PyObject_TypeCheck(value, &UnsignedType)) {
long long unsigned llu;
llu = PyLong_AsUnsignedLongLong(value);
if (!PyErr_Occurred() && (llu == (uint64_t)llu))
- meta_data_add_unsigned_int(m, keystring, llu);
+ meta_func->add_unsigned_int(m, keystring, llu);
} else if (PyNumber_Check(value)) {
long long int lli;
long long unsigned llu;
tmp = PyNumber_Long(value);
lli = PyLong_AsLongLong(tmp);
if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
- meta_data_add_signed_int(m, keystring, lli);
+ meta_func->add_signed_int(m, keystring, lli);
} else {
PyErr_Clear();
llu = PyLong_AsUnsignedLongLong(tmp);
if (!PyErr_Occurred() && (llu == (uint64_t)llu))
- meta_data_add_unsigned_int(m, keystring, llu);
+ meta_func->add_unsigned_int(m, keystring, llu);
}
Py_XDECREF(tmp);
} else {
string = cpy_unicode_or_bytes_to_string(&value);
if (string) {
- meta_data_add_string(m, keystring, string);
+ meta_func->add_string(m, keystring, string);
} else {
PyErr_Clear();
tmp = PyObject_Str(value);
string = cpy_unicode_or_bytes_to_string(&tmp);
if (string)
- meta_data_add_string(m, keystring, string);
+ meta_func->add_string(m, keystring, string);
Py_XDECREF(tmp);
}
}
Py_DECREF(key);
}
Py_XDECREF(l);
+ return 0;
+}
+
+#define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
+ static int cpy_##func(void *meta, const char *key, val_type val) { \
+ return func((meta_type *)meta, key, val); \
+ }
+
+#define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool) \
+ \
+ static cpy_build_meta_handler_t cpy_##func_prefix = { \
+ .add_string = cpy_##func_prefix##_add_string, \
+ .add_signed_int = cpy_##func_prefix##_add_signed_int, \
+ .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int, \
+ .add_double = cpy_##func_prefix##_add_double, \
+ .add_boolean = cpy_##func_prefix##_add_boolean}
+
+CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
+CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
+
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+ meta_data_t *m = meta_data_create();
+ if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
+ meta_data_destroy(m);
+ return NULL;
+ }
return m;
}
+static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
+ cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
+}
+
static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
int ret;
const data_set_t *ds;
Values_new /* tp_new */
};
+static char notification_meta_doc[] =
+ "These are the meta data for the Notification object.\n"
+ "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+ "strings. int and long objects will be dispatched as signed integers "
+ "unless\n"
+ "they are between 2**63 and 2**64-1, which will result in an unsigned "
+ "integer.\n"
+ "One of these storage classes can be forced by using the classes\n"
+ "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
+ "notification callback will always contain Signed or Unsigned objects.";
+
static char severity_doc[] =
"The severity of this notification. Assign or compare to\n"
"NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
int severity = 0;
double time = 0;
char *message = NULL;
+ PyObject *meta = NULL;
char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
*plugin = NULL, *host = NULL;
- static char *kwlist[] = {"type", "message", "plugin_instance",
- "type_instance", "plugin", "host",
- "time", "severity", NULL};
+ static char *kwlist[] = {
+ "type", "message", "plugin_instance", "type_instance", "plugin",
+ "host", "time", "severity", "meta", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
- &type, NULL, &message, NULL,
- &plugin_instance, NULL, &type_instance, NULL,
- &plugin, NULL, &host, &time, &severity))
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|etetetetetetdiO", kwlist, NULL, &type, NULL, &message,
+ NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL,
+ &host, &time, &severity, &meta))
return -1;
if (type && plugin_get_ds(type) == NULL) {
FreeAll();
PyMem_Free(message);
+
+ if (meta == NULL) {
+ meta = PyDict_New();
+ PyErr_Clear();
+ } else {
+ Py_INCREF(meta);
+ }
+
+ PyObject *tmp = self->meta;
+ self->meta = meta;
+ Py_XDECREF(tmp);
+
return 0;
}
const data_set_t *ds;
notification_t notification;
double t = self->data.time;
+ PyObject *meta = self->meta;
int severity = self->severity;
char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
*type_instance = NULL;
char *message = NULL;
- static char *kwlist[] = {"type", "message", "plugin_instance",
- "type_instance", "plugin", "host",
- "time", "severity", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
+ static char *kwlist[] = {
+ "type", "message", "plugin_instance", "type_instance", "plugin",
+ "host", "time", "severity", "meta", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
&type, NULL, &message, NULL,
&plugin_instance, NULL, &type_instance, NULL,
- &plugin, NULL, &host, &t, &severity))
+ &plugin, NULL, &host, &t, &severity, &meta))
return NULL;
notification.time = DOUBLE_TO_CDTIME_T(t);
PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
return NULL;
}
+ if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+ PyErr_Format(PyExc_TypeError, "meta must be a dict");
+ return NULL;
+ }
+ cpy_build_notification_meta(¬ification, meta);
if (notification.time == 0)
notification.time = cdtime();
sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
Py_BEGIN_ALLOW_THREADS;
ret = plugin_dispatch_notification(¬ification);
+ if (notification.meta)
+ plugin_notification_meta_free(notification.meta);
Py_END_ALLOW_THREADS;
if (ret != 0) {
PyErr_SetString(PyExc_RuntimeError,
if (self == NULL)
return NULL;
+ self->meta = PyDict_New();
self->message[0] = 0;
self->severity = 0;
return (PyObject *)self;
static PyObject *Notification_repr(PyObject *s) {
PyObject *ret, *tmp;
- static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
+ static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
+ *l_closing = NULL;
Notification *self = (Notification *)s;
if (l_severity == NULL)
l_severity = cpy_string_to_unicode_or_bytes(",severity=");
if (l_message == NULL)
l_message = cpy_string_to_unicode_or_bytes(",message=");
+ if (l_meta == NULL)
+ l_meta = cpy_string_to_unicode_or_bytes(",meta=");
if (l_closing == NULL)
l_closing = cpy_string_to_unicode_or_bytes(")");
- if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+ if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
+ l_closing == NULL)
return NULL;
ret = cpy_common_repr(s);
CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
CPY_STRCAT_AND_DEL(&ret, tmp);
}
+ if (self->meta &&
+ (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+ CPY_STRCAT(&ret, l_meta);
+ tmp = PyObject_Repr(self->meta);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
CPY_STRCAT(&ret, l_closing);
return ret;
}
+static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
+ Notification *n = (Notification *)self;
+ Py_VISIT(n->meta);
+ return 0;
+}
+
+static int Notification_clear(PyObject *self) {
+ Notification *n = (Notification *)self;
+ Py_CLEAR(n->meta);
+ return 0;
+}
+
+static void Notification_dealloc(PyObject *self) {
+ Notification_clear(self);
+ self->ob_type->tp_free(self);
+}
+
static PyMethodDef Notification_methods[] = {
{"dispatch", (PyCFunction)Notification_dispatch,
METH_VARARGS | METH_KEYWORDS, dispatch_doc},
static PyMemberDef Notification_members[] = {
{"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
+ {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
+ notification_meta_doc},
{NULL}};
static PyGetSetDef Notification_getseters[] = {
{NULL}};
PyTypeObject NotificationType = {
- CPY_INIT_TYPE "collectd.Notification", /* tp_name */
- sizeof(Notification), /* tp_basicsize */
- 0, /* Will be filled in later */
- 0, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- Notification_repr, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- Notification_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- Notification_methods, /* tp_methods */
- Notification_members, /* tp_members */
- Notification_getseters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- Notification_init, /* tp_init */
- 0, /* tp_alloc */
- Notification_new /* tp_new */
+ CPY_INIT_TYPE "collectd.Notification", /* tp_name */
+ sizeof(Notification), /* tp_basicsize */
+ 0, /* Will be filled in later */
+ Notification_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ Notification_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ Notification_doc, /* tp_doc */
+ Notification_traverse, /* tp_traverse */
+ Notification_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Notification_methods, /* tp_methods */
+ Notification_members, /* tp_members */
+ Notification_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ Notification_init, /* tp_init */
+ 0, /* tp_alloc */
+ Notification_new /* tp_new */
};
static char Signed_doc[] =
/*
* Private types
*/
-struct rrd_cache_s {
+typedef struct rrd_cache_s {
int values_num;
char **values;
cdtime_t first_value;
cdtime_t last_value;
int64_t random_variation;
enum { FLAG_NONE = 0x00, FLAG_QUEUED = 0x01, FLAG_FLUSHQ = 0x02 } flags;
-};
-typedef struct rrd_cache_s rrd_cache_t;
+} rrd_cache_t;
enum rrd_queue_dir_e { QUEUE_INSERT_FRONT, QUEUE_INSERT_BACK };
typedef enum rrd_queue_dir_e rrd_queue_dir_t;
#if HAVE_THREADSAFE_LIBRRD
static int srrd_update(char *filename, char *template, int argc,
const char **argv) {
- int status;
-
optind = 0; /* bug in librrd? */
rrd_clear_error();
- status = rrd_update_r(filename, template, argc, (void *)argv);
-
+ int status = rrd_update_r(filename, template, argc, (void *)argv);
if (status != 0) {
WARNING("rrdtool plugin: rrd_update_r (%s) failed: %s", filename,
rrd_get_error());
static int rrd_write(const data_set_t *ds, const value_list_t *vl,
user_data_t __attribute__((unused)) * user_data) {
- struct stat statbuf;
- char filename[512];
- char values[512];
- int status;
if (do_shutdown)
return 0;
return -1;
}
+ char filename[PATH_MAX];
if (value_list_to_filename(filename, sizeof(filename), vl) != 0)
return -1;
+ char values[32 * ds->ds_num];
if (value_list_to_string(values, sizeof(values), ds, vl) != 0)
return -1;
+ struct stat statbuf = {0};
if (stat(filename, &statbuf) == -1) {
if (errno == ENOENT) {
- status = cu_rrd_create_file(filename, ds, vl, &rrdcreate_config);
- if (status != 0)
+ if (cu_rrd_create_file(filename, ds, vl, &rrdcreate_config) != 0) {
return -1;
- else if (rrdcreate_config.async)
+ } else if (rrdcreate_config.async) {
return 0;
+ }
} else {
char errbuf[1024];
- ERROR("stat(%s) failed: %s", filename,
+ ERROR("rrdtool plugin: stat(%s) failed: %s", filename,
sstrerror(errno, errbuf, sizeof(errbuf)));
return -1;
}
} else if (!S_ISREG(statbuf.st_mode)) {
- ERROR("stat(%s): Not a regular file!", filename);
+ ERROR("rrdtool plugin: stat(%s): Not a regular file!", filename);
return -1;
}
- status = rrd_cache_insert(filename, values, vl->time);
-
- return status;
+ return rrd_cache_insert(filename, values, vl->time);
} /* int rrd_write */
static int rrd_flush(cdtime_t timeout, const char *identifier,
static int rrd_init(void) {
static int init_once = 0;
- int status;
if (init_once != 0)
return 0;
random_timeout = 0;
cache_flush_timeout = 0;
} else if (cache_flush_timeout < cache_timeout) {
- INFO("rrdtool plugin: \"CacheFlush %.3f\" is less than \"CacheTimeout %.3f\". "
+ INFO("rrdtool plugin: \"CacheFlush %.3f\" is less than \"CacheTimeout "
+ "%.3f\". "
"Ajusting \"CacheFlush\" to %.3f seconds.",
CDTIME_T_TO_DOUBLE(cache_flush_timeout),
CDTIME_T_TO_DOUBLE(cache_timeout),
pthread_mutex_unlock(&cache_lock);
- status =
+ int status =
plugin_thread_create(&queue_thread, /* attr = */ NULL, rrd_queue_thread,
/* args = */ NULL, "rrdtool queue");
if (status != 0) {
struct data_definition_s *next;
char **ignores;
size_t ignores_len;
- int invert_match;
+ _Bool invert_match;
};
typedef struct data_definition_s data_definition_t;
char *name;
char *address;
int version;
+ cdtime_t timeout;
+ int retries;
/* snmpv1/2 options */
char *community;
return 0;
} /* int csnmp_config_add_data_blacklist */
-static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd,
- oconfig_item_t *ci) {
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
- WARNING("snmp plugin: `InvertMatch' needs exactly one boolean argument.");
- return -1;
- }
-
- dd->invert_match = ci->values[0].value.boolean ? 1 : 0;
-
- return 0;
-} /* int csnmp_config_add_data_blacklist_match_inverted */
-
static int csnmp_config_add_data(oconfig_item_t *ci) {
- data_definition_t *dd;
- int status = 0;
-
- dd = calloc(1, sizeof(*dd));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
if (dd == NULL)
return -1;
- status = cf_util_get_string(ci, &dd->name);
+ int status = cf_util_get_string(ci, &dd->name);
if (status != 0) {
- free(dd);
+ sfree(dd);
return -1;
}
else if (strcasecmp("Ignore", option->key) == 0)
status = csnmp_config_add_data_blacklist(dd, option);
else if (strcasecmp("InvertMatch", option->key) == 0)
- status = csnmp_config_add_data_blacklist_match_inverted(dd, option);
+ status = cf_util_get_boolean(option, &dd->invert_match);
else {
WARNING("snmp plugin: Option `%s' not allowed here.", option->key);
status = -1;
hd->sess_handle = NULL;
hd->interval = 0;
+ /* These mean that we have not set a timeout or retry value */
+ hd->timeout = 0;
+ hd->retries = -1;
+
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *option = ci->children + i;
status = 0;
status = cf_util_get_string(option, &hd->community);
else if (strcasecmp("Version", option->key) == 0)
status = csnmp_config_add_host_version(hd, option);
+ else if (strcasecmp("Timeout", option->key) == 0)
+ cf_util_get_cdtime(option, &hd->timeout);
+ else if (strcasecmp("Retries", option->key) == 0)
+ cf_util_get_int(option, &hd->retries);
else if (strcasecmp("Collect", option->key) == 0)
csnmp_config_add_host_collect(hd, option);
else if (strcasecmp("Interval", option->key) == 0)
sess.community_len = strlen(host->community);
}
+ /* Set timeout & retries, if they have been changed from the default */
+ if (host->timeout != 0) {
+ /* net-snmp expects microseconds */
+ sess.timeout = CDTIME_T_TO_US(host->timeout);
+ }
+ if (host->retries >= 0) {
+ sess.retries = host->retries;
+ }
+
/* snmp_sess_open will copy the `struct snmp_session *'. */
host->sess_handle = snmp_sess_open(&sess);
struct variable_list *vb;
oid_t vb_name;
int status;
- uint32_t is_matched;
/* Set vb on the last variable */
for (vb = res->variables; (vb != NULL) && (vb->next_variable != NULL);
char *ptr;
csnmp_strvbcopy(il->instance, vb, sizeof(il->instance));
- is_matched = 0;
+ _Bool is_matched = 0;
for (uint32_t i = 0; i < dd->ignores_len; i++) {
status = fnmatch(dd->ignores[i], il->instance, 0);
if (status == 0) {
- if (dd->invert_match == 0) {
+ if (!dd->invert_match) {
sfree(il);
return 0;
} else {
}
}
}
- if (dd->invert_match != 0 && is_matched == 0) {
+ if (dd->invert_match && !is_matched) {
sfree(il);
return 0;
}
snmp_free_pdu(res);
res = NULL;
-
if (status == 0)
csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
status = write(fd, pkt_request, sizeof(pkt_request));
if (status <= 0) {
- ERROR("ted plugin: swrite failed.");
+ ERROR("ted plugin: write failed.");
return -1;
}
const data_set_t *ds, const value_list_t *vl,
int store_rates,
char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl) {
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
char temp[512];
size_t offset = 0;
int status;
for (size_t i = 0; i < ds->ds_num; i++) {
/* All value lists have a leading comma. The first one will be replaced with
* a square bracket in `format_kairosdb_finalize'. */
- BUFFER_ADD(",{");
+ BUFFER_ADD(",{\"name\":\"");
- BUFFER_ADD("\"name\":\"collectd");
+ if (metrics_prefix != NULL) {
+ BUFFER_ADD("%s.", metrics_prefix);
+ }
- BUFFER_ADD(".%s", vl->plugin);
+ BUFFER_ADD("%s", vl->plugin);
status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i);
if (status != 0)
char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds,
const value_list_t *vl, int store_rates, size_t temp_size,
- char const *const *http_attrs, size_t http_attrs_num, int data_ttl) {
+ char const *const *http_attrs, size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
char temp[temp_size];
int status;
status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
- http_attrs, http_attrs_num, data_ttl);
+ http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
if (status != 0)
return status;
temp_size = strlen(temp);
size_t *ret_buffer_fill, size_t *ret_buffer_free,
const data_set_t *ds, const value_list_t *vl,
int store_rates, char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl) {
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
(ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
return -EINVAL;
return format_kairosdb_value_list_nocheck(
buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
- (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl);
+ (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
} /* }}} int format_kairosdb_value_list */
/* vim: set sw=2 sts=2 et fdm=marker : */
size_t *ret_buffer_free, const data_set_t *ds,
const value_list_t *vl, int store_rates,
char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl);
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix);
int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
size_t *ret_buffer_free);
_Bool collect_mgt;
_Bool collect_smf;
_Bool collect_vbe;
+ _Bool collect_mse;
#endif
};
typedef struct user_config_s user_config_t; /* }}} */
else if (strcmp(name, "esi_warnings") == 0)
return varnish_submit_derive(conf->instance, "esi", "total_operations",
"warning", val);
+ else if (strcmp(name, "esi_maxdepth") == 0)
+ return varnish_submit_derive(conf->instance, "esi", "total_operations",
+ "max_depth", val);
}
if (conf->collect_backend) {
else if (strcmp(name, "s_req_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"req_body", val);
+ else if (strcmp(name, "s_req_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+ "req_proto", val);
else if (strcmp(name, "s_resp_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"resp_header", val);
else if (strcmp(name, "s_resp_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"resp_body", val);
+ else if (strcmp(name, "s_resp_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+ "resp_proto", val);
else if (strcmp(name, "s_pipe_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"pipe_header", val);
else if (strcmp(name, "busy_killed") == 0)
return varnish_submit_derive(conf->instance, "workers", "http_requests",
"busy_killed", val);
-
#endif
}
else if (strcmp(name, "bereq_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "vbe",
"total_bytes", "bereq_bodybytes", val);
+ else if (strcmp(name, "bereq_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "bereq_protobytes", val);
else if (strcmp(name, "beresp_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "vbe",
"total_bytes", "beresp_hdrbytes", val);
else if (strcmp(name, "beresp_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "vbe",
"total_bytes", "beresp_bodybytes", val);
+ else if (strcmp(name, "beresp_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "beresp_protobytes", val);
else if (strcmp(name, "pipe_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "vbe",
"total_bytes", "pipe_hdrbytes", val);
}
/* All Stevedores support these counters */
- if (conf->collect_sma || conf->collect_smf) {
+ if (conf->collect_sma || conf->collect_smf || conf->collect_mse) {
char category[4];
if (conf->collect_sma)
strncpy(category, "sma", 4);
- else
+ else if (conf->collect_smf)
strncpy(category, "smf", 4);
+ else
+ strncpy(category, "mse", 4);
if (strcmp(name, "c_req") == 0)
return varnish_submit_derive(conf->instance, category,
return varnish_submit_gauge(conf->instance, "mempool",
"objects", "ran_dry", val);
}
+
+ if (conf->collect_mse) {
+ if (strcmp(name, "c_full") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "full_allocs", val);
+ else if (strcmp(name, "c_truncated") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "truncated_allocs", val);
+ else if (strcmp(name, "c_expanded") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "expanded_allocs", val);
+ else if (strcmp(name, "c_failed") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "failed_allocs", val);
+ else if (strcmp(name, "c_bytes") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_bytes", "bytes_allocated", val);
+ else if (strcmp(name, "c_freed") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_bytes", "bytes_freed", val);
+ else if (strcmp(name, "g_fo_alloc") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "fo_allocs_outstanding", val);
+ else if (strcmp(name, "g_fo_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "fo_bytes_outstanding", val);
+ else if (strcmp(name, "g_membuf_alloc") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "objects", "membufs_allocated", val);
+ else if (strcmp(name, "g_membuf_inuse") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "objects", "membufs_inuse", val);
+ else if (strcmp(name, "g_bans_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "persisted_banspace_used", val);
+ else if (strcmp(name, "g_bans_space") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "persisted_banspace_available", val);
+ else if (strcmp(name, "g_bans_persisted") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "bans_persisted", val);
+ else if (strcmp(name, "g_bans_lost") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "bans_lost", val);
+
+ /* mse seg */
+ else if (strcmp(name, "g_journal_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_reg",
+ "bytes", "journal_bytes_used", val);
+ else if (strcmp(name, "g_journal_space") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_reg",
+ "bytes", "journal_bytes_free", val);
+
+ /* mse segagg */
+ else if (strcmp(name, "g_bigspace") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "bytes", "big_extents_bytes_available", val);
+ else if (strcmp(name, "g_extfree") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "free_extents", val);
+ else if (strcmp(name, "g_sparenode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "spare_nodes_available", val);
+ else if (strcmp(name, "g_objnode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "object_nodes_in_use", val);
+ else if (strcmp(name, "g_extnode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "extent_nodes_in_use", val);
+ else if (strcmp(name, "g_bigextfree") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "free_big_extents", val);
+ else if (strcmp(name, "c_pruneloop") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "prune_loops", val);
+ else if (strcmp(name, "c_pruned") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_objects", "pruned_objects", val);
+ else if (strcmp(name, "c_spared") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "spared_objects", val);
+ else if (strcmp(name, "c_skipped") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "missed_objects", val);
+ else if (strcmp(name, "c_nuked") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "nuked_objects", val);
+ else if (strcmp(name, "c_sniped") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "sniped_objects", val);
+
+ }
+
#endif
return 0;
conf->collect_mgt = 0;
conf->collect_smf = 0;
conf->collect_vbe = 0;
+ conf->collect_mse = 0;
#endif
return 0;
WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
child->key, "v4");
#endif
+ else if (strcasecmp("CollectMSE", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_mse);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "Plus v4");
+#endif
else {
WARNING("Varnish plugin: Ignoring unknown "
"configuration option: \"%s\". Did "
#if HAVE_VARNISH_V4
&& !conf->collect_vsm && !conf->collect_vbe && !conf->collect_smf
&& !conf->collect_mgt && !conf->collect_lck && !conf->collect_mempool
+ && !conf->collect_mse
#endif
) {
WARNING("Varnish plugin: No metric has been configured for "
#define WRITE_HTTP_DEFAULT_BUFFER_SIZE 4096
#endif
+#ifndef WRITE_HTTP_DEFAULT_PREFIX
+#define WRITE_HTTP_DEFAULT_PREFIX "collectd"
+#endif
+
/*
* Private variables
*/
pthread_mutex_t send_lock;
int data_ttl;
+ char *metrics_prefix;
};
typedef struct wh_callback_s wh_callback_t;
sfree(cb->clientcert);
sfree(cb->clientkeypass);
sfree(cb->send_buffer);
+ sfree(cb->metrics_prefix);
sfree(cb);
} /* }}} void wh_callback_free */
status = format_kairosdb_value_list(
cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
- cb->data_ttl);
+ cb->data_ttl, cb->metrics_prefix);
if (status == -ENOMEM) {
status = wh_flush_nolock(/* timeout = */ 0, cb);
if (status != 0) {
status = format_kairosdb_value_list(
cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
- cb->data_ttl);
+ cb->data_ttl, cb->metrics_prefix);
}
if (status != 0) {
pthread_mutex_unlock(&cb->send_lock);
cb->send_metrics = 1;
cb->send_notifications = 0;
cb->data_ttl = 0;
+ cb->metrics_prefix = strdup(WRITE_HTTP_DEFAULT_PREFIX);
+
+ if (cb->metrics_prefix == NULL) {
+ ERROR("write_http plugin: strdup failed.");
+ sfree(cb);
+ return -1;
+ }
pthread_mutex_init(&cb->send_lock, /* attr = */ NULL);
sfree(val);
} else if (strcasecmp("TTL", child->key) == 0) {
status = cf_util_get_int(child, &cb->data_ttl);
+ } else if (strcasecmp("Prefix", child->key) == 0) {
+ status = cf_util_get_string(child, &cb->metrics_prefix);
} else {
ERROR("write_http plugin: Invalid configuration "
"option: %s.",
return -1;
}
+ if (strlen(cb->metrics_prefix) == 0)
+ sfree(cb->metrics_prefix);
+
if (cb->low_speed_limit > 0)
cb->low_speed_time = CDTIME_T_TO_TIME_T(plugin_get_interval());
#include <microhttpd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
#ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA
#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300)
#endif
}
/* }}} */
+#if MHD_VERSION >= 0x00090000
+static int prom_open_socket(int addrfamily) {
+ /* {{{ */
+ char service[NI_MAXSERV];
+ snprintf(service, sizeof(service), "%hu", httpd_port);
+
+ struct addrinfo *res;
+ int status = getaddrinfo(NULL, service,
+ &(struct addrinfo){
+ .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
+ .ai_family = addrfamily,
+ .ai_socktype = SOCK_STREAM,
+ },
+ &res);
+ if (status != 0) {
+ return -1;
+ }
+
+ int fd = -1;
+ for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
+ fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0);
+ if (fd == -1)
+ continue;
+
+ if (bind(fd, ai->ai_addr, ai->ai_addrlen) != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ if (listen(fd, /* backlog = */ 16) != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res);
+
+ return fd;
+} /* }}} int prom_open_socket */
+
+static struct MHD_Daemon *prom_start_daemon() {
+ /* {{{ */
+ int fd = prom_open_socket(PF_INET6);
+ if (fd == -1)
+ fd = prom_open_socket(PF_INET);
+ if (fd == -1) {
+ ERROR("write_prometheus plugin: Opening a listening socket failed.");
+ return NULL;
+ }
+
+ struct MHD_Daemon *d =
+ MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+ NULL, MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_END);
+ if (d == NULL) {
+ ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+ close(fd);
+ return NULL;
+ }
+
+ return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#else /* if MHD_VERSION < 0x00090000 */
+static struct MHD_Daemon *prom_start_daemon() {
+ /* {{{ */
+ struct MHD_Daemon *d =
+ MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+ NULL, MHD_OPTION_END);
+ if (d == NULL) {
+ ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+ return NULL;
+ }
+
+ return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#endif
+
/*
* collectd callbacks
*/
}
if (httpd == NULL) {
- unsigned int flags = MHD_USE_THREAD_PER_CONNECTION;
-#if MHD_VERSION >= 0x00093300
- flags |= MHD_USE_DUAL_STACK;
-#endif
-
- httpd = MHD_start_daemon(flags, httpd_port,
- /* MHD_AcceptPolicyCallback = */ NULL,
- /* MHD_AcceptPolicyCallback arg = */ NULL,
- http_handler, NULL, MHD_OPTION_END);
+ httpd = prom_start_daemon();
if (httpd == NULL) {
ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
return -1;
ssize_t status = 0;
status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
- if (status < 0) {
+ if (status != 0) {
char errbuf[1024];
ERROR("write_tsdb plugin: send failed with status %zi (%s)", status,
sstrerror(errno, errbuf, sizeof(errbuf)));