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.
src/daemon/configfile.h \
src/daemon/filter_chain.c \
src/daemon/filter_chain.h \
+ src/daemon/globals.c \
+ src/daemon/globals.h \
src/daemon/meta_data.c \
src/daemon/meta_data.h \
src/daemon/plugin.c \
}
/* compact_ds_name removed the special characters ":", "_", "-" and "+" from the
- * intput string. Characters following these special characters are capitalized.
+ * input string. Characters following these special characters are capitalized.
* Trailing "+" and "-" characters are replaces with the strings "Plus" and
* "Minus". */
static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
If a Python script throws an exception it will be logged by collectd with the
name of the exception and the message. If you set this option to true it will
also log the full stacktrace just like the default output of an interactive
-Python interpreter. This should probably be set to false most of the time but
-is very useful for development and debugging of new modules.
+Python interpreter. This does not apply to the CollectError exception, which
+will never log a stacktrace.
+This should probably be set to false most of the time but is very useful for
+development and debugging of new modules.
=item B<Interactive> I<bool>
The following complex types are used to pass values between the Python plugin
and collectd:
+=head2 CollectdError
+
+This is an exception. If any Python script raises this exception it will
+still be treated like an error by collectd but it will be logged as a
+warning instead of an error and it will never generate a stacktrace.
+
+ class CollectdError(Exception)
+
+Basic exception for collectd Python scripts.
+Throwing this exception will not cause a stacktrace to be logged, even if
+LogTraces is enabled in the config.
+
=head2 Signed
The Signed class is just a long. It has all its methods and behaves exactly
=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
#<Plugin curl_xml>
# <URL "http://localhost/stats.xml">
# Host "my_host"
+# #Plugin "stats"
# Instance "some_instance"
# User "collectd"
# Password "thaiNg0I"
# Type "magic_level"
# #InstancePrefix "prefix-"
# InstanceFrom "td[1]"
+# #PluginInstanceFrom "td[1]"
# ValuesFrom "td[2]/span[@class=\"level\"]"
# </XPath>
# </URL>
# 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
<Plugin "curl_xml">
<URL "http://localhost/stats.xml">
Host "my_host"
+ #Plugin "curl_xml"
Instance "some_instance"
User "collectd"
Password "thaiNg0I"
Type "magic_level"
#InstancePrefix "prefix-"
InstanceFrom "td[1]"
+ #PluginInstanceFrom "td[1]"
ValuesFrom "td[2]/span[@class=\"level\"]"
</XPath>
</URL>
Use I<Name> as the host name when submitting values. Defaults to the global
host name setting.
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to 'curl_xml'.
+
=item B<Instance> I<Instance>
-Use I<Instance> as the plugin instance when submitting values. Defaults to an
-empty string (no plugin instance).
+Use I<Instance> as the plugin instance when submitting values.
+May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
+Defaults to an empty string (no plugin instance).
=item B<Namespace> I<Prefix> I<URL>
XPath expression must return exactly one element. The element's value is then
used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
-This value is required. As a special exception, if the "base XPath expression"
-(the argument to the B<XPath> block) returns exactly one argument, then this
-option may be omitted.
+=item B<PluginInstanceFrom> I<PluginInstanceFrom>
+
+Specifies a XPath expression to use for determining the I<plugin instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<plugin instance>.
+
+=back
+
+If the "base XPath expression" (the argument to the B<XPath> block) returns
+exactly one argument, then I<InstanceFrom> and I<PluginInstanceFrom> may be omitted.
+Otherwise, at least one of I<InstanceFrom> or I<PluginInstanceFrom> is required.
+
+=over 4
=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
I<type> specified with B<Type> (see above). Each XPath expression must return
exactly one element. The element's value is then parsed as a number and used as
value for the appropriate value in the value list dispatched to the daemon.
+This option is required.
=back
"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>,
an interval. If set to B<False>, the default, these values aren't calculated /
dispatched.
+Please note what reported timer values less than 0.001 are ignored in all B<Timer*> reports.
+
=back
=head2 Plugin C<swap>
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>.
/* Python object declarations. */
typedef struct {
+ // clang-format off
PyObject_HEAD /* No semicolon! */
- PyObject *parent; /* Config */
+ PyObject *parent; /* Config */
PyObject *key; /* String */
PyObject *values; /* Sequence */
PyObject *children; /* Sequence */
+ // clang-format on
} Config;
extern PyTypeObject ConfigType;
typedef struct {
+ // clang-format off
PyObject_HEAD /* No semicolon! */
- double time;
+ double time;
+ // clang-format on
char host[DATA_MAX_NAME_LEN];
char plugin[DATA_MAX_NAME_LEN];
char plugin_instance[DATA_MAX_NAME_LEN];
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]));
size_t values_len;
char *instance_prefix;
char *instance;
+ char *plugin_instance_from;
int is_table;
unsigned long magic;
};
struct cx_s /* {{{ */
{
char *instance;
+ char *plugin_name;
char *host;
char *url;
size_t buffer_size;
size_t buffer_fill;
- llist_t *list; /* list of xpath blocks */
+ llist_t *xpath_list; /* list of xpath blocks */
};
typedef struct cx_s cx_t; /* }}} */
static size_t cx_curl_callback(void *buf, /* {{{ */
size_t size, size_t nmemb, void *user_data) {
size_t len = size * nmemb;
- cx_t *db;
- db = user_data;
+ cx_t *db = user_data;
if (db == NULL) {
ERROR("curl_xml plugin: cx_curl_callback: "
"user_data pointer is NULL.");
return len;
if ((db->buffer_fill + len) >= db->buffer_size) {
- char *temp;
-
- temp = realloc(db->buffer, db->buffer_fill + len + 1);
+ char *temp = realloc(db->buffer, db->buffer_fill + len + 1);
if (temp == NULL) {
ERROR("curl_xml plugin: realloc failed.");
return 0;
sfree(xpath->path);
sfree(xpath->type);
sfree(xpath->instance_prefix);
+ sfree(xpath->plugin_instance_from);
sfree(xpath->instance);
sfree(xpath->values);
sfree(xpath);
} /* }}} void cx_xpath_free */
-static void cx_list_free(llist_t *list) /* {{{ */
+static void cx_xpath_list_free(llist_t *list) /* {{{ */
{
llentry_t *le;
le = llist_head(list);
while (le != NULL) {
- llentry_t *le_next;
+ llentry_t *le_next = le->next;
- le_next = le->next;
-
- sfree(le->key);
+ /* this also frees xpath->path used for le->key */
cx_xpath_free(le->value);
le = le_next;
}
llist_destroy(list);
-} /* }}} void cx_list_free */
+} /* }}} void cx_xpath_list_free */
static void cx_free(void *arg) /* {{{ */
{
curl_easy_cleanup(db->curl);
db->curl = NULL;
- if (db->list != NULL)
- cx_list_free(db->list);
+ if (db->xpath_list != NULL)
+ cx_xpath_list_free(db->xpath_list);
sfree(db->buffer);
sfree(db->instance);
+ sfree(db->plugin_name);
sfree(db->host);
sfree(db->url);
sfree(db);
} /* }}} void cx_free */
-static const char *cx_host(cx_t *db) /* {{{ */
+static const char *cx_host(const cx_t *db) /* {{{ */
{
if (db->host == NULL)
return hostname_g;
return 0;
} /* }}} cx_check_type */
-static xmlXPathObjectPtr
-cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- xmlChar *expr) {
- xmlXPathObjectPtr xpath_obj;
-
- /* XXX: When to free this? */
- xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+static xmlXPathObjectPtr cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx,
+ char *expr) /* {{{ */
+{
+ xmlXPathObjectPtr xpath_obj =
+ xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
if (xpath_obj == NULL) {
WARNING("curl_xml plugin: "
"Error unable to evaluate xpath expression \"%s\". Skipping...",
return -1;
} /* }}} cx_if_not_text_node */
-static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- cx_xpath_t *xpath, const data_set_t *ds,
- value_list_t *vl, int index) {
- xmlXPathObjectPtr values_node_obj;
- xmlNodeSetPtr values_node;
- int tmp_size;
- char *node_value;
-
- values_node_obj =
- cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->values[index].path);
+/*
+ * Returned value should be freed with xmlFree().
+ */
+static char *cx_get_text_node_value(xmlXPathContextPtr xpath_ctx, /* {{{ */
+ char *expr, const char *from_option) {
+ xmlXPathObjectPtr values_node_obj = cx_evaluate_xpath(xpath_ctx, expr);
if (values_node_obj == NULL)
- return -1; /* Error already logged. */
+ return NULL; /* Error already logged. */
- values_node = values_node_obj->nodesetval;
- tmp_size = (values_node) ? values_node->nodeNr : 0;
+ xmlNodeSetPtr values_node = values_node_obj->nodesetval;
+ size_t tmp_size = (values_node) ? values_node->nodeNr : 0;
if (tmp_size == 0) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" doesn't match any of the nodes. "
- "Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' doesn't match "
+ "any of the nodes.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
if (tmp_size > 1) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return "
- "only one node. Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' is expected to return "
+ "only one text node. Skipping the node.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
/* ignoring the element if other than textnode/attribute*/
if (cx_if_not_text_node(values_node->nodeTab[0])) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return "
- "only text/attribute node which is not the case. Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' is expected to return "
+ "only text/attribute node which is not the case. "
+ "Skipping the node.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
- node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+ char *node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+
+ /* free up object */
+ xmlXPathFreeObject(values_node_obj);
+
+ return node_value;
+} /* }}} char * cx_get_text_node_value */
+
+static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, const data_set_t *ds,
+ value_list_t *vl, int index) {
+
+ char *node_value = cx_get_text_node_value(
+ xpath_ctx, xpath->values[index].path, "ValuesFrom");
+
+ if (node_value == NULL)
+ return -1;
+
switch (ds->ds[index].type) {
case DS_TYPE_COUNTER:
vl->values[index].counter =
/* endptr = */ NULL);
}
- /* free up object */
- xmlXPathFreeObject(values_node_obj);
- sfree(node_value);
+ xmlFree(node_value);
/* We have reached here which means that
* we have got something to work */
cx_xpath_t *xpath, const data_set_t *ds,
value_list_t *vl) {
value_t values[xpath->values_len];
- int status;
assert(xpath->values_len > 0);
assert(xpath->values_len == vl->values_len);
vl->values = values;
for (size_t i = 0; i < xpath->values_len; i++) {
- status = cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i);
- if (status != 0)
+ if (cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i) != 0)
return -1; /* An error has been printed. */
} /* for (i = 0; i < xpath->values_len; i++) */
} /* }}} int cx_handle_all_value_xpaths */
static int cx_handle_instance_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- cx_xpath_t *xpath, value_list_t *vl,
- _Bool is_table) {
- xmlXPathObjectPtr instance_node_obj = NULL;
- xmlNodeSetPtr instance_node = NULL;
-
- memset(vl->type_instance, 0, sizeof(vl->type_instance));
-
- /* If the base xpath returns more than one block, the result is assumed to be
- * a table. The `Instance' option is not optional in this case. Check for the
- * condition and inform the user. */
- if (is_table && (xpath->instance == NULL)) {
- WARNING("curl_xml plugin: "
- "Base-XPath %s is a table (more than one result was returned), "
- "but no instance-XPath has been defined.",
- xpath->path);
- return -1;
- }
+ cx_xpath_t *xpath, value_list_t *vl) {
- /* instance has to be an xpath expression */
+ /* Handle type instance */
if (xpath->instance != NULL) {
- int tmp_size;
-
- instance_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->instance);
- if (instance_node_obj == NULL)
- return -1; /* error is logged already */
-
- instance_node = instance_node_obj->nodesetval;
- tmp_size = (instance_node) ? instance_node->nodeNr : 0;
-
- if (tmp_size <= 0) {
- WARNING(
- "curl_xml plugin: "
- "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
- "any of the nodes. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
+ char *node_value =
+ cx_get_text_node_value(xpath_ctx, xpath->instance, "InstanceFrom");
+ if (node_value == NULL)
return -1;
- }
- if (tmp_size > 1) {
- WARNING("curl_xml plugin: "
- "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
- "to return only one text node. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
- return -1;
- }
-
- /* ignoring the element if other than textnode/attribute */
- if (cx_if_not_text_node(instance_node->nodeTab[0])) {
- WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return only "
- "text node "
- "which is not the case. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
- return -1;
- }
- } /* if (xpath->instance != NULL) */
-
- if (xpath->instance_prefix != NULL) {
- if (instance_node != NULL) {
- char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+ if (xpath->instance_prefix != NULL)
snprintf(vl->type_instance, sizeof(vl->type_instance), "%s%s",
xpath->instance_prefix, node_value);
- sfree(node_value);
- } else
- sstrncpy(vl->type_instance, xpath->instance_prefix,
- sizeof(vl->type_instance));
- } else {
- /* If instance_prefix and instance_node are NULL, then
- * don't set the type_instance */
- if (instance_node != NULL) {
- char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+ else
sstrncpy(vl->type_instance, node_value, sizeof(vl->type_instance));
- sfree(node_value);
- }
- }
- /* Free `instance_node_obj' this late, because `instance_node' points to
- * somewhere inside this structure. */
- xmlXPathFreeObject(instance_node_obj);
+ xmlFree(node_value);
+ } else if (xpath->instance_prefix != NULL)
+ sstrncpy(vl->type_instance, xpath->instance_prefix,
+ sizeof(vl->type_instance));
+
+ /* Handle plugin instance */
+ if (xpath->plugin_instance_from != NULL) {
+ char *node_value = cx_get_text_node_value(
+ xpath_ctx, xpath->plugin_instance_from, "PluginInstanceFrom");
+
+ if (node_value == NULL)
+ return -1;
+
+ sstrncpy(vl->plugin_instance, node_value, sizeof(vl->plugin_instance));
+ xmlFree(node_value);
+ }
return 0;
} /* }}} int cx_handle_instance_xpath */
-static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */
- char const *host, xmlXPathContextPtr xpath_ctx,
- const data_set_t *ds, char *base_xpath,
- cx_xpath_t *xpath) {
- int total_nodes;
+static int cx_handle_xpath(const cx_t *db, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_xpath_t *xpath) {
- xmlXPathObjectPtr base_node_obj = NULL;
- xmlNodeSetPtr base_nodes = NULL;
-
- value_list_t vl = VALUE_LIST_INIT;
+ const data_set_t *ds = plugin_get_ds(xpath->type);
+ if (cx_check_type(ds, xpath) != 0)
+ return -1;
- base_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST base_xpath);
+ xmlXPathObjectPtr base_node_obj = cx_evaluate_xpath(xpath_ctx, xpath->path);
if (base_node_obj == NULL)
return -1; /* error is logged already */
- base_nodes = base_node_obj->nodesetval;
- total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+ xmlNodeSetPtr base_nodes = base_node_obj->nodesetval;
+ int total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
if (total_nodes == 0) {
ERROR("curl_xml plugin: "
"xpath expression \"%s\" doesn't match any of the nodes. "
"Skipping the xpath block...",
- base_xpath);
+ xpath->path);
xmlXPathFreeObject(base_node_obj);
return -1;
}
/* If base_xpath returned multiple results, then */
- /* Instance in the xpath block is required */
- if (total_nodes > 1 && xpath->instance == NULL) {
+ /* InstanceFrom or PluginInstanceFrom in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL &&
+ xpath->plugin_instance_from == NULL) {
ERROR("curl_xml plugin: "
- "InstanceFrom is must in xpath block since the base xpath expression "
- "\"%s\" "
+ "InstanceFrom or PluginInstanceFrom is must in xpath block "
+ "since the base xpath expression \"%s\" "
"returned multiple results. Skipping the xpath block...",
- base_xpath);
+ xpath->path);
+ xmlXPathFreeObject(base_node_obj);
return -1;
}
+ value_list_t vl = VALUE_LIST_INIT;
+
/* set the values for the value_list */
vl.values_len = ds->ds_num;
sstrncpy(vl.type, xpath->type, sizeof(vl.type));
- sstrncpy(vl.plugin, "curl_xml", sizeof(vl.plugin));
- sstrncpy(vl.host, host, sizeof(vl.host));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+ sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_xml",
+ sizeof(vl.plugin));
+ sstrncpy(vl.host, cx_host(db), sizeof(vl.host));
for (int i = 0; i < total_nodes; i++) {
- int status;
-
xpath_ctx->node = base_nodes->nodeTab[i];
- status = cx_handle_instance_xpath(xpath_ctx, xpath, &vl,
- /* is_table = */ (total_nodes > 1));
- if (status != 0)
+ if (db->instance != NULL)
+ sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
+
+ if (cx_handle_instance_xpath(xpath_ctx, xpath, &vl) != 0)
continue; /* An error has already been reported. */
- status = cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl);
- if (status != 0)
+ if (cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl) != 0)
continue; /* An error has been logged. */
} /* for (i = 0; i < total_nodes; i++) */
xmlXPathFreeObject(base_node_obj);
return 0;
-} /* }}} cx_handle_base_xpath */
+} /* }}} cx_handle_xpath */
-static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
- xmlXPathContextPtr xpath_ctx, cx_t *db) {
- llentry_t *le;
- const data_set_t *ds;
- cx_xpath_t *xpath;
+static int cx_handle_parsed_xml(cx_t *db, xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx) {
int status = -1;
- le = llist_head(db->list);
+ llentry_t *le = llist_head(db->xpath_list);
while (le != NULL) {
- /* get the ds */
- xpath = (cx_xpath_t *)le->value;
- ds = plugin_get_ds(xpath->type);
+ cx_xpath_t *xpath = (cx_xpath_t *)le->value;
- if ((cx_check_type(ds, xpath) == 0) &&
- (cx_handle_base_xpath(db->instance, cx_host(db), xpath_ctx, ds, le->key,
- xpath) == 0))
+ if (cx_handle_xpath(db, xpath_ctx, xpath) == 0)
status = 0; /* we got atleast one success */
le = le->next;
return status;
} /* }}} cx_handle_parsed_xml */
-static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */
+static int cx_parse_xml(cx_t *db, char *xml) /* {{{ */
{
- int status;
- xmlDocPtr doc;
- xmlXPathContextPtr xpath_ctx;
-
/* Load the XML */
- doc = xmlParseDoc(xml);
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
if (doc == NULL) {
ERROR("curl_xml plugin: Failed to parse the xml document - %s", xml);
return -1;
}
- xpath_ctx = xmlXPathNewContext(doc);
+ xmlXPathContextPtr xpath_ctx = xmlXPathNewContext(doc);
if (xpath_ctx == NULL) {
ERROR("curl_xml plugin: Failed to create the xml context");
xmlFreeDoc(doc);
for (size_t i = 0; i < db->namespaces_num; i++) {
cx_namespace_t const *ns = db->namespaces + i;
- status =
+ int status =
xmlXPathRegisterNs(xpath_ctx, BAD_CAST ns->prefix, BAD_CAST ns->url);
if (status != 0) {
ERROR("curl_xml plugin: "
}
}
- status = cx_handle_parsed_xml(doc, xpath_ctx, db);
+ int status = cx_handle_parsed_xml(db, doc, xpath_ctx);
/* Cleanup */
xmlXPathFreeContext(xpath_ctx);
xmlFreeDoc(doc);
return status;
-} /* }}} cx_parse_stats_xml */
+} /* }}} cx_parse_xml */
-static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */
+static int cx_read(user_data_t *ud) /* {{{ */
{
- int status;
+ if ((ud == NULL) || (ud->data == NULL)) {
+ ERROR("curl_xml plugin: cx_read: Invalid user data.");
+ return -1;
+ }
+
long rc;
- char *ptr;
char *url;
+ cx_t *db = (cx_t *)ud->data;
db->buffer_fill = 0;
curl_easy_setopt(db->curl, CURLOPT_URL, db->url);
- status = curl_easy_perform(curl);
+ int status = curl_easy_perform(db->curl);
if (status != CURLE_OK) {
ERROR("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
status, db->curl_errbuf, db->url);
curl_stats_dispatch(db->stats, db->curl, cx_host(db), "curl_xml",
db->instance);
- curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+ curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
/* The response code is zero if a non-HTTP transport was used. */
if ((rc != 0) && (rc != 200)) {
return -1;
}
- ptr = db->buffer;
-
- status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ status = cx_parse_xml(db, db->buffer);
db->buffer_fill = 0;
return status;
-} /* }}} int cx_curl_perform */
-
-static int cx_read(user_data_t *ud) /* {{{ */
-{
- cx_t *db;
-
- if ((ud == NULL) || (ud->data == NULL)) {
- ERROR("curl_xml plugin: cx_read: Invalid user data.");
- return -1;
- }
-
- db = (cx_t *)ud->data;
-
- return cx_curl_perform(db, db->curl);
} /* }}} int cx_read */
/* Configuration handling functions {{{ */
static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
{
- cx_xpath_t *xpath;
- char *name;
- llentry_t *le;
- int status;
-
- xpath = calloc(1, sizeof(*xpath));
+ cx_xpath_t *xpath = calloc(1, sizeof(*xpath));
if (xpath == NULL) {
ERROR("curl_xml plugin: calloc failed.");
return -1;
}
- status = cf_util_get_string(ci, &xpath->path);
+ int status = cf_util_get_string(ci, &xpath->path);
if (status != 0) {
cx_xpath_free(xpath);
return status;
status = cf_util_get_string(child, &xpath->instance_prefix);
else if (strcasecmp("InstanceFrom", child->key) == 0)
status = cf_util_get_string(child, &xpath->instance);
+ else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
+ status = cf_util_get_string(child, &xpath->plugin_instance_from);
else if (strcasecmp("ValuesFrom", child->key) == 0)
status = cx_config_add_values("ValuesFrom", xpath, child);
else {
return -1;
}
- if (db->list == NULL) {
- db->list = llist_create();
- if (db->list == NULL) {
- ERROR("curl_xml plugin: list creation failed.");
- cx_xpath_free(xpath);
- return -1;
- }
- }
-
- name = strdup(xpath->path);
- if (name == NULL) {
- ERROR("curl_xml plugin: strdup failed.");
+ if (xpath->values_len == 0) {
+ WARNING("curl_xml plugin: `ValuesFrom' missing in `xpath' block.");
cx_xpath_free(xpath);
return -1;
}
- le = llentry_create(name, xpath);
+ llentry_t *le = llentry_create(xpath->path, xpath);
if (le == NULL) {
ERROR("curl_xml plugin: llentry_create failed.");
cx_xpath_free(xpath);
- sfree(name);
return -1;
}
- llist_append(db->list, le);
+ llist_append(db->xpath_list, le);
return 0;
} /* }}} int cx_config_add_xpath */
static int cx_config_add_namespace(cx_t *db, /* {{{ */
oconfig_item_t *ci) {
- cx_namespace_t *ns;
if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
(ci->values[1].type != OCONFIG_TYPE_STRING)) {
return EINVAL;
}
- ns = realloc(db->namespaces,
- sizeof(*db->namespaces) * (db->namespaces_num + 1));
+ cx_namespace_t *ns = realloc(
+ db->namespaces, sizeof(*db->namespaces) * (db->namespaces_num + 1));
if (ns == NULL) {
ERROR("curl_xml plugin: realloc failed.");
return ENOMEM;
static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
{
- cx_t *db;
- int status = 0;
-
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
WARNING("curl_xml plugin: The `URL' block "
"needs exactly one string argument.");
return -1;
}
- db = calloc(1, sizeof(*db));
+ cx_t *db = calloc(1, sizeof(*db));
if (db == NULL) {
ERROR("curl_xml plugin: calloc failed.");
return -1;
}
- db->timeout = -1;
+ db->instance = strdup("default");
+ if (db->instance == NULL) {
+ ERROR("curl_xml plugin: strdup failed.");
+ sfree(db);
+ return -1;
+ }
- if (strcasecmp("URL", ci->key) == 0) {
- status = cf_util_get_string(ci, &db->url);
- if (status != 0) {
- sfree(db);
- return status;
- }
- } else {
- ERROR("curl_xml plugin: cx_config: "
- "Invalid key: %s",
- ci->key);
- cx_free(db);
+ db->xpath_list = llist_create();
+ if (db->xpath_list == NULL) {
+ ERROR("curl_xml plugin: list creation failed.");
+ sfree(db->instance);
+ sfree(db);
return -1;
}
+ db->timeout = -1;
+
+ int status = cf_util_get_string(ci, &db->url);
+ if (status != 0) {
+ llist_destroy(db->xpath_list);
+ sfree(db->instance);
+ sfree(db);
+ return status;
+ }
+
/* Fill the `cx_t' structure.. */
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("Instance", child->key) == 0)
status = cf_util_get_string(child, &db->instance);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &db->plugin_name);
else if (strcasecmp("Host", child->key) == 0)
status = cf_util_get_string(child, &db->host);
else if (strcasecmp("User", child->key) == 0)
break;
}
- if (status == 0) {
- if (db->list == NULL) {
- WARNING("curl_xml plugin: No (valid) `Key' block "
- "within `URL' block `%s'.",
- db->url);
- status = -1;
- }
- if (status == 0)
- status = cx_init_curl(db);
+ if (status != 0) {
+ cx_free(db);
+ return status;
}
- /* If all went well, register this database for reading */
- if (status == 0) {
- char *cb_name;
-
- if (db->instance == NULL)
- db->instance = strdup("default");
-
- DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
-
- cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+ if (llist_size(db->xpath_list) == 0) {
+ WARNING("curl_xml plugin: No `xpath' block within `URL' block `%s'.",
+ db->url);
+ cx_free(db);
+ return -1;
+ }
- plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
- /* interval = */ 0,
- &(user_data_t){
- .data = db, .free_func = cx_free,
- });
- sfree(cb_name);
- } else {
+ if (cx_init_curl(db) != 0) {
cx_free(db);
return -1;
}
+ /* If all went well, register this database for reading */
+ DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
+
+ char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+
+ plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = db, .free_func = cx_free,
+ });
+ sfree(cb_name);
return 0;
} /* }}} int cx_config_add_url */
static int cx_config(oconfig_item_t *ci) /* {{{ */
{
- int success;
- int errors;
- int status;
-
- success = 0;
- errors = 0;
+ int success = 0;
+ int errors = 0;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("URL", child->key) == 0) {
- status = cx_config_add_url(child);
- if (status == 0)
+ if (cx_config_add_url(child) == 0)
success++;
else
errors++;
#define COLLECTD_LOCALE "C"
#endif
-/*
- * Global variables
- */
-char hostname_g[DATA_MAX_NAME_LEN];
-cdtime_t interval_g;
-int timeout_g;
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
static int loop = 0;
static void *do_flush(void __attribute__((unused)) * arg) {
struct addrinfo *ai_list;
int status;
+ long hostname_len = sysconf(_SC_HOST_NAME_MAX);
+ if (hostname_len == -1) {
+ hostname_len = NI_MAXHOST;
+ }
+ char hostname[hostname_len];
+
str = global_option_get("Hostname");
if ((str != NULL) && (str[0] != 0)) {
- sstrncpy(hostname_g, str, sizeof(hostname_g));
+ hostname_set(str);
return 0;
}
- if (gethostname(hostname_g, sizeof(hostname_g)) != 0) {
+ if (gethostname(hostname, hostname_len) != 0) {
fprintf(stderr, "`gethostname' failed and no "
"hostname was configured.\n");
return -1;
struct addrinfo ai_hints = {.ai_flags = AI_CANONNAME};
- status = getaddrinfo(hostname_g, NULL, &ai_hints, &ai_list);
+ status = getaddrinfo(hostname, NULL, &ai_hints, &ai_list);
if (status != 0) {
ERROR("Looking up \"%s\" failed. You have set the "
"\"FQDNLookup\" option, but I cannot resolve "
"my hostname to a fully qualified domain "
"name. Please fix the network "
"configuration.",
- hostname_g);
+ hostname);
return -1;
}
if (ai_ptr->ai_canonname == NULL)
continue;
- sstrncpy(hostname_g, ai_ptr->ai_canonname, sizeof(hostname_g));
+ hostname_set(ai_ptr->ai_canonname);
break;
}
}
#endif /* KERNEL_LINUX */
-int main(int argc, char **argv) {
- const char *configfile = CONFIGFILE;
- int test_config = 0;
- int test_readall = 0;
- const char *basedir;
- _Bool opt_create_basedir = 1;
-#if COLLECT_DAEMON
- pid_t pid;
- int daemonize = 1;
-#endif
- int exit_status = 0;
+struct cmdline_config {
+ _Bool test_config;
+ _Bool test_readall;
+ _Bool create_basedir;
+ const char *configfile;
+ _Bool daemonize;
+};
+void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
/* read options */
while (1) {
int c;
-
- c = getopt(argc, argv, "BhtTC:"
+ c = getopt(argc, argv, "htTC:"
#if COLLECT_DAEMON
"fP:"
#endif
switch (c) {
case 'B':
- opt_create_basedir = 0;
+ config->create_basedir = 0;
break;
case 'C':
- configfile = optarg;
+ config->configfile = optarg;
break;
case 't':
- test_config = 1;
+ config->test_config = 1;
break;
case 'T':
- test_readall = 1;
+ config->test_readall = 1;
global_option_set("ReadThreads", "-1", 1);
#if COLLECT_DAEMON
- daemonize = 0;
+ config->daemonize = 0;
#endif /* COLLECT_DAEMON */
break;
#if COLLECT_DAEMON
global_option_set("PIDFile", optarg, 1);
break;
case 'f':
- daemonize = 0;
+ config->daemonize = 0;
break;
#endif /* COLLECT_DAEMON */
case 'h':
exit_usage(1);
} /* switch (c) */
} /* while (1) */
+}
- if (optind < argc)
- exit_usage(1);
-
- plugin_init_ctx();
-
+int configure_collectd(struct cmdline_config *config) {
+ const char *basedir;
/*
* Read options from the config file, the environment and the command
* line (in that order, with later options overwriting previous ones in
* general).
* Also, this will automatically load modules.
*/
- if (cf_read(configfile)) {
+ if (cf_read(config->configfile)) {
fprintf(stderr, "Error: Reading the config file failed!\n"
"Read the logs for details.\n");
return 1;
fprintf(stderr,
"Don't have a basedir to use. This should not happen. Ever.");
return 1;
- } else if (change_basedir(basedir, opt_create_basedir)) {
+ } else if (change_basedir(basedir, config->create_basedir)) {
fprintf(stderr, "Error: Unable to change to directory `%s'.\n", basedir);
return 1;
}
/*
- * Set global variables or, if that failes, exit. We cannot run with
+ * Set global variables or, if that fails, exit. We cannot run with
* them being uninitialized. If nothing is configured, then defaults
* are being used. So this means that the user has actually done
* something wrong.
*/
if (init_global_variables() != 0)
- exit(EXIT_FAILURE);
+ return 1;
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+#if COLLECT_DAEMON
+ pid_t pid;
+#endif
+ int exit_status = 0;
- if (test_config)
+ struct cmdline_config config = {
+ .daemonize = 1, .create_basedir = 1, .configfile = CONFIGFILE,
+ };
+
+ read_cmdline(argc, argv, &config);
+
+ if (config.test_config)
return 0;
+ if (optind < argc)
+ exit_usage(1);
+
+ plugin_init_ctx();
+
+ int status;
+ if ((status = configure_collectd(&config)) != 0)
+ exit(EXIT_FAILURE);
+
#if COLLECT_DAEMON
/*
* fork off child
* Only daemonize if we're not being supervised
* by upstart or systemd (when using Linux).
*/
- if (daemonize
+ if (config.daemonize
#ifdef KERNEL_LINUX
&& notify_upstart() == 0 && notify_systemd() == 0
#endif
status);
return 1;
}
- } /* if (daemonize) */
+ } /* if (config.daemonize) */
#endif /* COLLECT_DAEMON */
struct sigaction sig_pipe_action = {.sa_handler = SIG_IGN};
exit_status = 1;
}
- if (test_readall) {
+ if (config.test_readall) {
if (plugin_read_all_once() != 0) {
ERROR("Error: one or more plugin read callbacks failed.");
exit_status = 1;
}
#if COLLECT_DAEMON
- if (daemonize)
+ if (config.daemonize)
pidfile_remove();
#endif /* COLLECT_DAEMON */
#include <sys/param.h>
#endif
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "collectd"
#endif
#define GAUGE_FORMAT "%.15g"
#endif
-/* Type for time as used by "utils_time.h" */
-typedef uint64_t cdtime_t;
-
-extern char hostname_g[];
-extern cdtime_t interval_g;
-extern int timeout_g;
+#include "globals.h"
#endif /* COLLECTD_H */
}
#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
return 0;
}
- temp =
- realloc(dst->children, sizeof(oconfig_item_t) *
- (dst->children_num + src->children_num - 1));
+ temp = realloc(dst->children,
+ sizeof(oconfig_item_t) *
+ (dst->children_num + src->children_num - 1));
if (temp == NULL) {
ERROR("configfile: realloc failed.");
return -1;
if ((src == NULL) || (src->children_num == 0))
return 0;
- temp = realloc(dst->children, sizeof(oconfig_item_t) *
- (dst->children_num + src->children_num));
+ temp =
+ realloc(dst->children,
+ sizeof(oconfig_item_t) * (dst->children_num + src->children_num));
if (temp == NULL) {
ERROR("configfile: realloc failed.");
return -1;
return NULL;
}
- return (cf_global_options[i].value != NULL) ? cf_global_options[i].value : cf_global_options[i].def;
+ return (cf_global_options[i].value != NULL) ? cf_global_options[i].value
+ : cf_global_options[i].def;
} /* char *global_option_get */
long global_option_get_long(const char *option, long default_value) {
}
return ret;
+
} /* int cf_read */
/* Assures the config option is a string, duplicates it and returns the copy in
--- /dev/null
+/**
+ * collectd - src/globals.c
+ * Copyright (C) 2017 Google LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ **/
+
+#include "common.h"
+#include "globals.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+/*
+ * Global variables
+ */
+char *hostname_g;
+cdtime_t interval_g;
+int timeout_g;
+#if HAVE_KSTAT_H
+kstat_ctl_t *kc;
+#endif
+
+void hostname_set(char const *hostname) {
+ char *h = strdup(hostname);
+ if (h == NULL)
+ return;
+
+ sfree(hostname_g);
+ hostname_g = h;
+}
--- /dev/null
+/**
+ * collectd - src/globals.h
+ * Copyright (C) 2017 Google LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ **/
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#include <inttypes.h>
+
+#ifndef DATA_MAX_NAME_LEN
+#define DATA_MAX_NAME_LEN 128
+#endif
+
+/* Type for time as used by "utils_time.h" */
+typedef uint64_t cdtime_t;
+
+/* hostname_set updates hostname_g */
+void hostname_set(char const *hostname);
+
+extern char *hostname_g;
+extern cdtime_t interval_g;
+extern int pidfile_from_cli;
+extern int timeout_g;
+#endif /* GLOBALS_H */
#include <pthread.h>
-#ifndef DATA_MAX_NAME_LEN
-#define DATA_MAX_NAME_LEN 128
-#endif
-
#define DS_TYPE_COUNTER 0
#define DS_TYPE_GAUGE 1
#define DS_TYPE_DERIVE 2
#define DS_TYPE_ABSOLUTE 3
#define DS_TYPE_TO_STRING(t) \
- (t == DS_TYPE_COUNTER) ? "counter" : (t == DS_TYPE_GAUGE) \
- ? "gauge" \
- : (t == DS_TYPE_DERIVE) \
- ? "derive" \
- : (t == DS_TYPE_ABSOLUTE) \
- ? "absolute" \
- : "unknown"
+ (t == DS_TYPE_COUNTER) \
+ ? "counter" \
+ : (t == DS_TYPE_GAUGE) \
+ ? "gauge" \
+ : (t == DS_TYPE_DERIVE) \
+ ? "derive" \
+ : (t == DS_TYPE_ABSOLUTE) ? "absolute" : "unknown"
#ifndef LOG_ERR
#define LOG_ERR 3
kstat_ctl_t *kc = NULL;
#endif /* HAVE_LIBKSTAT */
-char hostname_g[] = "example.com";
+char *hostname_g = "example.com";
void plugin_set_dir(const char *dir) { /* nop */
}
#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];
+ char type_instance[DATA_MAX_NAME_LEN];
int sensor_not_present;
c_ipmi_sensor_list_t *next;
+ c_ipmi_instance_t *instance;
+ unsigned int use;
};
+struct c_ipmi_db_type_map_s {
+ enum ipmi_unit_type_e type;
+ const char *type_name;
+};
+typedef struct c_ipmi_db_type_map_s c_ipmi_db_type_map_t;
+
/*
* 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,
+ sstrncpy(n.type_instance, list_item->type_instance,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s not present",
} 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,
+ sstrncpy(n.type_instance, list_item->type_instance,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s present",
}
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));
+ sstrncpy(vl.type_instance, list_item->type_instance,
+ 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 const char *sensor_unit_to_type(ipmi_sensor_t *sensor) {
+ static const c_ipmi_db_type_map_t ipmi_db_type_map[] = {
+ {IPMI_UNIT_TYPE_WATTS, "power"}, {IPMI_UNIT_TYPE_CFM, "flow"}};
+
+ /* check the modifier and rate of the sensor value */
+ if ((ipmi_sensor_get_modifier_unit_use(sensor) != IPMI_MODIFIER_UNIT_NONE) ||
+ (ipmi_sensor_get_rate_unit(sensor) != IPMI_RATE_UNIT_NONE))
+ return NULL;
+
+ /* find the db type by using sensor base unit type */
+ enum ipmi_unit_type_e ipmi_type = ipmi_sensor_get_base_unit(sensor);
+ for (int i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
+ if (ipmi_db_type_map[i].type == ipmi_type)
+ return ipmi_db_type_map[i].type_name;
+
+ return NULL;
+} /* const char* sensor_unit_to_type */
+
+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 isn'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";
type = "fanspeed";
break;
+ case IPMI_SENSOR_TYPE_MEMORY:
+ type = "memory";
+ break;
+
default: {
- const char *sensor_type_str;
+ /* try to get collectd DB type based on sensor base unit type */
+ if ((type = sensor_unit_to_type(sensor)) != NULL)
+ break;
- sensor_type_str = ipmi_sensor_get_sensor_type_string(sensor);
- INFO("ipmi plugin: sensor_list_add: Ignore sensor %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);
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because I don't know how to handle its units (%#x, %#x, %#x). "
+ "Sensor type: (%#x, %s). If you need this sensor, please file "
+ "a bug report at http://collectd.org/.",
+ sensor_name_ptr, st->name, ipmi_sensor_get_base_unit(sensor),
+ ipmi_sensor_get_modifier_unit(sensor),
+ ipmi_sensor_get_rate_unit(sensor), 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;
+
+ /* if sensor provides the percentage value, use "percent" collectd type
+ and add the `percent` to the type instance of the reported value */
+ if (ipmi_sensor_get_percentage(sensor)) {
+ snprintf(list_item->type_instance, sizeof(list_item->type_instance),
+ "percent-%s", sensor_name_ptr);
+ type = "percent";
+ } else {
+ /* use type instance as a name of the sensor */
+ sstrncpy(list_item->type_instance, sensor_name_ptr,
+ sizeof(list_item->type_instance));
+ }
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_instance, list_item->type_instance,
+ sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s added",
list_item->sensor_name);
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_instance, list_item->type_instance,
+ sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s removed",
list_item->sensor_name);
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;
+
+ 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;
- c_ipmi_active = 1;
+ 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;
{"Collectd::NOTIF_WARNING", NOTIF_WARNING},
{"Collectd::NOTIF_OKAY", NOTIF_OKAY},
{"", 0}};
-
-struct {
- char name[64];
- char *var;
-} g_strings[] = {{"Collectd::hostname_g", hostname_g}, {"", NULL}};
-
/*
* Helper functions for data type conversion.
*/
/* Lock the base thread to avoid race conditions with c_ithread_create().
* See https://github.com/collectd/collectd/issues/9 and
* https://github.com/collectd/collectd/issues/1706 for details.
- */
+ */
assert(aTHX == perl_threads->head->interp);
pthread_mutex_lock(&perl_threads->mutex);
/* Lock the base thread if this is not called from one of the read threads
* to avoid race conditions with c_ithread_create(). See
* https://github.com/collectd/collectd/issues/9 for details.
- */
+ */
if (aTHX == perl_threads->head->interp)
pthread_mutex_lock(&perl_threads->mutex);
return 0;
} /* static int g_interval_set (pTHX_ SV *, MAGIC *) */
-static MGVTBL g_pv_vtbl = {g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL
+static MGVTBL g_pv_vtbl = {g_pv_get,
+ g_pv_set,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
#if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
,
NULL
#endif
};
-static MGVTBL g_interval_vtbl = {g_interval_get, g_interval_set, NULL, NULL,
- NULL, NULL, NULL
+static MGVTBL g_interval_vtbl = {g_interval_get,
+ g_interval_set,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
#if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
,
NULL
* accessing any such variable (this is basically the same as using
* tie() in Perl) */
/* global strings */
+ struct {
+ char name[64];
+ char *var;
+ } g_strings[] = {{"Collectd::hostname_g", hostname_g}, {"", NULL}};
+
for (int i = 0; '\0' != g_strings[i].name[0]; ++i) {
tmp = get_sv(g_strings[i].name, 1);
sv_magicext(tmp, NULL, PERL_MAGIC_ext, &g_pv_vtbl, g_strings[i].var, 0);
C_PSQL_PAR_APPEND(buf, buf_len, "sslmode", db->sslmode);
C_PSQL_PAR_APPEND(buf, buf_len, "krbsrvname", db->krbsrvname);
C_PSQL_PAR_APPEND(buf, buf_len, "service", db->service);
+ C_PSQL_PAR_APPEND(buf, buf_len, "application_name", "collectd_postgresql");
db->conn = PQconnectdb(conninfo);
db->proto_version = PQprotocolVersion(db->conn);
status = udb_query_prepare_result(
q, prep_area, host,
- (db->plugin_name != NULL) ? db->plugin_name : "postgresql",
- db->instance, column_names, (size_t)column_num, db->interval);
+ (db->plugin_name != NULL) ? db->plugin_name : "postgresql", db->instance,
+ column_names, (size_t)column_num, db->interval);
if (0 != status) {
log_err("udb_query_prepare_result failed with status %i.", status);
cf_util_get_string(c, &db->password);
else if (0 == strcasecmp(c->key, "Instance"))
cf_util_get_string(c, &db->instance);
- else if (0 == strcasecmp (c->key, "Plugin"))
- cf_util_get_string (c, &db->plugin_name);
+ else if (0 == strcasecmp(c->key, "Plugin"))
+ cf_util_get_string(c, &db->plugin_name);
else if (0 == strcasecmp(c->key, "SSLMode"))
cf_util_get_string(c, &db->sslmode);
else if (0 == strcasecmp(c->key, "KRBSrvName"))
"The callback function will be called with no parameters except for\n"
" data if it was supplied.";
+static char CollectdError_doc[] =
+ "Basic exception for collectd Python scripts.\n"
+ "\n"
+ "Throwing this exception will not cause a stacktrace to be logged, \n"
+ "even if LogTraces is enabled in the config.";
+
static pthread_t main_thread;
static PyOS_sighandler_t python_sigint_handler;
static _Bool do_interactive = 0;
static PyThreadState *state;
-static PyObject *sys_path, *cpy_format_exception;
+static PyObject *sys_path, *cpy_format_exception, *CollectdError;
static cpy_callback_t *cpy_config_callbacks;
static cpy_callback_t *cpy_init_callbacks;
}
void cpy_log_exception(const char *context) {
- int l = 0;
+ int l = 0, collectd_error;
const char *typename = NULL, *message = NULL;
PyObject *type, *value, *traceback, *tn, *m, *list;
PyErr_NormalizeException(&type, &value, &traceback);
if (type == NULL)
return;
+ collectd_error = PyErr_GivenExceptionMatches(value, CollectdError);
tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
m = PyObject_Str(value); /* New reference. */
if (tn != NULL)
typename = "NamelessException";
if (message == NULL)
message = "N/A";
- Py_BEGIN_ALLOW_THREADS ERROR("Unhandled python exception in %s: %s: %s",
- context, typename, message);
- Py_END_ALLOW_THREADS Py_XDECREF(tn);
+ Py_BEGIN_ALLOW_THREADS;
+ if (collectd_error) {
+ WARNING("%s in %s: %s", typename, context, message);
+ } else {
+ ERROR("Unhandled python exception in %s: %s: %s", context, typename,
+ message);
+ }
+ Py_END_ALLOW_THREADS;
+ Py_XDECREF(tn);
Py_XDECREF(m);
- if (!cpy_format_exception || !traceback) {
+ if (!cpy_format_exception || !traceback || collectd_error) {
PyErr_Clear();
Py_DECREF(type);
Py_XDECREF(value);
if (cpy[strlen(cpy) - 1] == '\n')
cpy[strlen(cpy) - 1] = 0;
- Py_BEGIN_ALLOW_THREADS ERROR("%s", cpy);
- Py_END_ALLOW_THREADS
+ Py_BEGIN_ALLOW_THREADS;
+ ERROR("%s", cpy);
+ Py_END_ALLOW_THREADS;
- free(cpy);
+ free(cpy);
}
Py_XDECREF(list);
PyList_SetItem(
list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
} else {
- Py_BEGIN_ALLOW_THREADS ERROR("cpy_write_callback: Unknown value type %d.",
- ds->ds[i].type);
- Py_END_ALLOW_THREADS Py_DECREF(list);
+ Py_BEGIN_ALLOW_THREADS;
+ ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
+ Py_END_ALLOW_THREADS;
+ Py_DECREF(list);
CPY_RETURN_FROM_THREADS 0;
}
if (PyErr_Occurred() != NULL) {
}
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);
if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
&timeout, NULL, &identifier) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_flush(plugin, timeout, identifier);
- Py_END_ALLOW_THREADS PyMem_Free(plugin);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_flush(plugin, timeout, identifier);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(plugin);
PyMem_Free(identifier);
Py_RETURN_NONE;
}
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);
char *text;
if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_log(LOG_ERR, "%s", text);
- Py_END_ALLOW_THREADS PyMem_Free(text);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_log(LOG_ERR, "%s", text);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(text);
Py_RETURN_NONE;
}
char *text;
if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_log(LOG_WARNING, "%s", text);
- Py_END_ALLOW_THREADS PyMem_Free(text);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_log(LOG_WARNING, "%s", text);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(text);
Py_RETURN_NONE;
}
char *text;
if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_log(LOG_NOTICE, "%s", text);
- Py_END_ALLOW_THREADS PyMem_Free(text);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_log(LOG_NOTICE, "%s", text);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(text);
Py_RETURN_NONE;
}
char *text;
if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text);
- Py_END_ALLOW_THREADS PyMem_Free(text);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_log(LOG_INFO, "%s", text);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(text);
Py_RETURN_NONE;
}
char *text;
if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
return NULL;
- Py_BEGIN_ALLOW_THREADS plugin_log(LOG_DEBUG, "%s", text);
- Py_END_ALLOW_THREADS PyMem_Free(text);
+ Py_BEGIN_ALLOW_THREADS;
+ plugin_log(LOG_DEBUG, "%s", text);
+ Py_END_ALLOW_THREADS;
+ PyMem_Free(text);
#endif
Py_RETURN_NONE;
}
}
PyErr_Print();
- Py_BEGIN_ALLOW_THREADS cpy_unregister_list(&cpy_config_callbacks);
+ Py_BEGIN_ALLOW_THREADS;
+ cpy_unregister_list(&cpy_config_callbacks);
cpy_unregister_list(&cpy_init_callbacks);
cpy_unregister_list(&cpy_shutdown_callbacks);
cpy_shutdown_triggered = 1;
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS;
- if (!cpy_num_callbacks) {
+ if (!cpy_num_callbacks) {
Py_Finalize();
return 0;
}
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));
static int cpy_init_python(void) {
PyOS_sighandler_t cur_sig;
- PyObject *sys;
+ PyObject *sys, *errordict;
PyObject *module;
#ifdef IS_PY3K
PyType_Ready(&SignedType);
UnsignedType.tp_base = &PyLong_Type;
PyType_Ready(&UnsignedType);
+ errordict = PyDict_New();
+ PyDict_SetItemString(
+ errordict, "__doc__",
+ cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */
+ CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict);
sys = PyImport_ImportModule("sys"); /* New reference. */
if (sys == NULL) {
cpy_log_exception("python initialization");
(void *)&SignedType); /* Steals a reference. */
PyModule_AddObject(module, "Unsigned",
(void *)&UnsignedType); /* Steals a reference. */
+ Py_XINCREF(CollectdError);
+ PyModule_AddObject(module, "CollectdError",
+ CollectdError); /* Steals a reference. */
PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
#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[] =
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
Signed_doc /* tp_doc */
};
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
Unsigned_doc /* tp_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) {
"workspace_overflow", val);
#if HAVE_VARNISH_V4
else if (strcmp(name, "exp_mailed") == 0)
- return varnish_submit_derive(conf->instance, "struct", "objects",
+ return varnish_submit_gauge(conf->instance, "struct", "objects",
"exp_mailed", val);
else if (strcmp(name, "exp_received") == 0)
- return varnish_submit_derive(conf->instance, "struct", "objects",
+ return varnish_submit_gauge(conf->instance, "struct", "objects",
"exp_received", val);
#endif
}
return varnish_submit_derive(conf->instance, "ban", "total_operations",
"obj_killed", val);
else if (strcmp(name, "bans_persisted_bytes") == 0)
- return varnish_submit_gauge(conf->instance, "ban", "total_bytes",
+ return varnish_submit_derive(conf->instance, "ban", "total_bytes",
"persisted_bytes", val);
else if (strcmp(name, "bans_persisted_fragmentation") == 0)
- return varnish_submit_gauge(conf->instance, "ban", "total_bytes",
+ return varnish_submit_derive(conf->instance, "ban", "total_bytes",
"persisted_fragmentation", val);
else if (strcmp(name, "bans_tests_tested") == 0)
return varnish_submit_derive(conf->instance, "ban", "total_operations",
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);
return varnish_submit_derive(conf->instance, "workers", "total_threads",
"dropped", val);
else if (strcmp(name, "thread_queue_len") == 0)
- return varnish_submit_derive(conf->instance, "workers", "queue_length",
+ return varnish_submit_gauge(conf->instance, "workers", "queue_length",
"threads", val);
else if (strcmp(name, "n_wrk") == 0)
return varnish_submit_gauge(conf->instance, "workers", "threads",
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);
return varnish_submit_derive(conf->instance, "vbe", "connections",
"c_conns", val);
else if (strcmp(name, "req") == 0)
- return varnish_submit_gauge(conf->instance, "vbe", "http_requests",
+ return varnish_submit_derive(conf->instance, "vbe", "http_requests",
"b_reqs", 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_derive(conf->instance, category,
"total_bytes", "bytes_freed", val);
else if (strcmp(name, "g_alloc") == 0)
- return varnish_submit_gauge(conf->instance, category,
+ return varnish_submit_derive(conf->instance, category,
"total_operations", "alloc_outstanding", val);
else if (strcmp(name, "g_bytes") == 0)
return varnish_submit_gauge(conf->instance, category, "bytes",
if (conf->collect_lck) {
if (strcmp(name, "creat") == 0)
- return varnish_submit_derive(conf->instance, "lck", "objects",
+ return varnish_submit_gauge(conf->instance, "lck", "objects",
"created", val);
else if (strcmp(name, "destroy") == 0)
- return varnish_submit_derive(conf->instance, "lck", "objects",
+ return varnish_submit_gauge(conf->instance, "lck", "objects",
"destroyed", val);
else if (strcmp(name, "locks") == 0)
return varnish_submit_derive(conf->instance, "lck", "total_operations",
return varnish_submit_derive(conf->instance, "mempool",
"total_operations", "frees", val);
else if (strcmp(name, "recycle") == 0)
- return varnish_submit_derive(conf->instance, "mempool",
+ return varnish_submit_gauge(conf->instance, "mempool",
"objects", "recycled", val);
else if (strcmp(name, "timeout") == 0)
- return varnish_submit_derive(conf->instance, "mempool",
+ return varnish_submit_gauge(conf->instance, "mempool",
"objects", "timed_out", val);
else if (strcmp(name, "toosmall") == 0)
- return varnish_submit_derive(conf->instance, "mempool",
+ return varnish_submit_gauge(conf->instance, "mempool",
"objects", "too_small", val);
else if (strcmp(name, "surplus") == 0)
- return varnish_submit_derive(conf->instance, "mempool",
+ return varnish_submit_gauge(conf->instance, "mempool",
"objects", "surplus", val);
else if (strcmp(name, "randry") == 0)
- return varnish_submit_derive(conf->instance, "mempool",
+ 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
}
/* }}} */
+static void prom_logger(__attribute__((unused)) void *arg, char const *fmt,
+ va_list ap) {
+ /* {{{ */
+ char errbuf[1024];
+ vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
+
+ ERROR("write_prometheus plugin: %s", errbuf);
+} /* }}} prom_logger */
+
+#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 | MHD_USE_DEBUG, httpd_port,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, NULL,
+ MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_EXTERNAL_LOGGER, prom_logger,
+ NULL, 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 | MHD_USE_DEBUG, httpd_port,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, NULL,
+ MHD_OPTION_EXTERNAL_LOGGER, prom_logger, 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)));