From: Florian Forster Date: Fri, 6 Oct 2017 13:32:01 +0000 (+0200) Subject: Merge remote-tracking branch 'github/pr/2453' X-Git-Tag: collectd-5.8.0~54 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=48dce3b57e6acfdbbd0d5262f896dd07f4dd3e20;hp=736647c8a3d07ad6ab017ee284a437fb1394ff71 Merge remote-tracking branch 'github/pr/2453' --- diff --git a/ChangeLog b/ChangeLog index b8755d99..f990dd46 100644 --- a/ChangeLog +++ b/ChangeLog @@ -134,6 +134,77 @@ 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. diff --git a/contrib/curl_json/php-fpm.conf b/contrib/curl_json/php-fpm.conf new file mode 100644 index 00000000..34b8b67d --- /dev/null +++ b/contrib/curl_json/php-fpm.conf @@ -0,0 +1,27 @@ +# Example configuration for PHP-FPM + + + Plugin "phpfpm" + Instance "main" + + Type "total_requests" + Instance "accepted" + + + Type "total_requests" + Instance "slow" + + + Type "queue_length" + Instance "listen" + + + Type "vs_processes" + Instance "active" + + + Type "vs_processes" + Instance "total" + + + diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index 23cac9a0..7862663e 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -16,7 +16,7 @@ # # - fetch the desired collectd release file from https://collectd.org/files/ # and save it in your ~/rpmbuild/SOURCES/ directory (or build your own out of -# the git repository: ./build.sh && ./configure && make-dist-bz2) +# the git repository: ./build.sh && ./configure && make dist) # # - copy this file in your ~/rpmbuild/SPECS/ directory. Make sure the # "Version:" tag matches the version from the tarball. diff --git a/src/ceph.c b/src/ceph.c index c2284cb3..8bee8e88 100644 --- a/src/ceph.c +++ b/src/ceph.c @@ -418,7 +418,7 @@ static void ceph_daemon_free(struct ceph_daemon *d) { } /* 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) { diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 4db9f9f1..826b4866 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -466,6 +466,7 @@ # # # Host "my_host" +# #Plugin "stats" # Instance "some_instance" # User "collectd" # Password "thaiNg0I" @@ -480,6 +481,7 @@ # Type "magic_level" # #InstancePrefix "prefix-" # InstanceFrom "td[1]" +# #PluginInstanceFrom "td[1]" # ValuesFrom "td[2]/span[@class=\"level\"]" # # @@ -495,6 +497,7 @@ # # # +# #Plugin "mycompany" # Driver "mysql" # DriverOption "host" "localhost" # DriverOption "username" "collectd" @@ -679,6 +682,8 @@ # NotifySensorAdd false # NotifySensorRemove true # NotifySensorNotPresent false +# SELEnabled false +# SELClearEvent false # # @@ -1045,6 +1050,7 @@ # # # +# #Plugin "warehouse" # ConnectID "db01" # Username "oracle" # Password "secret" @@ -1131,6 +1137,7 @@ # StoreRates true # # +# #Plugin "kingdom" # Host "hostname" # Port "5432" # User "username" @@ -1170,6 +1177,7 @@ # # CollectFileDescriptor true # CollectContextSwitch true +# CollectMemoryMaps true # Process "name" # ProcessMatch "name" "regex" # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index c33243c5..7b037bcc 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -1624,6 +1624,7 @@ finance page and dispatch the value to collectd. + Plugin "quotes" URL "http://finance.google.com/finance?q=NYSE%3AAMD" User "foo" Password "bar" @@ -1655,6 +1656,11 @@ The following options are valid within B blocks: =over 4 +=item B I + +Use I as the plugin name when submitting values. +Defaults to C. + =item B I URL of the web site to retrieve. Since a regular expression will be used to @@ -1815,6 +1821,11 @@ The following options are valid within B blocks: Use I as the host name when submitting values. Defaults to the global host name setting. +=item B I + +Use I as the plugin name when submitting values. +Defaults to C. + =item B I Sets the plugin instance to I. @@ -1877,6 +1888,7 @@ The B uses B (L) and B Host "my_host" + #Plugin "curl_xml" Instance "some_instance" User "collectd" Password "thaiNg0I" @@ -1890,6 +1902,7 @@ The B uses B (L) and B Type "magic_level" #InstancePrefix "prefix-" InstanceFrom "td[1]" + #PluginInstanceFrom "td[1]" ValuesFrom "td[2]/span[@class=\"level\"]" @@ -1915,10 +1928,16 @@ Within the B block the following options are accepted: Use I as the host name when submitting values. Defaults to the global host name setting. +=item B I + +Use I as the plugin name when submitting values. +Defaults to 'curl_xml'. + =item B I -Use I as the plugin instance when submitting values. Defaults to an -empty string (no plugin instance). +Use I as the plugin instance when submitting values. +May be overridden by B option inside B blocks. +Defaults to an empty string (no plugin instance). =item B I I @@ -1989,9 +2008,19 @@ Specifies a XPath expression to use for determining the I. The XPath expression must return exactly one element. The element's value is then used as I, possibly prefixed with I (see above). -This value is required. As a special exception, if the "base XPath expression" -(the argument to the B block) returns exactly one argument, then this -option may be omitted. +=item B I + +Specifies a XPath expression to use for determining the I. The +XPath expression must return exactly one element. The element's value is then +used as I. + +=back + +If the "base XPath expression" (the argument to the B block) returns +exactly one argument, then I and I may be omitted. +Otherwise, at least one of I or I is required. + +=over 4 =item B I [I ...] @@ -2000,6 +2029,7 @@ number of XPath expressions must match the number of data sources in the I specified with B (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 @@ -2030,6 +2060,7 @@ than those of other plugins. It usually looks something like this: + #Plugin "warehouse" Driver "mysql" Interval 120 DriverOption "host" "localhost" @@ -2211,6 +2242,11 @@ the daemon. Other than that, that name is not used. =over 4 +=item B I + +Use I as the plugin name when submitting query results from +this B. Defaults to C. + =item B I Sets the interval (in seconds) in which the values will be collected from this @@ -3345,6 +3381,19 @@ If a sensor disappears a notification is sent. If you have for example dual power supply and one of them is (un)plugged then a notification is sent. +=item B I|I + +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. + +=item B I|I + +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. + =back =head2 Plugin C @@ -3673,6 +3722,7 @@ Synopsis of the configuration: Server "localhost" Key "page_key" + Plugin "plugin_name" Regex "(\\d+) bytes sent" DSType CounterAdd @@ -3700,6 +3750,11 @@ B block. When connected to the memcached server, asks for the page I. +=item B I + +Use I as the plugin name when submitting values. +Defaults to C. + =item EBE Match blocks define which strings to look for and how matches substrings are @@ -5748,6 +5803,7 @@ plugin's documentation above for details. + #Plugin "warehouse" ConnectID "db01" Username "oracle" Password "secret" @@ -5770,6 +5826,11 @@ values submitted to the daemon. Other than that, that name is not used. =over 4 +=item B I + +Use I as the plugin name when submitting query results from +this B. Defaults to C. + =item B I Defines the "database alias" or "service name" to connect to. Usually, these @@ -5825,7 +5886,7 @@ The address of the OVS DB server JSON-RPC interface used by the plugin. To enable the interface, OVS DB daemon should be running with C<--remote=ptcp:> option. See L for more details. The option may be either network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string -format. Defaults to B<'localhost'>. +format. Defaults to C. =item B I @@ -5891,7 +5952,7 @@ The address of the OVS DB server JSON-RPC interface used by the plugin. To enable the interface, OVS DB daemon should be running with C<--remote=ptcp:> option. See L for more details. The option may be either network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string -format. Defaults to B<'localhost'>. +format. Defaults to C. =item B I @@ -6123,6 +6184,7 @@ L. + Plugin "kingdom" Host "hostname" Port "5432" User "username" @@ -6444,6 +6506,11 @@ activating this option. The draw-back is, that data covering the specified amount of time will be lost, for example, if a single statement within the transaction fails or if the database server crashes. +=item B I + +Use I as the plugin name when submitting query results from +this B. Defaults to C. + =item B I Specify the plugin instance name that should be used instead of the database @@ -6708,6 +6775,7 @@ collected for these selected processes are: - number of processes - number of threads - number of open files (under Linux) + - number of memory mapped files (under Linux) - io data (where available) - context switches (under Linux) - minor and major pagefaults. @@ -6728,6 +6796,12 @@ slashes. Collect context switch of the process. +=item B I + +Collect the number of memory mapped files of the process. +The limit for this number is configured via F in +the Linux kernel. + =back =head2 Plugin C @@ -7703,6 +7777,7 @@ user using (extended) regular expressions, as described in L. + Plugin "mail" Instance "exim" Interval 60 @@ -7735,11 +7810,13 @@ The config consists of one or more B blocks, each of which configures one logfile to parse. Within each B block, there are one or more B blocks, which configure a regular expression to search for. -The B option in the B block may be used to set the plugin -instance. So in the above example the plugin name C would be used. -This plugin instance is for all B blocks that B it, until the -next B option. This way you can extract several plugin instances from -one logfile, handy when parsing syslog and the like. +The B and B options in the B block may be used to set +the plugin name and instance respectively. So in the above example the plugin name +C would be used. + +These options are applied for all B blocks that B it, until the +next B or B option. This way you can extract several plugin +instances from one logfile, handy when parsing syslog and the like. The B option allows you to define the length of time between reads. If this is not set, the default Interval will be used. @@ -7927,7 +8004,8 @@ B Index 1 - Instance "snort-eth0" + Plugin "snortstats" + Instance "eth0" Interval 600 Collect "snort-dropped" @@ -7979,6 +8057,11 @@ I block but there can be multiple if you have multiple CSV files. =over 4 +=item B I + +Use I as the plugin name when submitting values. +Defaults to C. + =item B I Sets the I used when dispatching the values. @@ -9328,6 +9411,7 @@ Synopsis: Prefix "collectd/" Database 1 MaxSetSize -1 + MaxSetDuration -1 StoreRates true @@ -9390,6 +9474,12 @@ to C<0>. The B option limits the number of items that the I can hold. Negative values for I sets no limit, which is the default behavior. +=item B I + +The B option limits the duration of items that the +I can hold. Negative values for I sets no duration, which +is the default behavior. + =item B B|B If set to B (the default), convert counter values to rates. If set to diff --git a/src/curl.c b/src/curl.c index 288c974e..35ec1f83 100644 --- a/src/curl.c +++ b/src/curl.c @@ -53,6 +53,7 @@ struct web_page_s; typedef struct web_page_s web_page_t; struct web_page_s /* {{{ */ { + char *plugin_name; char *instance; char *url; @@ -146,6 +147,7 @@ static void cc_web_page_free(web_page_t *wp) /* {{{ */ curl_easy_cleanup(wp->curl); wp->curl = NULL; + sfree(wp->plugin_name); sfree(wp->instance); sfree(wp->url); @@ -412,6 +414,7 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */ ERROR("curl plugin: calloc failed."); return -1; } + page->plugin_name = NULL; page->url = NULL; page->user = NULL; page->pass = NULL; @@ -435,7 +438,9 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */ for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; - if (strcasecmp("URL", child->key) == 0) + if (strcasecmp("Plugin", child->key) == 0) + status = cf_util_get_string(child, &page->plugin_name); + else if (strcasecmp("URL", child->key) == 0) status = cf_util_get_string(child, &page->url); else if (strcasecmp("User", child->key) == 0) status = cf_util_get_string(child, &page->user); @@ -566,7 +571,8 @@ static void cc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */ vl.values = &value; vl.values_len = 1; - sstrncpy(vl.plugin, "curl", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl", + sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, wm->type, sizeof(vl.type)); if (wm->instance != NULL) @@ -581,7 +587,8 @@ static void cc_submit_response_code(const web_page_t *wp, long code) /* {{{ */ vl.values = &(value_t){.gauge = (gauge_t)code}; vl.values_len = 1; - sstrncpy(vl.plugin, "curl", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl", + sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, "response_code", sizeof(vl.type)); @@ -594,7 +601,8 @@ static void cc_submit_response_time(const web_page_t *wp, /* {{{ */ vl.values = &(value_t){.gauge = response_time}; vl.values_len = 1; - sstrncpy(vl.plugin, "curl", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl", + sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, "response_time", sizeof(vl.type)); diff --git a/src/curl_json.c b/src/curl_json.c index 35b11c02..756f24f3 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -82,6 +82,7 @@ typedef struct { struct cj_s /* {{{ */ { char *instance; + char *plugin_name; char *host; char *sock; @@ -396,6 +397,7 @@ static void cj_free(void *arg) /* {{{ */ db->tree = NULL; sfree(db->instance); + sfree(db->plugin_name); sfree(db->host); sfree(db->sock); @@ -672,6 +674,8 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ 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 (db->url && strcasecmp("User", child->key) == 0) @@ -805,7 +809,8 @@ static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ sstrncpy(vl.type_instance, key->instance, sizeof(vl.type_instance)); sstrncpy(vl.host, cj_host(db), sizeof(vl.host)); - sstrncpy(vl.plugin, "curl_json", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_json", + sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, key->type, sizeof(vl.type)); diff --git a/src/curl_xml.c b/src/curl_xml.c index e83de73e..c99e3f1d 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -54,6 +54,7 @@ struct cx_xpath_s /* {{{ */ size_t values_len; char *instance_prefix; char *instance; + char *plugin_instance_from; int is_table; unsigned long magic; }; @@ -71,6 +72,7 @@ typedef struct cx_namespace_s cx_namespace_t; struct cx_s /* {{{ */ { char *instance; + char *plugin_name; char *host; char *url; @@ -95,7 +97,7 @@ struct cx_s /* {{{ */ 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; /* }}} */ @@ -105,9 +107,8 @@ 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."); @@ -118,9 +119,7 @@ static size_t cx_curl_callback(void *buf, /* {{{ */ 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; @@ -144,29 +143,28 @@ static void cx_xpath_free(cx_xpath_t *xpath) /* {{{ */ 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) /* {{{ */ { @@ -183,11 +181,12 @@ 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); @@ -208,7 +207,7 @@ static void cx_free(void *arg) /* {{{ */ 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; @@ -250,13 +249,11 @@ static int cx_check_type(const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */ 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...", @@ -279,51 +276,65 @@ static int cx_if_not_text_node(xmlNodePtr node) /* {{{ */ 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 = @@ -345,9 +356,7 @@ static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */ /* 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 */ @@ -358,7 +367,6 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */ 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); @@ -366,8 +374,7 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */ 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++) */ @@ -378,150 +385,96 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */ } /* }}} 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++) */ @@ -529,24 +482,17 @@ static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */ 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; @@ -555,20 +501,16 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */ 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); @@ -577,7 +519,7 @@ static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */ 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: " @@ -589,25 +531,29 @@ static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */ } } - 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); @@ -617,8 +563,8 @@ static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */ 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)) { @@ -628,26 +574,10 @@ static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */ 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 {{{ */ @@ -685,18 +615,13 @@ static int cx_config_add_values(const char *name, cx_xpath_t *xpath, /* {{{ */ 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; @@ -720,6 +645,8 @@ static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */ 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 { @@ -742,37 +669,25 @@ static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */ 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)) { @@ -781,8 +696,8 @@ static int cx_config_add_namespace(cx_t *db, /* {{{ */ 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; @@ -871,43 +786,51 @@ static int cx_init_curl(cx_t *db) /* {{{ */ 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) @@ -945,39 +868,34 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ 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 */ @@ -985,19 +903,14 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ 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++; diff --git a/src/dbi.c b/src/dbi.c index a577aefc..62ef1dc4 100644 --- a/src/dbi.c +++ b/src/dbi.c @@ -62,6 +62,7 @@ struct cdbi_database_s /* {{{ */ { char *name; char *select_db; + char *plugin_name; cdtime_t interval; @@ -172,7 +173,10 @@ static void cdbi_database_free(cdbi_database_t *db) /* {{{ */ return; sfree(db->name); + sfree(db->select_db); + sfree(db->plugin_name); sfree(db->driver); + sfree(db->host); for (size_t i = 0; i < db->driver_options_num; i++) { sfree(db->driver_options[i].key); @@ -184,7 +188,10 @@ static void cdbi_database_free(cdbi_database_t *db) /* {{{ */ if (db->q_prep_areas) for (size_t i = 0; i < db->queries_num; ++i) udb_query_delete_preparation_area(db->q_prep_areas[i]); - free(db->q_prep_areas); + sfree(db->q_prep_areas); + /* N.B.: db->queries references objects "owned" by the global queries + * variable. Free the array here, but not the content. */ + sfree(db->queries); sfree(db); } /* }}} void cdbi_database_free */ @@ -298,6 +305,8 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string(child, &db->host); else if (strcasecmp("Interval", child->key) == 0) status = cf_util_get_cdtime(child, &db->interval); + else if (strcasecmp("Plugin", child->key) == 0) + status = cf_util_get_string(child, &db->plugin_name); else { WARNING("dbi plugin: Option `%s' not allowed here.", child->key); status = -1; @@ -540,7 +549,8 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */ udb_query_prepare_result( q, prep_area, (db->host ? db->host : hostname_g), - /* plugin = */ "dbi", db->name, column_names, column_num, + /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi", + db->name, column_names, column_num, /* interval = */ (db->interval > 0) ? db->interval : 0); /* 0 = error; 1 = success; */ diff --git a/src/email.c b/src/email.c index e5f015b2..00e7413d 100644 --- a/src/email.c +++ b/src/email.c @@ -402,9 +402,15 @@ static void *open_connection(void __attribute__((unused)) * arg) { { struct group sg; struct group *grp; - char grbuf[4096]; int status; + long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX); + if (grbuf_size <= 0) + grbuf_size = sysconf(_SC_PAGESIZE); + if (grbuf_size <= 0) + grbuf_size = 4096; + char grbuf[grbuf_size]; + grp = NULL; status = getgrnam_r(group, &sg, grbuf, sizeof(grbuf), &grp); if (status != 0) { diff --git a/src/exec.c b/src/exec.c index 22da3160..a9f7be5c 100644 --- a/src/exec.c +++ b/src/exec.c @@ -368,11 +368,17 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out, struct passwd *sp_ptr; struct passwd sp; - char nambuf[4096]; if (pl->pid != 0) return -1; + long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (nambuf_size <= 0) + nambuf_size = sysconf(_SC_PAGESIZE); + if (nambuf_size <= 0) + nambuf_size = 4096; + char nambuf[nambuf_size]; + if ((create_pipe(fd_pipe_in) == -1) || (create_pipe(fd_pipe_out) == -1) || (create_pipe(fd_pipe_err) == -1)) goto failed; @@ -405,7 +411,14 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out, struct group *gr_ptr = NULL; struct group gr; - status = getgrnam_r(pl->group, &gr, nambuf, sizeof(nambuf), &gr_ptr); + long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX); + if (grbuf_size <= 0) + grbuf_size = sysconf(_SC_PAGESIZE); + if (grbuf_size <= 0) + grbuf_size = 4096; + char grbuf[grbuf_size]; + + status = getgrnam_r(pl->group, &gr, grbuf, sizeof(grbuf), &gr_ptr); if (status != 0) { ERROR("exec plugin: Failed to get group information " "for group ``%s'': %s", diff --git a/src/intel_pmu.c b/src/intel_pmu.c index e5f19cee..fd2bd6f3 100644 --- a/src/intel_pmu.c +++ b/src/intel_pmu.c @@ -67,7 +67,7 @@ struct intel_pmu_ctx_s { _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; @@ -265,7 +265,8 @@ static int pmu_config(oconfig_item_t *ci) { 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}; @@ -275,6 +276,7 @@ static void pmu_submit_counter(int cpu, char *event, counter_t 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)); @@ -283,6 +285,27 @@ static void pmu_submit_counter(int cpu, char *event, counter_t value) { 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; @@ -297,17 +320,27 @@ static void pmu_dispatch_data(void) { 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); } } } @@ -539,7 +572,6 @@ init_error: sfree(g_ctx.hw_events); g_ctx.hw_events_count = 0; - return ret; } diff --git a/src/ipmi.c b/src/ipmi.c index c8c80429..87f8b441 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -59,16 +59,22 @@ 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 const char *config_keys[] = {"Sensor", + "IgnoreSelected", + "NotifySensorAdd", + "NotifySensorRemove", + "NotifySensorNotPresent", + "SELEnabled", + "SELClearEvent"}; 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 _Bool c_ipmi_notify_add = 0; +static _Bool c_ipmi_notify_remove = 0; +static _Bool c_ipmi_notify_notpresent = 0; +static _Bool c_ipmi_sel_enabled = 0; +static _Bool c_ipmi_sel_clear_event = 0; /* * Misc private functions @@ -115,7 +121,7 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err, "not present.", list_item->sensor_name); - if (c_ipmi_nofiy_notpresent) { + if (c_ipmi_notify_notpresent) { notification_t n = { NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL}; @@ -164,7 +170,7 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err, INFO("ipmi plugin: sensor_read_handler: sensor %s present.", list_item->sensor_name); - if (c_ipmi_nofiy_notpresent) { + if (c_ipmi_notify_notpresent) { notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL}; @@ -200,56 +206,66 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err, plugin_dispatch_values(&vl); } /* void sensor_read_handler */ +static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) { + char temp[DATA_MAX_NAME_LEN] = {0}; + ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor); + const char *entity_id_string = ipmi_entity_get_entity_id_string(ent); + char sensor_name[DATA_MAX_NAME_LEN] = ""; + char *sensor_name_ptr; + + if ((buffer == NULL) || (buf_len == 0)) + return; + + ipmi_sensor_get_name(sensor, temp, sizeof(temp)); + temp[sizeof(temp) - 1] = 0; + + if (entity_id_string != NULL && strlen(temp)) + snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string); + else if (entity_id_string != NULL) + sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name)); + else + sstrncpy(sensor_name, temp, sizeof(sensor_name)); + + if (strlen(temp)) { + sstrncpy(temp, sensor_name, sizeof(temp)); + sensor_name_ptr = strstr(temp, ")."); + if (sensor_name_ptr != NULL) { + /* If name is something like "foo (123).bar", + * change that to "bar (123)". + * Both, sensor_name_ptr and sensor_id_ptr point to memory within the + * `temp' array, which holds a copy of the current `sensor_name'. */ + char *sensor_id_ptr; + + /* `sensor_name_ptr' points to ").bar". */ + sensor_name_ptr[1] = 0; + /* `temp' holds "foo (123)\0bar\0". */ + sensor_name_ptr += 2; + /* `sensor_name_ptr' now points to "bar". */ + + sensor_id_ptr = strstr(temp, "("); + if (sensor_id_ptr != NULL) { + /* `sensor_id_ptr' now points to "(123)". */ + snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr, + sensor_id_ptr); + } + /* else: don't touch sensor_name. */ + } + } + sstrncpy(buffer, sensor_name, buf_len); +} + static int sensor_list_add(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); - - ipmi_sensor_get_name(sensor, buffer, sizeof(buffer)); - buffer[sizeof(buffer) - 1] = 0; - - entity_id_string = ipmi_entity_get_entity_id_string(ent); - - 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. */ - } - sensor_name_ptr = sensor_name; + sensor_get_name(sensor, buffer, sizeof(buffer)); /* Both `ignorelist' and `plugin_instance' may be NULL. */ if (ignorelist_match(ignorelist, sensor_name_ptr) != 0) @@ -321,7 +337,7 @@ static int sensor_list_add(ipmi_sensor_t *sensor) { pthread_mutex_unlock(&sensor_list_lock); - if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) { + if (c_ipmi_notify_add && (c_ipmi_init_in_progress == 0)) { notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL}; sstrncpy(n.host, hostname_g, sizeof(n.host)); @@ -368,7 +384,7 @@ static int sensor_list_remove(ipmi_sensor_t *sensor) { pthread_mutex_unlock(&sensor_list_lock); - if (c_ipmi_nofiy_remove && c_ipmi_active) { + if (c_ipmi_notify_remove && c_ipmi_active) { notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL}; @@ -420,6 +436,160 @@ static int sensor_list_remove_all(void) { 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) { + + /* 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; + + /* 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 */ + notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL}; + 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.host, hostname_g, sizeof(n.host)); + 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 (c_ipmi_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) { + /* 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 = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL}; + 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.host, hostname_g, sizeof(n.host)); + 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 (c_ipmi_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 */ @@ -431,8 +601,39 @@ static void entity_sensor_update_handler( if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) { /* Will check for duplicate entries.. */ sensor_list_add(sensor); + + if (c_ipmi_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, NULL); + /* 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, NULL); + + 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); + + if (c_ipmi_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, NULL); + else + ipmi_sensor_remove_discrete_event_handler( + sensor, sensor_discrete_event_handler, NULL); + } } } /* void entity_sensor_update_handler */ @@ -459,6 +660,23 @@ static void domain_entity_update_handler( } } /* 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, @@ -476,6 +694,13 @@ static void domain_connection_change_handler(ipmi_domain_t *domain, int err, if (status != 0) { c_ipmi_error("ipmi_domain_add_entity_update_handler", status); } + + ipmi_con_t *smi_connection = user_data; + status = smi_connection->add_event_handler(smi_connection, smi_event_handler, + (void *)domain); + + if (status != 0) + c_ipmi_error("Failed to register smi event handler", status); } /* void domain_connection_change_handler */ static int thread_init(os_handler_t **ret_os_handler) { @@ -504,7 +729,8 @@ static int thread_init(os_handler_t **ret_os_handler) { status = ipmi_open_domain( "mydomain", &smi_connection, /* num_con = */ 1, - domain_connection_change_handler, /* user data = */ NULL, + domain_connection_change_handler, + /* user data = */ (void *)smi_connection, /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option, sizeof(open_option) / sizeof(open_option[0]), &domain_id); if (status != 0) { @@ -545,19 +771,17 @@ static int c_ipmi_config(const char *key, const char *value) { 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); + ignorelist_set_invert(ignorelist, !IS_TRUE(value)); } else if (strcasecmp("NotifySensorAdd", key) == 0) { - if (IS_TRUE(value)) - c_ipmi_nofiy_add = 1; + c_ipmi_notify_add = IS_TRUE(value); } else if (strcasecmp("NotifySensorRemove", key) == 0) { - if (IS_TRUE(value)) - c_ipmi_nofiy_remove = 1; + c_ipmi_notify_remove = IS_TRUE(value); } else if (strcasecmp("NotifySensorNotPresent", key) == 0) { - if (IS_TRUE(value)) - c_ipmi_nofiy_notpresent = 1; + c_ipmi_notify_notpresent = IS_TRUE(value); + } else if (strcasecmp("SELEnabled", key) == 0) { + c_ipmi_sel_enabled = IS_TRUE(value); + } else if (strcasecmp("SELClearEvent", key) == 0) { + c_ipmi_sel_clear_event = IS_TRUE(value); } else { return -1; } diff --git a/src/memcachec.c b/src/memcachec.c index c2147fd5..bd088ecd 100644 --- a/src/memcachec.c +++ b/src/memcachec.c @@ -51,6 +51,7 @@ struct web_page_s; typedef struct web_page_s web_page_t; struct web_page_s /* {{{ */ { + char *plugin_name; char *instance; char *server; @@ -94,6 +95,7 @@ static void cmc_web_page_free(web_page_t *wp) /* {{{ */ memcached_free(wp->memc); wp->memc = NULL; + sfree(wp->plugin_name); sfree(wp->instance); sfree(wp->server); sfree(wp->key); @@ -302,6 +304,8 @@ static int cmc_config_add_page(oconfig_item_t *ci) /* {{{ */ status = cmc_config_add_string("Server", &page->server, child); else if (strcasecmp("Key", child->key) == 0) status = cmc_config_add_string("Key", &page->key, child); + else if (strcasecmp("Plugin", child->key) == 0) + status = cmc_config_add_string("Plugin", &page->plugin_name, child); else if (strcasecmp("Match", child->key) == 0) /* Be liberal with failing matches => don't set `status'. */ cmc_config_add_match(page, child); @@ -407,7 +411,8 @@ static void cmc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */ vl.values = &value; vl.values_len = 1; - sstrncpy(vl.plugin, "memcachec", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "memcachec", + sizeof (vl.plugin)); sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, wm->type, sizeof(vl.type)); sstrncpy(vl.type_instance, wm->instance, sizeof(vl.type_instance)); diff --git a/src/oracle.c b/src/oracle.c index 44bd5648..099013e3 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -62,6 +62,7 @@ struct o_database_s { char *connect_id; char *username; char *password; + char *plugin_name; udb_query_preparation_area_t **q_prep_areas; udb_query_t **queries; @@ -141,6 +142,7 @@ static void o_database_free(o_database_t *db) /* {{{ */ sfree(db->username); sfree(db->password); sfree(db->queries); + sfree(db->plugin_name); if (db->q_prep_areas != NULL) for (size_t i = 0; i < db->queries_num; ++i) @@ -192,6 +194,7 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */ db->connect_id = NULL; db->username = NULL; db->password = NULL; + db->plugin_name = NULL; status = cf_util_get_string(ci, &db->name); if (status != 0) { @@ -211,6 +214,8 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string(child, &db->username); else if (strcasecmp("Password", child->key) == 0) status = cf_util_get_string(child, &db->password); + else if (strcasecmp("Plugin", child->key) == 0) + status = cf_util_get_string(child, &db->plugin_name); else if (strcasecmp("Query", child->key) == 0) status = udb_query_pick_from_list(child, queries, queries_num, &db->queries, &db->queries_num); @@ -544,7 +549,8 @@ static int o_read_database_query(o_database_t *db, /* {{{ */ status = udb_query_prepare_result( q, prep_area, (db->host != NULL) ? db->host : hostname_g, - /* plugin = */ "oracle", db->name, column_names, column_num, + /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle", + db->name, column_names, column_num, /* interval = */ 0); if (status != 0) { ERROR("oracle plugin: o_read_database_query (%s, %s): " diff --git a/src/postgresql.c b/src/postgresql.c index 56730b47..3b702aee 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -139,6 +139,7 @@ typedef struct { char *password; char *instance; + char *plugin_name; char *sslmode; @@ -250,6 +251,8 @@ static c_psql_database_t *c_psql_database_new(const char *name) { db->instance = sstrdup(name); + db->plugin_name = NULL; + db->sslmode = NULL; db->krbsrvname = NULL; @@ -300,6 +303,8 @@ static void c_psql_database_delete(void *data) { sfree(db->instance); + sfree(db->plugin_name); + sfree(db->sslmode); sfree(db->krbsrvname); @@ -539,9 +544,11 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q, else host = db->host; - status = - udb_query_prepare_result(q, prep_area, host, "postgresql", db->instance, - column_names, (size_t)column_num, db->interval); + 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); + if (0 != status) { log_err("udb_query_prepare_result failed with status %i.", status); BAIL_OUT(-1); @@ -1139,6 +1146,8 @@ static int c_psql_config_database(oconfig_item_t *ci) { 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, "SSLMode")) cf_util_get_string(c, &db->sslmode); else if (0 == strcasecmp(c->key, "KRBSrvName")) diff --git a/src/processes.c b/src/processes.c index 727ec7fd..89cf3e0e 100644 --- a/src/processes.c +++ b/src/processes.c @@ -33,6 +33,7 @@ * Clément Stenac * Cosmin Ioiart * Pavel Rochnyack + * Wilfried Goesgens **/ #include "collectd.h" @@ -168,6 +169,7 @@ typedef struct process_entry_s { unsigned long num_proc; unsigned long num_lwp; unsigned long num_fd; + unsigned long num_maps; unsigned long vmem_size; unsigned long vmem_rss; unsigned long vmem_data; @@ -194,6 +196,8 @@ typedef struct process_entry_s { _Bool has_cswitch; _Bool has_fd; + + _Bool has_maps; } process_entry_t; typedef struct procstat_entry_s { @@ -229,6 +233,7 @@ typedef struct procstat { unsigned long num_proc; unsigned long num_lwp; unsigned long num_fd; + unsigned long num_maps; unsigned long vmem_size; unsigned long vmem_rss; unsigned long vmem_data; @@ -253,6 +258,7 @@ typedef struct procstat { derive_t cswitch_invol; _Bool report_fd_num; + _Bool report_maps_num; _Bool report_ctx_switch; struct procstat *next; @@ -264,6 +270,7 @@ static procstat_t *list_head_g = NULL; static _Bool want_init = 1; static _Bool report_ctx_switch = 0; static _Bool report_fd_num = 0; +static _Bool report_maps_num = 0; #if HAVE_THREAD_INFO static mach_port_t port_host_self; @@ -322,6 +329,7 @@ static procstat_t *ps_list_register(const char *name, const char *regexp) { new->cswitch_invol = -1; new->report_fd_num = report_fd_num; + new->report_maps_num = report_maps_num; new->report_ctx_switch = report_ctx_switch; #if HAVE_REGEX_H @@ -472,6 +480,7 @@ static void ps_list_add(const char *name, const char *cmdline, ps->num_proc += entry->num_proc; ps->num_lwp += entry->num_lwp; ps->num_fd += entry->num_fd; + ps->num_maps += entry->num_maps; ps->vmem_size += entry->vmem_size; ps->vmem_rss += entry->vmem_rss; ps->vmem_data += entry->vmem_data; @@ -521,6 +530,7 @@ static void ps_list_reset(void) { ps->num_proc = 0; ps->num_lwp = 0; ps->num_fd = 0; + ps->num_maps = 0; ps->vmem_size = 0; ps->vmem_rss = 0; ps->vmem_data = 0; @@ -561,6 +571,8 @@ static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) { cf_util_get_boolean(c, &ps->report_ctx_switch); else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) cf_util_get_boolean(c, &ps->report_fd_num); + else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) + cf_util_get_boolean(c, &ps->report_maps_num); else { ERROR("processes plugin: Option `%s' not allowed here.", c->key); } @@ -619,6 +631,8 @@ static int ps_config(oconfig_item_t *ci) { cf_util_get_boolean(c, &report_ctx_switch); } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) { cf_util_get_boolean(c, &report_fd_num); + } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) { + cf_util_get_boolean(c, &report_maps_num); } else { ERROR("processes plugin: The `%s' configuration option is not " "understood and will be ignored.", @@ -768,6 +782,14 @@ static void ps_submit_proc_list(procstat_t *ps) { plugin_dispatch_values(&vl); } + if (ps->num_maps > 0) { + sstrncpy(vl.type, "file_handles", sizeof(vl.type)); + sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance)); + vl.values[0].gauge = ps->num_maps; + vl.values_len = 1; + plugin_dispatch_values(&vl); + } + if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) { sstrncpy(vl.type, "contextswitch", sizeof(vl.type)); sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance)); @@ -782,20 +804,23 @@ static void ps_submit_proc_list(procstat_t *ps) { plugin_dispatch_values(&vl); } - DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; " - "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; " - "vmem_code = %lu; " - "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; " - "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; " - "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; " - "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; " - "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; " - "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";", - ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->vmem_size, - ps->vmem_rss, ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter, - ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter, - ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->io_diskr, - ps->io_diskw, ps->cswitch_vol, ps->cswitch_invol); + DEBUG( + "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; " + "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; " + "vmem_code = %lu; " + "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; " + "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; " + "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; " + "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; " + "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; " + "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";", + ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps, + ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code, + ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter, + ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr, + ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol, + ps->cswitch_invol); + } /* void ps_submit_proc_list */ #if KERNEL_LINUX || KERNEL_SOLARIS @@ -1004,6 +1029,31 @@ static int ps_read_io(process_entry_t *ps) { return 0; } /* int ps_read_io (...) */ +static int ps_count_maps(pid_t pid) { + FILE *fh; + char buffer[1024]; + char filename[64]; + int count = 0; + + snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); + if ((fh = fopen(filename, "r")) == NULL) { + DEBUG("ps_count_maps: Failed to open file `%s'", filename); + return -1; + } + + while (fgets(buffer, sizeof(buffer), fh) != NULL) { + if (strchr(buffer, '\n')) { + count++; + } + } /* while (fgets) */ + + if (fclose(fh)) { + char errbuf[1024]; + WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + } + return count; +} /* int ps_count_maps (...) */ + static int ps_count_fd(int pid) { char dirname[64]; DIR *dh; @@ -1040,6 +1090,14 @@ static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) { } } + if (ps->report_maps_num) { + int num_maps; + if (entry->has_maps == 0 && (num_maps = ps_count_maps(entry->id)) > 0) { + entry->num_maps = num_maps; + } + entry->has_maps = 1; + } + if (ps->report_fd_num) { int num_fd; if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) { @@ -1421,6 +1479,9 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) { */ ps->num_fd = 0; + /* Number of memory mappings */ + ps->num_maps = 0; + /* * Calculating input/ouput chars * Formula used is total chars / total blocks => chars/block @@ -1656,6 +1717,9 @@ static int ps_read(void) { /* File descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + pse.vmem_minflt_counter = task_events_info.cow_faults; pse.vmem_majflt_counter = task_events_info.faults; @@ -1961,6 +2025,9 @@ static int ps_read(void) { /* file descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + /* context switch counters not implemented */ pse.cswitch_vol = -1; pse.cswitch_invol = -1; @@ -2102,6 +2169,9 @@ static int ps_read(void) { /* file descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + /* context switch counters not implemented */ pse.cswitch_vol = -1; pse.cswitch_invol = -1; @@ -2265,6 +2335,7 @@ static int ps_read(void) { pse.io_diskw = -1; pse.num_fd = 0; + pse.num_maps = 0; pse.cswitch_vol = -1; pse.cswitch_invol = -1; diff --git a/src/snmp.c b/src/snmp.c index aa3c9dda..1ac65c8e 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -1327,8 +1327,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { status = 0; while (status == 0) { - int oid_list_todo_num; - req = snmp_pdu_create(SNMP_MSG_GETNEXT); if (req == NULL) { ERROR("snmp plugin: snmp_pdu_create failed."); @@ -1336,24 +1334,33 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { break; } - oid_list_todo_num = 0; + size_t oid_list_todo_num = 0; + size_t var_idx[oid_list_len]; + memset(var_idx, 0, sizeof(var_idx)); + for (i = 0; i < oid_list_len; i++) { /* Do not rerequest already finished OIDs */ if (!oid_list_todo[i]) continue; - oid_list_todo_num++; snmp_add_null_var(req, oid_list[i].oid, oid_list[i].oid_len); + var_idx[oid_list_todo_num] = i; + oid_list_todo_num++; } if (oid_list_todo_num == 0) { /* The request is still empty - so we are finished */ DEBUG("snmp plugin: all variables have left their subtree"); + snmp_free_pdu(req); status = 0; break; } res = NULL; status = snmp_sess_synch_response(host->sess_handle, req, &res); + + /* snmp_sess_synch_response always frees our req PDU */ + req = NULL; + if ((status != STAT_SUCCESS) || (res == NULL)) { char *errstr = NULL; @@ -1367,8 +1374,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { snmp_free_pdu(res); res = NULL; - /* snmp_synch_response already freed our PDU */ - req = NULL; sfree(errstr); csnmp_host_close_session(host); @@ -1388,6 +1393,39 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { break; } + if (res->errstat != SNMP_ERR_NOERROR) { + if (res->errindex != 0) { + /* Find the OID which caused error */ + for (i = 1, vb = res->variables; vb != NULL && i != res->errindex; + vb = vb->next_variable, i++) + /* do nothing */; + } + + if ((res->errindex == 0) || (vb == NULL)) { + ERROR("snmp plugin: host %s; data %s: response error: %s (%li) ", + host->name, data->name, snmp_errstring(res->errstat), + res->errstat); + status = -1; + break; + } + + char oid_buffer[1024] = {0}; + snprint_objid(oid_buffer, sizeof(oid_buffer) - 1, vb->name, + vb->name_length); + NOTICE("snmp plugin: host %s; data %s: OID `%s` failed: %s", host->name, + data->name, oid_buffer, snmp_errstring(res->errstat)); + + /* Get value index from todo list and skip OID found */ + assert(res->errindex <= oid_list_todo_num); + i = var_idx[res->errindex - 1]; + assert(i < oid_list_len); + oid_list_todo[i] = 0; + + snmp_free_pdu(res); + res = NULL; + continue; + } + for (vb = res->variables, i = 0; (vb != NULL); vb = vb->next_variable, i++) { /* Calculate value index from todo list */ @@ -1483,9 +1521,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { snmp_free_pdu(res); res = NULL; - if (req != NULL) - snmp_free_pdu(req); - req = NULL; if (status == 0) csnmp_dispatch_table(host, data, instance_list_head, value_list_head); diff --git a/src/tail.c b/src/tail.c index 407970ae..fbba4788 100644 --- a/src/tail.c +++ b/src/tail.c @@ -34,7 +34,8 @@ /* * * - * Instance "exim" + * Plugin "mail" + * Instance "exim" * Interval 60 * * Regex "S=([1-9][0-9]*)" @@ -134,6 +135,7 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm, } /* int ctail_config_add_match_dstype */ static int ctail_config_add_match(cu_tail_match_t *tm, + const char *plugin_name, const char *plugin_instance, oconfig_item_t *ci, cdtime_t interval) { ctail_config_match_t cm = {0}; @@ -191,7 +193,8 @@ static int ctail_config_add_match(cu_tail_match_t *tm, if (status == 0) { // TODO(octo): there's nothing "simple" about the latency stuff … status = tail_match_add_match_simple( - tm, cm.regex, cm.excluderegex, cm.flags, "tail", plugin_instance, + tm, cm.regex, cm.excluderegex, cm.flags, + (plugin_name != NULL) ? plugin_name : "tail", plugin_instance, cm.type, cm.type_instance, cm.latency, interval); if (status != 0) @@ -210,6 +213,7 @@ static int ctail_config_add_match(cu_tail_match_t *tm, static int ctail_config_add_file(oconfig_item_t *ci) { cu_tail_match_t *tm; cdtime_t interval = 0; + char *plugin_name = NULL; char *plugin_instance = NULL; int num_matches = 0; @@ -229,12 +233,15 @@ static int ctail_config_add_file(oconfig_item_t *ci) { oconfig_item_t *option = ci->children + i; int status = 0; - if (strcasecmp("Instance", option->key) == 0) + if (strcasecmp("Plugin", option->key) == 0) + status = cf_util_get_string (option, &plugin_name); + else if (strcasecmp("Instance", option->key) == 0) status = cf_util_get_string(option, &plugin_instance); else if (strcasecmp("Interval", option->key) == 0) cf_util_get_cdtime(option, &interval); else if (strcasecmp("Match", option->key) == 0) { - status = ctail_config_add_match(tm, plugin_instance, option, interval); + status = ctail_config_add_match(tm, plugin_name, plugin_instance, option, + interval); if (status == 0) num_matches++; /* Be mild with failed matches.. */ @@ -247,6 +254,7 @@ static int ctail_config_add_file(oconfig_item_t *ci) { break; } /* for (i = 0; i < ci->children_num; i++) */ + sfree(plugin_name); sfree(plugin_instance); if (num_matches == 0) { diff --git a/src/tail_csv.c b/src/tail_csv.c index a9ce5d19..2e3ac5f0 100644 --- a/src/tail_csv.c +++ b/src/tail_csv.c @@ -44,6 +44,7 @@ struct metric_definition_s { typedef struct metric_definition_s metric_definition_t; struct instance_definition_s { + char *plugin_name; char *instance; char *path; cu_tail_t *tail; @@ -67,7 +68,8 @@ static int tcsv_submit(instance_definition_t *id, metric_definition_t *md, vl.values_len = 1; vl.values = &v; - sstrncpy(vl.plugin, "tail_csv", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (id->plugin_name != NULL) ? id->plugin_name : "tail_csv", + sizeof(vl.plugin)); if (id->instance != NULL) sstrncpy(vl.plugin_instance, id->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, md->type, sizeof(vl.type)); @@ -358,6 +360,7 @@ static void tcsv_instance_definition_destroy(void *arg) { cu_tail_destroy(id->tail); id->tail = NULL; + sfree(id->plugin_name); sfree(id->instance); sfree(id->path); sfree(id->metric_list); @@ -420,6 +423,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { id = calloc(1, sizeof(*id)); if (id == NULL) return -1; + id->plugin_name = NULL; id->instance = NULL; id->path = NULL; id->metric_list = NULL; @@ -447,6 +451,8 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { cf_util_get_cdtime(option, &id->interval); else if (strcasecmp("TimeFrom", option->key) == 0) status = tcsv_config_get_index(option, &id->time_from); + else if (strcasecmp("Plugin", option->key) == 0) + status = cf_util_get_string(option, &id->plugin_name); else { WARNING("tail_csv plugin: Option `%s' not allowed here.", option->key); status = -1; diff --git a/src/unixsock.c b/src/unixsock.c index 00b930e6..99e39eee 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -134,7 +134,13 @@ static int us_open_socket(void) { const char *grpname; struct group *g; struct group sg; - char grbuf[4096]; + + long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX); + if (grbuf_size <= 0) + grbuf_size = sysconf(_SC_PAGESIZE); + if (grbuf_size <= 0) + grbuf_size = 4096; + char grbuf[grbuf_size]; grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME; g = NULL; diff --git a/src/utils_match.c b/src/utils_match.c index df70fbec..d3edb57c 100644 --- a/src/utils_match.c +++ b/src/utils_match.c @@ -294,7 +294,7 @@ void match_value_reset(cu_match_value_t *mv) { /* Reset GAUGE metrics only and except GAUGE_PERSIST. */ if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { - mv->value.gauge = NAN; + mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN; mv->values_num = 0; } } /* }}} void match_value_reset */ diff --git a/src/varnish.c b/src/varnish.c index a0286d31..e4daf4bf 100644 --- a/src/varnish.c +++ b/src/varnish.c @@ -367,10 +367,10 @@ static int varnish_monitor(void *priv, "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 } @@ -442,10 +442,10 @@ static int varnish_monitor(void *priv, 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", @@ -668,7 +668,7 @@ static int varnish_monitor(void *priv, 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", @@ -761,7 +761,7 @@ static int varnish_monitor(void *priv, 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); } @@ -839,10 +839,10 @@ static int varnish_monitor(void *priv, 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", @@ -869,19 +869,19 @@ static int varnish_monitor(void *priv, 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); } diff --git a/src/write_prometheus.c b/src/write_prometheus.c index eac0d68e..a005ffb2 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -36,6 +36,10 @@ #include +#include +#include +#include + #ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA #define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300) #endif @@ -727,6 +731,90 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index, } /* }}} */ +#if MHD_VERSION >= 0x00090000 +static int prom_open_socket(int addrfamily) { + /* {{{ */ + char service[NI_MAXSERV]; + snprintf(service, sizeof(service), "%hu", httpd_port); + + struct addrinfo *res; + int status = getaddrinfo(NULL, service, + &(struct addrinfo){ + .ai_flags = AI_PASSIVE | AI_ADDRCONFIG, + .ai_family = addrfamily, + .ai_socktype = SOCK_STREAM, + }, + &res); + if (status != 0) { + return -1; + } + + int fd = -1; + for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) { + fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0); + if (fd == -1) + continue; + + if (bind(fd, ai->ai_addr, ai->ai_addrlen) != 0) { + close(fd); + fd = -1; + continue; + } + + if (listen(fd, /* backlog = */ 16) != 0) { + close(fd); + fd = -1; + continue; + } + + break; + } + + freeaddrinfo(res); + + return fd; +} /* }}} int prom_open_socket */ + +static struct MHD_Daemon *prom_start_daemon() { + /* {{{ */ + int fd = prom_open_socket(PF_INET6); + if (fd == -1) + fd = prom_open_socket(PF_INET); + if (fd == -1) { + ERROR("write_prometheus plugin: Opening a listening socket failed."); + return NULL; + } + + struct MHD_Daemon *d = + MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0, + /* MHD_AcceptPolicyCallback = */ NULL, + /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, + NULL, MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_END); + if (d == NULL) { + ERROR("write_prometheus plugin: MHD_start_daemon() failed."); + close(fd); + return NULL; + } + + return d; +} /* }}} struct MHD_Daemon *prom_start_daemon */ +#else /* if MHD_VERSION < 0x00090000 */ +static struct MHD_Daemon *prom_start_daemon() { + /* {{{ */ + struct MHD_Daemon *d = + MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0, + /* MHD_AcceptPolicyCallback = */ NULL, + /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, + NULL, MHD_OPTION_END); + if (d == NULL) { + ERROR("write_prometheus plugin: MHD_start_daemon() failed."); + return NULL; + } + + return d; +} /* }}} struct MHD_Daemon *prom_start_daemon */ +#endif + /* * collectd callbacks */ @@ -760,15 +848,7 @@ static int prom_init() { } 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;