From: Ruben Kerkhof Date: Tue, 29 Nov 2016 14:46:52 +0000 (+0100) Subject: Merge pull request #2064 from rubenk/fix-make-distcheck X-Git-Tag: collectd-5.7.0~6 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=1584297665665f900011297f1ae0e29ecf9ce379;hp=2e632b0929fb957fd686231658bc2999fdfb4b20;p=collectd.git Merge pull request #2064 from rubenk/fix-make-distcheck Fix make distcheck --- diff --git a/src/Makefile.am b/src/Makefile.am index a01176d9..37763894 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -334,7 +334,8 @@ endif if BUILD_PLUGIN_CURL pkglib_LTLIBRARIES += curl.la curl_la_SOURCES = curl.c \ - utils_curl_stats.c utils_curl_stats.h + utils_curl_stats.c utils_curl_stats.h \ + utils_match.c utils_match.h curl_la_LDFLAGS = $(PLUGIN_LDFLAGS) curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) curl_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) @@ -686,7 +687,7 @@ endif if BUILD_PLUGIN_MEMCACHEC pkglib_LTLIBRARIES += memcachec.la -memcachec_la_SOURCES = memcachec.c +memcachec_la_SOURCES = memcachec.c utils_match.c utils_match.h memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS) memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS) memcachec_la_LIBADD = $(BUILD_WITH_LIBMEMCACHED_LIBS) @@ -1114,14 +1115,14 @@ endif if BUILD_PLUGIN_TAIL pkglib_LTLIBRARIES += tail.la -tail_la_SOURCES = tail.c +tail_la_SOURCES = tail.c utils_tail_match.c utils_tail_match.h tail_la_LDFLAGS = $(PLUGIN_LDFLAGS) tail_la_LIBADD = liblatency.la endif if BUILD_PLUGIN_TAIL_CSV pkglib_LTLIBRARIES += tail_csv.la -tail_csv_la_SOURCES = tail_csv.c +tail_csv_la_SOURCES = tail_csv.c utils_tail.c utils_tail.h tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS) endif diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index ac8c7640..52079438 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -61,15 +61,10 @@ collectd_SOURCES = collectd.c collectd.h \ utils_ignorelist.c utils_ignorelist.h \ utils_llist.c utils_llist.h \ utils_random.c utils_random.h \ - utils_tail_match.c utils_tail_match.h \ - utils_match.c utils_match.h \ utils_subst.c utils_subst.h \ - utils_tail.c utils_tail.h \ utils_time.c utils_time.h \ types_list.c types_list.h \ - utils_threshold.c utils_threshold.h \ - ../utils_latency_config.h ../utils_latency_config.c \ - ../utils_latency.h ../utils_latency.c + utils_threshold.c utils_threshold.h collectd_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) diff --git a/src/daemon/utils_match.c b/src/daemon/utils_match.c deleted file mode 100644 index 2e487b59..00000000 --- a/src/daemon/utils_match.c +++ /dev/null @@ -1,372 +0,0 @@ -/** - * collectd - src/utils_match.c - * Copyright (C) 2008-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_match.h" - -#include - -#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02 - -struct cu_match_s { - regex_t regex; - regex_t excluderegex; - int flags; - - int (*callback)(const char *str, char *const *matches, size_t matches_num, - void *user_data); - void *user_data; - void (*free)(void *user_data); -}; - -/* - * Private functions - */ -static char *match_substr(const char *str, int begin, int end) { - char *ret; - size_t ret_len; - - if ((begin < 0) || (end < 0) || (begin >= end)) - return (NULL); - if ((size_t)end > (strlen(str) + 1)) { - ERROR("utils_match: match_substr: `end' points after end of string."); - return (NULL); - } - - ret_len = end - begin; - ret = malloc(ret_len + 1); - if (ret == NULL) { - ERROR("utils_match: match_substr: malloc failed."); - return (NULL); - } - - sstrncpy(ret, str + begin, ret_len + 1); - return (ret); -} /* char *match_substr */ - -static int default_callback(const char __attribute__((unused)) * str, - char *const *matches, size_t matches_num, - void *user_data) { - cu_match_value_t *data = (cu_match_value_t *)user_data; - - if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) { - gauge_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) { - data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1; - data->values_num++; - return (0); - } - - if (matches_num < 2) - return (-1); - - value = (gauge_t)strtod(matches[1], &endptr); - if (matches[1] == endptr) - return (-1); - - if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) { - latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value)); - data->values_num++; - return (0); - } - - if ((data->values_num == 0) || - (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) || - (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) { - double f = ((double)data->values_num) / ((double)(data->values_num + 1)); - data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f)); - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) { - if (data->value.gauge > value) - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) { - if (data->value.gauge < value) - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) { - data->value.gauge += value; - } else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return (-1); - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) { - counter_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) { - data->value.counter++; - data->values_num++; - return (0); - } - - if (matches_num < 2) - return (-1); - - value = (counter_t)strtoull(matches[1], &endptr, 0); - if (matches[1] == endptr) - return (-1); - - if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET) - data->value.counter = value; - else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD) - data->value.counter += value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return (-1); - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) { - derive_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) { - data->value.derive++; - data->values_num++; - return (0); - } - - if (matches_num < 2) - return (-1); - - value = (derive_t)strtoll(matches[1], &endptr, 0); - if (matches[1] == endptr) - return (-1); - - if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET) - data->value.derive = value; - else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD) - data->value.derive += value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return (-1); - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) { - absolute_t value; - char *endptr = NULL; - - if (matches_num < 2) - return (-1); - - value = (absolute_t)strtoull(matches[1], &endptr, 0); - if (matches[1] == endptr) - return (-1); - - if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET) - data->value.absolute = value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return (-1); - } - - data->values_num++; - } else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return (-1); - } - - return (0); -} /* int default_callback */ - -static void match_simple_free(void *data) { - cu_match_value_t *user_data = (cu_match_value_t *)data; - if (user_data->latency) - latency_counter_destroy(user_data->latency); - - free(data); -} /* void match_simple_free */ - -/* - * Public functions - */ -cu_match_t * -match_create_callback(const char *regex, const char *excluderegex, - int (*callback)(const char *str, char *const *matches, - size_t matches_num, void *user_data), - void *user_data, - void (*free_user_data)(void *user_data)) { - cu_match_t *obj; - int status; - - DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s", - regex, excluderegex); - - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) - return (NULL); - - status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE); - if (status != 0) { - ERROR("Compiling the regular expression \"%s\" failed.", regex); - sfree(obj); - return (NULL); - } - - if (excluderegex && strcmp(excluderegex, "") != 0) { - status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED); - if (status != 0) { - ERROR("Compiling the excluding regular expression \"%s\" failed.", - excluderegex); - sfree(obj); - return (NULL); - } - obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX; - } - - obj->callback = callback; - obj->user_data = user_data; - obj->free = free_user_data; - - return (obj); -} /* cu_match_t *match_create_callback */ - -cu_match_t *match_create_simple(const char *regex, const char *excluderegex, - int match_ds_type) { - cu_match_value_t *user_data; - cu_match_t *obj; - - user_data = calloc(1, sizeof(*user_data)); - if (user_data == NULL) - return (NULL); - user_data->ds_type = match_ds_type; - - if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && - (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { - user_data->latency = latency_counter_create(); - if (user_data->latency == NULL) { - ERROR("match_create_simple(): latency_counter_create() failed."); - free(user_data); - return (NULL); - } - } - - obj = match_create_callback(regex, excluderegex, default_callback, user_data, - match_simple_free); - if (obj == NULL) { - if (user_data->latency) - latency_counter_destroy(user_data->latency); - - sfree(user_data); - return (NULL); - } - return (obj); -} /* cu_match_t *match_create_simple */ - -void match_value_reset(cu_match_value_t *mv) { - if (mv == NULL) - return; - - /* 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->values_num = 0; - } -} /* }}} void match_value_reset */ - -void match_destroy(cu_match_t *obj) { - if (obj == NULL) - return; - - if ((obj->user_data != NULL) && (obj->free != NULL)) - (*obj->free)(obj->user_data); - - sfree(obj); -} /* void match_destroy */ - -int match_apply(cu_match_t *obj, const char *str) { - int status; - regmatch_t re_match[32]; - char *matches[32] = {0}; - size_t matches_num; - - if ((obj == NULL) || (str == NULL)) - return (-1); - - if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) { - status = - regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match, - /* eflags = */ 0); - /* Regex did match, so exclude this line */ - if (status == 0) { - DEBUG("ExludeRegex matched, don't count that line\n"); - return (0); - } - } - - status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match, - /* eflags = */ 0); - - /* Regex did not match */ - if (status != 0) - return (0); - - for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches); - matches_num++) { - if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0)) - break; - - matches[matches_num] = match_substr(str, re_match[matches_num].rm_so, - re_match[matches_num].rm_eo); - if (matches[matches_num] == NULL) { - status = -1; - break; - } - } - - if (status != 0) { - ERROR("utils_match: match_apply: match_substr failed."); - } else { - status = obj->callback(str, matches, matches_num, obj->user_data); - if (status != 0) { - ERROR("utils_match: match_apply: callback failed."); - } - } - - for (size_t i = 0; i < matches_num; i++) { - sfree(matches[i]); - } - - return (status); -} /* int match_apply */ - -void *match_get_user_data(cu_match_t *obj) { - if (obj == NULL) - return (NULL); - return (obj->user_data); -} /* void *match_get_user_data */ - -/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/daemon/utils_match.h b/src/daemon/utils_match.h deleted file mode 100644 index 1383530c..00000000 --- a/src/daemon/utils_match.h +++ /dev/null @@ -1,181 +0,0 @@ -/** - * collectd - src/utils_match.h - * Copyright (C) 2008-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_MATCH_H -#define UTILS_MATCH_H 1 - -#include "plugin.h" -#include "utils_latency.h" - -/* - * Each type may have 12 sub-types - * 0x1000 = 1000000000000 - * ^ <- Type bit - * ^^^^^^^^^^^^ <- Subtype bits - */ -#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000 -#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000 -#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000 -#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000 - -#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01 -#define UTILS_MATCH_CF_GAUGE_MIN 0x02 -#define UTILS_MATCH_CF_GAUGE_MAX 0x04 -#define UTILS_MATCH_CF_GAUGE_LAST 0x08 -#define UTILS_MATCH_CF_GAUGE_INC 0x10 -#define UTILS_MATCH_CF_GAUGE_ADD 0x20 -#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40 -#define UTILS_MATCH_CF_GAUGE_DIST 0x80 - -#define UTILS_MATCH_CF_COUNTER_SET 0x01 -#define UTILS_MATCH_CF_COUNTER_ADD 0x02 -#define UTILS_MATCH_CF_COUNTER_INC 0x04 - -#define UTILS_MATCH_CF_DERIVE_SET 0x01 -#define UTILS_MATCH_CF_DERIVE_ADD 0x02 -#define UTILS_MATCH_CF_DERIVE_INC 0x04 - -#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01 -#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02 -#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04 - -/* - * Data types - */ -struct cu_match_s; -typedef struct cu_match_s cu_match_t; - -struct cu_match_value_s { - int ds_type; - value_t value; - unsigned int values_num; - latency_counter_t *latency; -}; -typedef struct cu_match_value_s cu_match_value_t; - -/* - * Prototypes - */ -/* - * NAME - * match_create_callback - * - * DESCRIPTION - * Creates a new `cu_match_t' object which will use the regular expression - * `regex' to match lines, see the `match_apply' method below. If the line - * matches, the callback passed in `callback' will be called along with the - * pointer `user_pointer'. - * The string that's passed to the callback depends on the regular expression: - * If the regular expression includes a sub-match, i. e. something like - * "value=([0-9][0-9]*)" - * then only the submatch (the part in the parenthesis) will be passed to the - * callback. If there is no submatch, then the entire string is passed to the - * callback. - * The optional `excluderegex' allows to exclude the line from the match, if - * the excluderegex matches. - * When `match_destroy' is called the `user_data' pointer is freed using - * the `free_user_data' callback - if it is not NULL. - */ -cu_match_t * -match_create_callback(const char *regex, const char *excluderegex, - int (*callback)(const char *str, char *const *matches, - size_t matches_num, void *user_data), - void *user_data, void (*free_user_data)(void *user_data)); - -/* - * NAME - * match_create_simple - * - * DESCRIPTION - * Creates a new `cu_match_t' with a default callback. The user data for that - * default callback will be a `cu_match_value_t' structure, with - * `ds_type' copied to the structure. The default callback will handle the - * string as containing a number (see strtoll(3) and strtod(3)) and store that - * number in the `value' member. How that is done depends on `ds_type': - * - * UTILS_MATCH_DS_TYPE_GAUGE - * The function will search for a floating point number in the string and - * store it in value.gauge. - * UTILS_MATCH_DS_TYPE_COUNTER_SET - * The function will search for an integer in the string and store it in - * value.counter. - * UTILS_MATCH_DS_TYPE_COUNTER_ADD - * The function will search for an integer in the string and add it to the - * value in value.counter. - * UTILS_MATCH_DS_TYPE_COUNTER_INC - * The function will not search for anything in the string and increase - * value.counter by one. - */ -cu_match_t *match_create_simple(const char *regex, const char *excluderegex, - int ds_type); - -/* - * NAME - * match_value_reset - * - * DESCRIPTION - * Resets the internal state, if applicable. This function must be called - * after each iteration for "simple" matches, usually after dispatching the - * metrics. - */ -void match_value_reset(cu_match_value_t *mv); - -/* - * NAME - * match_destroy - * - * DESCRIPTION - * Destroys the object and frees all internal resources. - */ -void match_destroy(cu_match_t *obj); - -/* - * NAME - * match_apply - * - * DESCRIPTION - * Tries to match the string `str' with the regular expression of `obj'. If - * the string matches, calls the callback in `obj' with the (sub-)match. - * - * The user_data pointer passed to `match_create_callback' is NOT freed - * automatically. The `cu_match_value_t' structure allocated by - * `match_create_callback' is freed automatically. - */ -int match_apply(cu_match_t *obj, const char *str); - -/* - * NAME - * match_get_user_data - * - * DESCRIPTION - * Returns the pointer passed to `match_create_callback' or a pointer to the - * `cu_match_value_t' structure allocated by `match_create_simple'. - */ -void *match_get_user_data(cu_match_t *obj); - -#endif /* UTILS_MATCH_H */ - -/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/daemon/utils_tail.c b/src/daemon/utils_tail.c deleted file mode 100644 index 565a224c..00000000 --- a/src/daemon/utils_tail.c +++ /dev/null @@ -1,235 +0,0 @@ -/** - * collectd - src/utils_tail.c - * Copyright (C) 2007-2008 C-Ware, Inc. - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: - * Luke Heberling - * Florian Forster - * - * Description: - * Encapsulates useful code for plugins which must watch for appends to - * the end of a file. - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_tail.h" - -struct cu_tail_s { - char *file; - FILE *fh; - struct stat stat; -}; - -static int cu_tail_reopen(cu_tail_t *obj) { - int seek_end = 0; - FILE *fh; - struct stat stat_buf = {0}; - int status; - - status = stat(obj->file, &stat_buf); - if (status != 0) { - char errbuf[1024]; - ERROR("utils_tail: stat (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); - return (-1); - } - - /* The file is already open.. */ - if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) { - /* Seek to the beginning if file was truncated */ - if (stat_buf.st_size < obj->stat.st_size) { - INFO("utils_tail: File `%s' was truncated.", obj->file); - status = fseek(obj->fh, 0, SEEK_SET); - if (status != 0) { - char errbuf[1024]; - ERROR("utils_tail: fseek (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); - fclose(obj->fh); - obj->fh = NULL; - return (-1); - } - } - memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); - return (1); - } - - /* Seek to the end if we re-open the same file again or the file opened - * is the first at all or the first after an error */ - if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino)) - seek_end = 1; - - fh = fopen(obj->file, "r"); - if (fh == NULL) { - char errbuf[1024]; - ERROR("utils_tail: fopen (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); - return (-1); - } - - if (seek_end != 0) { - status = fseek(fh, 0, SEEK_END); - if (status != 0) { - char errbuf[1024]; - ERROR("utils_tail: fseek (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); - fclose(fh); - return (-1); - } - } - - if (obj->fh != NULL) - fclose(obj->fh); - obj->fh = fh; - memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); - - return (0); -} /* int cu_tail_reopen */ - -cu_tail_t *cu_tail_create(const char *file) { - cu_tail_t *obj; - - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) - return (NULL); - - obj->file = strdup(file); - if (obj->file == NULL) { - free(obj); - return (NULL); - } - - obj->fh = NULL; - - return (obj); -} /* cu_tail_t *cu_tail_create */ - -int cu_tail_destroy(cu_tail_t *obj) { - if (obj->fh != NULL) - fclose(obj->fh); - free(obj->file); - free(obj); - - return (0); -} /* int cu_tail_destroy */ - -int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { - int status; - - if (buflen < 1) { - ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen); - return (-1); - } - - if (obj->fh == NULL) { - status = cu_tail_reopen(obj); - if (status < 0) - return (status); - } - assert(obj->fh != NULL); - - /* Try to read from the filehandle. If that succeeds, everything appears to - * be fine and we can return. */ - clearerr(obj->fh); - if (fgets(buf, buflen, obj->fh) != NULL) { - buf[buflen - 1] = 0; - return (0); - } - - /* Check if we encountered an error */ - if (ferror(obj->fh) != 0) { - /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */ - fclose(obj->fh); - obj->fh = NULL; - } - /* else: eof -> check if the file was moved away and reopen the new file if - * so.. */ - - status = cu_tail_reopen(obj); - /* error -> return with error */ - if (status < 0) - return (status); - /* file end reached and file not reopened -> nothing more to read */ - else if (status > 0) { - buf[0] = 0; - return (0); - } - - /* If we get here: file was re-opened and there may be more to read.. Let's - * try again. */ - if (fgets(buf, buflen, obj->fh) != NULL) { - buf[buflen - 1] = 0; - return (0); - } - - if (ferror(obj->fh) != 0) { - char errbuf[1024]; - WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); - fclose(obj->fh); - obj->fh = NULL; - return (-1); - } - - /* EOf, well, apparently the new file is empty.. */ - buf[0] = 0; - return (0); -} /* int cu_tail_readline */ - -int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, - void *data) { - int status; - - while (42) { - size_t len; - - status = cu_tail_readline(obj, buf, buflen); - if (status != 0) { - ERROR("utils_tail: cu_tail_read: cu_tail_readline " - "failed."); - break; - } - - /* check for EOF */ - if (buf[0] == 0) - break; - - len = strlen(buf); - while (len > 0) { - if (buf[len - 1] != '\n') - break; - buf[len - 1] = '\0'; - len--; - } - - status = callback(data, buf, buflen); - if (status != 0) { - ERROR("utils_tail: cu_tail_read: callback returned " - "status %i.", - status); - break; - } - } - - return status; -} /* int cu_tail_read */ diff --git a/src/daemon/utils_tail.h b/src/daemon/utils_tail.h deleted file mode 100644 index 73a6de21..00000000 --- a/src/daemon/utils_tail.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * collectd - src/utils_tail.h - * Copyright (C) 2007-2008 C-Ware, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: - * Luke Heberling - * - * DESCRIPTION - * Facilitates reading information that is appended to a file, taking into - * account that the file may be rotated and a new file created under the - * same name. - **/ - -#ifndef UTILS_TAIL_H -#define UTILS_TAIL_H 1 - -struct cu_tail_s; -typedef struct cu_tail_s cu_tail_t; - -typedef int tailfunc_t(void *data, char *buf, int buflen); - -/* - * NAME - * cu_tail_create - * - * DESCRIPTION - * Allocates a new tail object.. - * - * PARAMETERS - * `file' The name of the file to be tailed. - */ -cu_tail_t *cu_tail_create(const char *file); - -/* - * cu_tail_destroy - * - * Takes a tail object returned by `cu_tail_create' and destroys it, freeing - * all internal memory. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_destroy(cu_tail_t *obj); - -/* - * cu_tail_readline - * - * Reads from the file until `buflen' characters are read, a newline - * character is read, or an eof condition is encountered. `buf' is - * always null-terminated on successful return and isn't touched when non-zero - * is returned. - * - * You can check if the EOF condition is reached by looking at the buffer: If - * the length of the string stored in the buffer is zero, EOF occurred. - * Otherwise at least the newline character will be in the buffer. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen); - -/* - * cu_tail_readline - * - * Reads from the file until eof condition or an error is encountered. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, - void *data); - -#endif /* UTILS_TAIL_H */ diff --git a/src/daemon/utils_tail_match.c b/src/daemon/utils_tail_match.c deleted file mode 100644 index 505c6937..00000000 --- a/src/daemon/utils_tail_match.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * collectd - src/utils_tail_match.c - * Copyright (C) 2007-2008 C-Ware, Inc. - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: - * Luke Heberling - * Florian Forster - * - * Description: - * Encapsulates useful code to plugins which must parse a log file. - */ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_latency_config.h" -#include "utils_match.h" -#include "utils_tail.h" -#include "utils_tail_match.h" - -struct cu_tail_match_simple_s { - char plugin[DATA_MAX_NAME_LEN]; - char plugin_instance[DATA_MAX_NAME_LEN]; - char type[DATA_MAX_NAME_LEN]; - char type_instance[DATA_MAX_NAME_LEN]; - cdtime_t interval; - latency_config_t latency_config; -}; -typedef struct cu_tail_match_simple_s cu_tail_match_simple_t; - -struct cu_tail_match_match_s { - cu_match_t *match; - void *user_data; - int (*submit)(cu_match_t *match, void *user_data); - void (*free)(void *user_data); -}; -typedef struct cu_tail_match_match_s cu_tail_match_match_t; - -struct cu_tail_match_s { - int flags; - cu_tail_t *tail; - - cdtime_t interval; - cu_tail_match_match_t *matches; - size_t matches_num; -}; - -/* - * Private functions - */ -static int simple_submit_match(cu_match_t *match, void *user_data) { - cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data; - cu_match_value_t *match_value; - value_list_t vl = VALUE_LIST_INIT; - value_t values[1]; - - match_value = (cu_match_value_t *)match_get_user_data(match); - if (match_value == NULL) - return (-1); - - if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && - (match_value->values_num == 0)) - values[0].gauge = NAN; - else - values[0] = match_value->value; - - vl.values = values; - vl.values_len = 1; - sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin)); - sstrncpy(vl.plugin_instance, data->plugin_instance, - sizeof(vl.plugin_instance)); - sstrncpy(vl.type, data->type, sizeof(vl.type)); - sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance)); - - vl.interval = data->interval; - plugin_dispatch_values(&vl); - - match_value_reset(match_value); - return (0); -} /* int simple_submit_match */ - -static int latency_submit_match(cu_match_t *match, void *user_data) { - cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data; - cu_match_value_t *match_value; - value_list_t vl = VALUE_LIST_INIT; - - match_value = (cu_match_value_t *)match_get_user_data(match); - if (match_value == NULL) - return (-1); - - sstrncpy(vl.host, hostname_g, sizeof(vl.host)); - sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin)); - sstrncpy(vl.plugin_instance, data->plugin_instance, - sizeof(vl.plugin_instance)); - vl.interval = data->interval; - vl.time = cdtime(); - - /* Submit percentiles */ - sstrncpy(vl.type, data->type, sizeof(vl.type)); - for (size_t i = 0; i < data->latency_config.percentile_num; i++) { - if (strlen(data->type_instance) != 0) - ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%.0f", - data->type_instance, data->latency_config.percentile[i]); - else - ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%.0f", - data->latency_config.percentile[i]); - - vl.values = &(value_t){ - .gauge = - (match_value->values_num != 0) - ? CDTIME_T_TO_DOUBLE(latency_counter_get_percentile( - match_value->latency, data->latency_config.percentile[i])) - : NAN, - }; - vl.values_len = 1; - - plugin_dispatch_values(&vl); - } - - /* Submit buckets */ - sstrncpy(vl.type, "bucket", sizeof(vl.type)); - for (size_t i = 0; i < data->latency_config.buckets_num; i++) { - latency_bucket_t bucket = data->latency_config.buckets[i]; - - double lower_bound = CDTIME_T_TO_DOUBLE(bucket.lower_bound); - double upper_bound = - bucket.upper_bound ? CDTIME_T_TO_DOUBLE(bucket.upper_bound) : INFINITY; - - if (strlen(data->type_instance) != 0) - ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s-%g_%g", - data->type, data->type_instance, lower_bound, upper_bound); - else - ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%g_%g", - data->type, lower_bound, upper_bound); - - vl.values = &(value_t){ - .gauge = - latency_counter_get_rate(match_value->latency, bucket.lower_bound, - bucket.upper_bound, vl.time), - }; - vl.values_len = 1; - - plugin_dispatch_values(&vl); - } - - match_value->value.gauge = NAN; - match_value->values_num = 0; - latency_counter_reset(match_value->latency); - - return (0); -} /* int latency_submit_match */ - -static int tail_callback(void *data, char *buf, - int __attribute__((unused)) buflen) { - cu_tail_match_t *obj = (cu_tail_match_t *)data; - - for (size_t i = 0; i < obj->matches_num; i++) - match_apply(obj->matches[i].match, buf); - - return (0); -} /* int tail_callback */ - -static void tail_match_simple_free(void *data) { - cu_tail_match_simple_t *user_data = (cu_tail_match_simple_t *)data; - latency_config_free(user_data->latency_config); - sfree(user_data); -} /* void tail_match_simple_free */ - -/* - * Public functions - */ -cu_tail_match_t *tail_match_create(const char *filename) { - cu_tail_match_t *obj; - - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) - return (NULL); - - obj->tail = cu_tail_create(filename); - if (obj->tail == NULL) { - sfree(obj); - return (NULL); - } - - return (obj); -} /* cu_tail_match_t *tail_match_create */ - -void tail_match_destroy(cu_tail_match_t *obj) { - if (obj == NULL) - return; - - if (obj->tail != NULL) { - cu_tail_destroy(obj->tail); - obj->tail = NULL; - } - - for (size_t i = 0; i < obj->matches_num; i++) { - cu_tail_match_match_t *match = obj->matches + i; - if (match->match != NULL) { - match_destroy(match->match); - match->match = NULL; - } - - if ((match->user_data != NULL) && (match->free != NULL)) - (*match->free)(match->user_data); - match->user_data = NULL; - } - - sfree(obj->matches); - sfree(obj); -} /* void tail_match_destroy */ - -int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, - int (*submit_match)(cu_match_t *match, - void *user_data), - void *user_data, - void (*free_user_data)(void *user_data)) { - cu_tail_match_match_t *temp; - - temp = realloc(obj->matches, - sizeof(cu_tail_match_match_t) * (obj->matches_num + 1)); - if (temp == NULL) - return (-1); - - obj->matches = temp; - obj->matches_num++; - - DEBUG("tail_match_add_match interval %lf", - CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval)); - temp = obj->matches + (obj->matches_num - 1); - - temp->match = match; - temp->user_data = user_data; - temp->submit = submit_match; - temp->free = free_user_data; - - return (0); -} /* int tail_match_add_match */ - -int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, - const char *excluderegex, int ds_type, - const char *plugin, const char *plugin_instance, - const char *type, const char *type_instance, - const latency_config_t latency_cfg, - const cdtime_t interval) { - cu_match_t *match; - cu_tail_match_simple_t *user_data; - int status; - - match = match_create_simple(regex, excluderegex, ds_type); - if (match == NULL) - return (-1); - - user_data = calloc(1, sizeof(*user_data)); - if (user_data == NULL) { - match_destroy(match); - return (-1); - } - - sstrncpy(user_data->plugin, plugin, sizeof(user_data->plugin)); - if (plugin_instance != NULL) - sstrncpy(user_data->plugin_instance, plugin_instance, - sizeof(user_data->plugin_instance)); - - sstrncpy(user_data->type, type, sizeof(user_data->type)); - if (type_instance != NULL) - sstrncpy(user_data->type_instance, type_instance, - sizeof(user_data->type_instance)); - - user_data->interval = interval; - - if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && - (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { - status = latency_config_copy(&user_data->latency_config, latency_cfg); - if (status != 0) { - ERROR("tail_match_add_match_simple: latency_config_copy() failed."); - status = -1; - goto out; - } - - status = tail_match_add_match(obj, match, latency_submit_match, user_data, - tail_match_simple_free); - } else { - status = - tail_match_add_match(obj, match, simple_submit_match, user_data, free); - } - -out: - if (status != 0) { - tail_match_simple_free(user_data); - match_destroy(match); - } - - return (status); -} /* int tail_match_add_match_simple */ - -int tail_match_read(cu_tail_match_t *obj) { - char buffer[4096]; - int status; - - status = cu_tail_read(obj->tail, buffer, sizeof(buffer), tail_callback, - (void *)obj); - if (status != 0) { - ERROR("tail_match: cu_tail_read failed."); - return (status); - } - - for (size_t i = 0; i < obj->matches_num; i++) { - cu_tail_match_match_t *lt_match = obj->matches + i; - - if (lt_match->submit == NULL) - continue; - - (*lt_match->submit)(lt_match->match, lt_match->user_data); - } - - return (0); -} /* int tail_match_read */ - -/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/daemon/utils_tail_match.h b/src/daemon/utils_tail_match.h deleted file mode 100644 index 09e3d402..00000000 --- a/src/daemon/utils_tail_match.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * collectd - src/utils_tail_match.h - * Copyright (C) 2007-2008 C-Ware, Inc. - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Luke Heberling - * Florian Forster - * - * Description: - * `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to - * match it using several regular expressions. Matches are then passed to - * user-provided callback functions or default handlers. This should keep all - * of the parsing logic out of the actual plugin, which only operate with - * regular expressions. - */ - -#include "utils_latency_config.h" -#include "utils_match.h" - -struct cu_tail_match_s; -typedef struct cu_tail_match_s cu_tail_match_t; - -/* - * NAME - * tail_match_create - * - * DESCRIPTION - * Allocates, initializes and returns a new `cu_tail_match_t' object. - * - * PARAMETERS - * `filename' The name to read data from. - * - * RETURN VALUE - * Returns NULL upon failure, non-NULL otherwise. - */ -cu_tail_match_t *tail_match_create(const char *filename); - -/* - * NAME - * tail_match_destroy - * - * DESCRIPTION - * Releases resources used by the `cu_tail_match_t' object. - * - * PARAMETERS - * The object to destroy. - */ -void tail_match_destroy(cu_tail_match_t *obj); - -/* - * NAME - * tail_match_add_match - * - * DESCRIPTION - * Adds a match, in form of a `cu_match_t' object, to the object. - * After data has been read from the logfile (using utils_tail) the callback - * function `submit_match' is called with the match object and the user - * supplied data. - * Please note that his function is called regardless whether this match - * matched any lines recently or not. - * When `tail_match_destroy' is called the `user_data' pointer is freed using - * the `free_user_data' callback - if it is not NULL. - * When using this interface the `tail_match' module doesn't dispatch any - * values - * itself - all that has to happen in either the match-callbacks or the - * submit_match callback. - * - * RETURN VALUE - * Zero upon success, non-zero otherwise. - */ -int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, - int (*submit_match)(cu_match_t *match, - void *user_data), - void *user_data, - void (*free_user_data)(void *user_data)); - -/* - * NAME - * tail_match_add_match_simple - * - * DESCRIPTION - * A simplified version of `tail_match_add_match'. The regular expressen - * `regex' - * must match a number, which is then dispatched according to `ds_type'. See - * the `match_create_simple' function in utils_match.h for a description how - * this flag effects calculation of a new value. - * The values gathered are dispatched by the tail_match module in this case. - * The - * passed `plugin', `plugin_instance', `type', and `type_instance' are - * directly used when submitting these values. - * With excluderegex it is possible to exlude lines from the match. - * The `latency_cfg' specifies configuration for submitting latency. - * - * RETURN VALUE - * Zero upon success, non-zero otherwise. - */ -int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, - const char *excluderegex, int ds_type, - const char *plugin, const char *plugin_instance, - const char *type, const char *type_instance, - const latency_config_t latency_cfg, - const cdtime_t interval); - -/* - * NAME - * tail_match_read - * - * DESCRIPTION - * This function should be called periodically by plugins. It reads new lines - * from the logfile using `utils_tail' and tries to match them using all - * added `utils_match' objects. - * After all lines have been read and processed, the submit_match callback is - * called or, in case of tail_match_add_match_simple, the data is dispatched - * to - * the daemon directly. - * - * RETURN VALUE - * Zero on success, nonzero on failure. -*/ -int tail_match_read(cu_tail_match_t *obj); - -/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/utils_match.c b/src/utils_match.c new file mode 100644 index 00000000..2e487b59 --- /dev/null +++ b/src/utils_match.c @@ -0,0 +1,372 @@ +/** + * collectd - src/utils_match.c + * Copyright (C) 2008-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" + +#include "utils_match.h" + +#include + +#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02 + +struct cu_match_s { + regex_t regex; + regex_t excluderegex; + int flags; + + int (*callback)(const char *str, char *const *matches, size_t matches_num, + void *user_data); + void *user_data; + void (*free)(void *user_data); +}; + +/* + * Private functions + */ +static char *match_substr(const char *str, int begin, int end) { + char *ret; + size_t ret_len; + + if ((begin < 0) || (end < 0) || (begin >= end)) + return (NULL); + if ((size_t)end > (strlen(str) + 1)) { + ERROR("utils_match: match_substr: `end' points after end of string."); + return (NULL); + } + + ret_len = end - begin; + ret = malloc(ret_len + 1); + if (ret == NULL) { + ERROR("utils_match: match_substr: malloc failed."); + return (NULL); + } + + sstrncpy(ret, str + begin, ret_len + 1); + return (ret); +} /* char *match_substr */ + +static int default_callback(const char __attribute__((unused)) * str, + char *const *matches, size_t matches_num, + void *user_data) { + cu_match_value_t *data = (cu_match_value_t *)user_data; + + if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) { + gauge_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) { + data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1; + data->values_num++; + return (0); + } + + if (matches_num < 2) + return (-1); + + value = (gauge_t)strtod(matches[1], &endptr); + if (matches[1] == endptr) + return (-1); + + if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) { + latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value)); + data->values_num++; + return (0); + } + + if ((data->values_num == 0) || + (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) || + (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) { + double f = ((double)data->values_num) / ((double)(data->values_num + 1)); + data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f)); + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) { + if (data->value.gauge > value) + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) { + if (data->value.gauge < value) + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) { + data->value.gauge += value; + } else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return (-1); + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) { + counter_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) { + data->value.counter++; + data->values_num++; + return (0); + } + + if (matches_num < 2) + return (-1); + + value = (counter_t)strtoull(matches[1], &endptr, 0); + if (matches[1] == endptr) + return (-1); + + if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET) + data->value.counter = value; + else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD) + data->value.counter += value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return (-1); + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) { + derive_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) { + data->value.derive++; + data->values_num++; + return (0); + } + + if (matches_num < 2) + return (-1); + + value = (derive_t)strtoll(matches[1], &endptr, 0); + if (matches[1] == endptr) + return (-1); + + if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET) + data->value.derive = value; + else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD) + data->value.derive += value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return (-1); + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) { + absolute_t value; + char *endptr = NULL; + + if (matches_num < 2) + return (-1); + + value = (absolute_t)strtoull(matches[1], &endptr, 0); + if (matches[1] == endptr) + return (-1); + + if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET) + data->value.absolute = value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return (-1); + } + + data->values_num++; + } else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return (-1); + } + + return (0); +} /* int default_callback */ + +static void match_simple_free(void *data) { + cu_match_value_t *user_data = (cu_match_value_t *)data; + if (user_data->latency) + latency_counter_destroy(user_data->latency); + + free(data); +} /* void match_simple_free */ + +/* + * Public functions + */ +cu_match_t * +match_create_callback(const char *regex, const char *excluderegex, + int (*callback)(const char *str, char *const *matches, + size_t matches_num, void *user_data), + void *user_data, + void (*free_user_data)(void *user_data)) { + cu_match_t *obj; + int status; + + DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s", + regex, excluderegex); + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) + return (NULL); + + status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE); + if (status != 0) { + ERROR("Compiling the regular expression \"%s\" failed.", regex); + sfree(obj); + return (NULL); + } + + if (excluderegex && strcmp(excluderegex, "") != 0) { + status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED); + if (status != 0) { + ERROR("Compiling the excluding regular expression \"%s\" failed.", + excluderegex); + sfree(obj); + return (NULL); + } + obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX; + } + + obj->callback = callback; + obj->user_data = user_data; + obj->free = free_user_data; + + return (obj); +} /* cu_match_t *match_create_callback */ + +cu_match_t *match_create_simple(const char *regex, const char *excluderegex, + int match_ds_type) { + cu_match_value_t *user_data; + cu_match_t *obj; + + user_data = calloc(1, sizeof(*user_data)); + if (user_data == NULL) + return (NULL); + user_data->ds_type = match_ds_type; + + if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && + (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { + user_data->latency = latency_counter_create(); + if (user_data->latency == NULL) { + ERROR("match_create_simple(): latency_counter_create() failed."); + free(user_data); + return (NULL); + } + } + + obj = match_create_callback(regex, excluderegex, default_callback, user_data, + match_simple_free); + if (obj == NULL) { + if (user_data->latency) + latency_counter_destroy(user_data->latency); + + sfree(user_data); + return (NULL); + } + return (obj); +} /* cu_match_t *match_create_simple */ + +void match_value_reset(cu_match_value_t *mv) { + if (mv == NULL) + return; + + /* 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->values_num = 0; + } +} /* }}} void match_value_reset */ + +void match_destroy(cu_match_t *obj) { + if (obj == NULL) + return; + + if ((obj->user_data != NULL) && (obj->free != NULL)) + (*obj->free)(obj->user_data); + + sfree(obj); +} /* void match_destroy */ + +int match_apply(cu_match_t *obj, const char *str) { + int status; + regmatch_t re_match[32]; + char *matches[32] = {0}; + size_t matches_num; + + if ((obj == NULL) || (str == NULL)) + return (-1); + + if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) { + status = + regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match, + /* eflags = */ 0); + /* Regex did match, so exclude this line */ + if (status == 0) { + DEBUG("ExludeRegex matched, don't count that line\n"); + return (0); + } + } + + status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match, + /* eflags = */ 0); + + /* Regex did not match */ + if (status != 0) + return (0); + + for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches); + matches_num++) { + if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0)) + break; + + matches[matches_num] = match_substr(str, re_match[matches_num].rm_so, + re_match[matches_num].rm_eo); + if (matches[matches_num] == NULL) { + status = -1; + break; + } + } + + if (status != 0) { + ERROR("utils_match: match_apply: match_substr failed."); + } else { + status = obj->callback(str, matches, matches_num, obj->user_data); + if (status != 0) { + ERROR("utils_match: match_apply: callback failed."); + } + } + + for (size_t i = 0; i < matches_num; i++) { + sfree(matches[i]); + } + + return (status); +} /* int match_apply */ + +void *match_get_user_data(cu_match_t *obj) { + if (obj == NULL) + return (NULL); + return (obj->user_data); +} /* void *match_get_user_data */ + +/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/utils_match.h b/src/utils_match.h new file mode 100644 index 00000000..1383530c --- /dev/null +++ b/src/utils_match.h @@ -0,0 +1,181 @@ +/** + * collectd - src/utils_match.h + * Copyright (C) 2008-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_MATCH_H +#define UTILS_MATCH_H 1 + +#include "plugin.h" +#include "utils_latency.h" + +/* + * Each type may have 12 sub-types + * 0x1000 = 1000000000000 + * ^ <- Type bit + * ^^^^^^^^^^^^ <- Subtype bits + */ +#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000 +#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000 +#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000 +#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000 + +#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01 +#define UTILS_MATCH_CF_GAUGE_MIN 0x02 +#define UTILS_MATCH_CF_GAUGE_MAX 0x04 +#define UTILS_MATCH_CF_GAUGE_LAST 0x08 +#define UTILS_MATCH_CF_GAUGE_INC 0x10 +#define UTILS_MATCH_CF_GAUGE_ADD 0x20 +#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40 +#define UTILS_MATCH_CF_GAUGE_DIST 0x80 + +#define UTILS_MATCH_CF_COUNTER_SET 0x01 +#define UTILS_MATCH_CF_COUNTER_ADD 0x02 +#define UTILS_MATCH_CF_COUNTER_INC 0x04 + +#define UTILS_MATCH_CF_DERIVE_SET 0x01 +#define UTILS_MATCH_CF_DERIVE_ADD 0x02 +#define UTILS_MATCH_CF_DERIVE_INC 0x04 + +#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01 +#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02 +#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04 + +/* + * Data types + */ +struct cu_match_s; +typedef struct cu_match_s cu_match_t; + +struct cu_match_value_s { + int ds_type; + value_t value; + unsigned int values_num; + latency_counter_t *latency; +}; +typedef struct cu_match_value_s cu_match_value_t; + +/* + * Prototypes + */ +/* + * NAME + * match_create_callback + * + * DESCRIPTION + * Creates a new `cu_match_t' object which will use the regular expression + * `regex' to match lines, see the `match_apply' method below. If the line + * matches, the callback passed in `callback' will be called along with the + * pointer `user_pointer'. + * The string that's passed to the callback depends on the regular expression: + * If the regular expression includes a sub-match, i. e. something like + * "value=([0-9][0-9]*)" + * then only the submatch (the part in the parenthesis) will be passed to the + * callback. If there is no submatch, then the entire string is passed to the + * callback. + * The optional `excluderegex' allows to exclude the line from the match, if + * the excluderegex matches. + * When `match_destroy' is called the `user_data' pointer is freed using + * the `free_user_data' callback - if it is not NULL. + */ +cu_match_t * +match_create_callback(const char *regex, const char *excluderegex, + int (*callback)(const char *str, char *const *matches, + size_t matches_num, void *user_data), + void *user_data, void (*free_user_data)(void *user_data)); + +/* + * NAME + * match_create_simple + * + * DESCRIPTION + * Creates a new `cu_match_t' with a default callback. The user data for that + * default callback will be a `cu_match_value_t' structure, with + * `ds_type' copied to the structure. The default callback will handle the + * string as containing a number (see strtoll(3) and strtod(3)) and store that + * number in the `value' member. How that is done depends on `ds_type': + * + * UTILS_MATCH_DS_TYPE_GAUGE + * The function will search for a floating point number in the string and + * store it in value.gauge. + * UTILS_MATCH_DS_TYPE_COUNTER_SET + * The function will search for an integer in the string and store it in + * value.counter. + * UTILS_MATCH_DS_TYPE_COUNTER_ADD + * The function will search for an integer in the string and add it to the + * value in value.counter. + * UTILS_MATCH_DS_TYPE_COUNTER_INC + * The function will not search for anything in the string and increase + * value.counter by one. + */ +cu_match_t *match_create_simple(const char *regex, const char *excluderegex, + int ds_type); + +/* + * NAME + * match_value_reset + * + * DESCRIPTION + * Resets the internal state, if applicable. This function must be called + * after each iteration for "simple" matches, usually after dispatching the + * metrics. + */ +void match_value_reset(cu_match_value_t *mv); + +/* + * NAME + * match_destroy + * + * DESCRIPTION + * Destroys the object and frees all internal resources. + */ +void match_destroy(cu_match_t *obj); + +/* + * NAME + * match_apply + * + * DESCRIPTION + * Tries to match the string `str' with the regular expression of `obj'. If + * the string matches, calls the callback in `obj' with the (sub-)match. + * + * The user_data pointer passed to `match_create_callback' is NOT freed + * automatically. The `cu_match_value_t' structure allocated by + * `match_create_callback' is freed automatically. + */ +int match_apply(cu_match_t *obj, const char *str); + +/* + * NAME + * match_get_user_data + * + * DESCRIPTION + * Returns the pointer passed to `match_create_callback' or a pointer to the + * `cu_match_value_t' structure allocated by `match_create_simple'. + */ +void *match_get_user_data(cu_match_t *obj); + +#endif /* UTILS_MATCH_H */ + +/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/utils_tail.c b/src/utils_tail.c new file mode 100644 index 00000000..565a224c --- /dev/null +++ b/src/utils_tail.c @@ -0,0 +1,235 @@ +/** + * collectd - src/utils_tail.c + * Copyright (C) 2007-2008 C-Ware, Inc. + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: + * Luke Heberling + * Florian Forster + * + * Description: + * Encapsulates useful code for plugins which must watch for appends to + * the end of a file. + **/ + +#include "collectd.h" + +#include "common.h" +#include "utils_tail.h" + +struct cu_tail_s { + char *file; + FILE *fh; + struct stat stat; +}; + +static int cu_tail_reopen(cu_tail_t *obj) { + int seek_end = 0; + FILE *fh; + struct stat stat_buf = {0}; + int status; + + status = stat(obj->file, &stat_buf); + if (status != 0) { + char errbuf[1024]; + ERROR("utils_tail: stat (%s) failed: %s", obj->file, + sstrerror(errno, errbuf, sizeof(errbuf))); + return (-1); + } + + /* The file is already open.. */ + if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) { + /* Seek to the beginning if file was truncated */ + if (stat_buf.st_size < obj->stat.st_size) { + INFO("utils_tail: File `%s' was truncated.", obj->file); + status = fseek(obj->fh, 0, SEEK_SET); + if (status != 0) { + char errbuf[1024]; + ERROR("utils_tail: fseek (%s) failed: %s", obj->file, + sstrerror(errno, errbuf, sizeof(errbuf))); + fclose(obj->fh); + obj->fh = NULL; + return (-1); + } + } + memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); + return (1); + } + + /* Seek to the end if we re-open the same file again or the file opened + * is the first at all or the first after an error */ + if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino)) + seek_end = 1; + + fh = fopen(obj->file, "r"); + if (fh == NULL) { + char errbuf[1024]; + ERROR("utils_tail: fopen (%s) failed: %s", obj->file, + sstrerror(errno, errbuf, sizeof(errbuf))); + return (-1); + } + + if (seek_end != 0) { + status = fseek(fh, 0, SEEK_END); + if (status != 0) { + char errbuf[1024]; + ERROR("utils_tail: fseek (%s) failed: %s", obj->file, + sstrerror(errno, errbuf, sizeof(errbuf))); + fclose(fh); + return (-1); + } + } + + if (obj->fh != NULL) + fclose(obj->fh); + obj->fh = fh; + memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); + + return (0); +} /* int cu_tail_reopen */ + +cu_tail_t *cu_tail_create(const char *file) { + cu_tail_t *obj; + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) + return (NULL); + + obj->file = strdup(file); + if (obj->file == NULL) { + free(obj); + return (NULL); + } + + obj->fh = NULL; + + return (obj); +} /* cu_tail_t *cu_tail_create */ + +int cu_tail_destroy(cu_tail_t *obj) { + if (obj->fh != NULL) + fclose(obj->fh); + free(obj->file); + free(obj); + + return (0); +} /* int cu_tail_destroy */ + +int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { + int status; + + if (buflen < 1) { + ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen); + return (-1); + } + + if (obj->fh == NULL) { + status = cu_tail_reopen(obj); + if (status < 0) + return (status); + } + assert(obj->fh != NULL); + + /* Try to read from the filehandle. If that succeeds, everything appears to + * be fine and we can return. */ + clearerr(obj->fh); + if (fgets(buf, buflen, obj->fh) != NULL) { + buf[buflen - 1] = 0; + return (0); + } + + /* Check if we encountered an error */ + if (ferror(obj->fh) != 0) { + /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */ + fclose(obj->fh); + obj->fh = NULL; + } + /* else: eof -> check if the file was moved away and reopen the new file if + * so.. */ + + status = cu_tail_reopen(obj); + /* error -> return with error */ + if (status < 0) + return (status); + /* file end reached and file not reopened -> nothing more to read */ + else if (status > 0) { + buf[0] = 0; + return (0); + } + + /* If we get here: file was re-opened and there may be more to read.. Let's + * try again. */ + if (fgets(buf, buflen, obj->fh) != NULL) { + buf[buflen - 1] = 0; + return (0); + } + + if (ferror(obj->fh) != 0) { + char errbuf[1024]; + WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file, + sstrerror(errno, errbuf, sizeof(errbuf))); + fclose(obj->fh); + obj->fh = NULL; + return (-1); + } + + /* EOf, well, apparently the new file is empty.. */ + buf[0] = 0; + return (0); +} /* int cu_tail_readline */ + +int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, + void *data) { + int status; + + while (42) { + size_t len; + + status = cu_tail_readline(obj, buf, buflen); + if (status != 0) { + ERROR("utils_tail: cu_tail_read: cu_tail_readline " + "failed."); + break; + } + + /* check for EOF */ + if (buf[0] == 0) + break; + + len = strlen(buf); + while (len > 0) { + if (buf[len - 1] != '\n') + break; + buf[len - 1] = '\0'; + len--; + } + + status = callback(data, buf, buflen); + if (status != 0) { + ERROR("utils_tail: cu_tail_read: callback returned " + "status %i.", + status); + break; + } + } + + return status; +} /* int cu_tail_read */ diff --git a/src/utils_tail.h b/src/utils_tail.h new file mode 100644 index 00000000..73a6de21 --- /dev/null +++ b/src/utils_tail.h @@ -0,0 +1,88 @@ +/** + * collectd - src/utils_tail.h + * Copyright (C) 2007-2008 C-Ware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: + * Luke Heberling + * + * DESCRIPTION + * Facilitates reading information that is appended to a file, taking into + * account that the file may be rotated and a new file created under the + * same name. + **/ + +#ifndef UTILS_TAIL_H +#define UTILS_TAIL_H 1 + +struct cu_tail_s; +typedef struct cu_tail_s cu_tail_t; + +typedef int tailfunc_t(void *data, char *buf, int buflen); + +/* + * NAME + * cu_tail_create + * + * DESCRIPTION + * Allocates a new tail object.. + * + * PARAMETERS + * `file' The name of the file to be tailed. + */ +cu_tail_t *cu_tail_create(const char *file); + +/* + * cu_tail_destroy + * + * Takes a tail object returned by `cu_tail_create' and destroys it, freeing + * all internal memory. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_destroy(cu_tail_t *obj); + +/* + * cu_tail_readline + * + * Reads from the file until `buflen' characters are read, a newline + * character is read, or an eof condition is encountered. `buf' is + * always null-terminated on successful return and isn't touched when non-zero + * is returned. + * + * You can check if the EOF condition is reached by looking at the buffer: If + * the length of the string stored in the buffer is zero, EOF occurred. + * Otherwise at least the newline character will be in the buffer. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen); + +/* + * cu_tail_readline + * + * Reads from the file until eof condition or an error is encountered. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, + void *data); + +#endif /* UTILS_TAIL_H */ diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c new file mode 100644 index 00000000..505c6937 --- /dev/null +++ b/src/utils_tail_match.c @@ -0,0 +1,340 @@ +/* + * collectd - src/utils_tail_match.c + * Copyright (C) 2007-2008 C-Ware, Inc. + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: + * Luke Heberling + * Florian Forster + * + * Description: + * Encapsulates useful code to plugins which must parse a log file. + */ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" +#include "utils_latency_config.h" +#include "utils_match.h" +#include "utils_tail.h" +#include "utils_tail_match.h" + +struct cu_tail_match_simple_s { + char plugin[DATA_MAX_NAME_LEN]; + char plugin_instance[DATA_MAX_NAME_LEN]; + char type[DATA_MAX_NAME_LEN]; + char type_instance[DATA_MAX_NAME_LEN]; + cdtime_t interval; + latency_config_t latency_config; +}; +typedef struct cu_tail_match_simple_s cu_tail_match_simple_t; + +struct cu_tail_match_match_s { + cu_match_t *match; + void *user_data; + int (*submit)(cu_match_t *match, void *user_data); + void (*free)(void *user_data); +}; +typedef struct cu_tail_match_match_s cu_tail_match_match_t; + +struct cu_tail_match_s { + int flags; + cu_tail_t *tail; + + cdtime_t interval; + cu_tail_match_match_t *matches; + size_t matches_num; +}; + +/* + * Private functions + */ +static int simple_submit_match(cu_match_t *match, void *user_data) { + cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data; + cu_match_value_t *match_value; + value_list_t vl = VALUE_LIST_INIT; + value_t values[1]; + + match_value = (cu_match_value_t *)match_get_user_data(match); + if (match_value == NULL) + return (-1); + + if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && + (match_value->values_num == 0)) + values[0].gauge = NAN; + else + values[0] = match_value->value; + + vl.values = values; + vl.values_len = 1; + sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin)); + sstrncpy(vl.plugin_instance, data->plugin_instance, + sizeof(vl.plugin_instance)); + sstrncpy(vl.type, data->type, sizeof(vl.type)); + sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance)); + + vl.interval = data->interval; + plugin_dispatch_values(&vl); + + match_value_reset(match_value); + return (0); +} /* int simple_submit_match */ + +static int latency_submit_match(cu_match_t *match, void *user_data) { + cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data; + cu_match_value_t *match_value; + value_list_t vl = VALUE_LIST_INIT; + + match_value = (cu_match_value_t *)match_get_user_data(match); + if (match_value == NULL) + return (-1); + + sstrncpy(vl.host, hostname_g, sizeof(vl.host)); + sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin)); + sstrncpy(vl.plugin_instance, data->plugin_instance, + sizeof(vl.plugin_instance)); + vl.interval = data->interval; + vl.time = cdtime(); + + /* Submit percentiles */ + sstrncpy(vl.type, data->type, sizeof(vl.type)); + for (size_t i = 0; i < data->latency_config.percentile_num; i++) { + if (strlen(data->type_instance) != 0) + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%.0f", + data->type_instance, data->latency_config.percentile[i]); + else + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%.0f", + data->latency_config.percentile[i]); + + vl.values = &(value_t){ + .gauge = + (match_value->values_num != 0) + ? CDTIME_T_TO_DOUBLE(latency_counter_get_percentile( + match_value->latency, data->latency_config.percentile[i])) + : NAN, + }; + vl.values_len = 1; + + plugin_dispatch_values(&vl); + } + + /* Submit buckets */ + sstrncpy(vl.type, "bucket", sizeof(vl.type)); + for (size_t i = 0; i < data->latency_config.buckets_num; i++) { + latency_bucket_t bucket = data->latency_config.buckets[i]; + + double lower_bound = CDTIME_T_TO_DOUBLE(bucket.lower_bound); + double upper_bound = + bucket.upper_bound ? CDTIME_T_TO_DOUBLE(bucket.upper_bound) : INFINITY; + + if (strlen(data->type_instance) != 0) + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s-%g_%g", + data->type, data->type_instance, lower_bound, upper_bound); + else + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%g_%g", + data->type, lower_bound, upper_bound); + + vl.values = &(value_t){ + .gauge = + latency_counter_get_rate(match_value->latency, bucket.lower_bound, + bucket.upper_bound, vl.time), + }; + vl.values_len = 1; + + plugin_dispatch_values(&vl); + } + + match_value->value.gauge = NAN; + match_value->values_num = 0; + latency_counter_reset(match_value->latency); + + return (0); +} /* int latency_submit_match */ + +static int tail_callback(void *data, char *buf, + int __attribute__((unused)) buflen) { + cu_tail_match_t *obj = (cu_tail_match_t *)data; + + for (size_t i = 0; i < obj->matches_num; i++) + match_apply(obj->matches[i].match, buf); + + return (0); +} /* int tail_callback */ + +static void tail_match_simple_free(void *data) { + cu_tail_match_simple_t *user_data = (cu_tail_match_simple_t *)data; + latency_config_free(user_data->latency_config); + sfree(user_data); +} /* void tail_match_simple_free */ + +/* + * Public functions + */ +cu_tail_match_t *tail_match_create(const char *filename) { + cu_tail_match_t *obj; + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) + return (NULL); + + obj->tail = cu_tail_create(filename); + if (obj->tail == NULL) { + sfree(obj); + return (NULL); + } + + return (obj); +} /* cu_tail_match_t *tail_match_create */ + +void tail_match_destroy(cu_tail_match_t *obj) { + if (obj == NULL) + return; + + if (obj->tail != NULL) { + cu_tail_destroy(obj->tail); + obj->tail = NULL; + } + + for (size_t i = 0; i < obj->matches_num; i++) { + cu_tail_match_match_t *match = obj->matches + i; + if (match->match != NULL) { + match_destroy(match->match); + match->match = NULL; + } + + if ((match->user_data != NULL) && (match->free != NULL)) + (*match->free)(match->user_data); + match->user_data = NULL; + } + + sfree(obj->matches); + sfree(obj); +} /* void tail_match_destroy */ + +int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, + int (*submit_match)(cu_match_t *match, + void *user_data), + void *user_data, + void (*free_user_data)(void *user_data)) { + cu_tail_match_match_t *temp; + + temp = realloc(obj->matches, + sizeof(cu_tail_match_match_t) * (obj->matches_num + 1)); + if (temp == NULL) + return (-1); + + obj->matches = temp; + obj->matches_num++; + + DEBUG("tail_match_add_match interval %lf", + CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval)); + temp = obj->matches + (obj->matches_num - 1); + + temp->match = match; + temp->user_data = user_data; + temp->submit = submit_match; + temp->free = free_user_data; + + return (0); +} /* int tail_match_add_match */ + +int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, + const char *excluderegex, int ds_type, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance, + const latency_config_t latency_cfg, + const cdtime_t interval) { + cu_match_t *match; + cu_tail_match_simple_t *user_data; + int status; + + match = match_create_simple(regex, excluderegex, ds_type); + if (match == NULL) + return (-1); + + user_data = calloc(1, sizeof(*user_data)); + if (user_data == NULL) { + match_destroy(match); + return (-1); + } + + sstrncpy(user_data->plugin, plugin, sizeof(user_data->plugin)); + if (plugin_instance != NULL) + sstrncpy(user_data->plugin_instance, plugin_instance, + sizeof(user_data->plugin_instance)); + + sstrncpy(user_data->type, type, sizeof(user_data->type)); + if (type_instance != NULL) + sstrncpy(user_data->type_instance, type_instance, + sizeof(user_data->type_instance)); + + user_data->interval = interval; + + if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && + (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { + status = latency_config_copy(&user_data->latency_config, latency_cfg); + if (status != 0) { + ERROR("tail_match_add_match_simple: latency_config_copy() failed."); + status = -1; + goto out; + } + + status = tail_match_add_match(obj, match, latency_submit_match, user_data, + tail_match_simple_free); + } else { + status = + tail_match_add_match(obj, match, simple_submit_match, user_data, free); + } + +out: + if (status != 0) { + tail_match_simple_free(user_data); + match_destroy(match); + } + + return (status); +} /* int tail_match_add_match_simple */ + +int tail_match_read(cu_tail_match_t *obj) { + char buffer[4096]; + int status; + + status = cu_tail_read(obj->tail, buffer, sizeof(buffer), tail_callback, + (void *)obj); + if (status != 0) { + ERROR("tail_match: cu_tail_read failed."); + return (status); + } + + for (size_t i = 0; i < obj->matches_num; i++) { + cu_tail_match_match_t *lt_match = obj->matches + i; + + if (lt_match->submit == NULL) + continue; + + (*lt_match->submit)(lt_match->match, lt_match->user_data); + } + + return (0); +} /* int tail_match_read */ + +/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h new file mode 100644 index 00000000..09e3d402 --- /dev/null +++ b/src/utils_tail_match.h @@ -0,0 +1,141 @@ +/* + * collectd - src/utils_tail_match.h + * Copyright (C) 2007-2008 C-Ware, Inc. + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Luke Heberling + * Florian Forster + * + * Description: + * `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to + * match it using several regular expressions. Matches are then passed to + * user-provided callback functions or default handlers. This should keep all + * of the parsing logic out of the actual plugin, which only operate with + * regular expressions. + */ + +#include "utils_latency_config.h" +#include "utils_match.h" + +struct cu_tail_match_s; +typedef struct cu_tail_match_s cu_tail_match_t; + +/* + * NAME + * tail_match_create + * + * DESCRIPTION + * Allocates, initializes and returns a new `cu_tail_match_t' object. + * + * PARAMETERS + * `filename' The name to read data from. + * + * RETURN VALUE + * Returns NULL upon failure, non-NULL otherwise. + */ +cu_tail_match_t *tail_match_create(const char *filename); + +/* + * NAME + * tail_match_destroy + * + * DESCRIPTION + * Releases resources used by the `cu_tail_match_t' object. + * + * PARAMETERS + * The object to destroy. + */ +void tail_match_destroy(cu_tail_match_t *obj); + +/* + * NAME + * tail_match_add_match + * + * DESCRIPTION + * Adds a match, in form of a `cu_match_t' object, to the object. + * After data has been read from the logfile (using utils_tail) the callback + * function `submit_match' is called with the match object and the user + * supplied data. + * Please note that his function is called regardless whether this match + * matched any lines recently or not. + * When `tail_match_destroy' is called the `user_data' pointer is freed using + * the `free_user_data' callback - if it is not NULL. + * When using this interface the `tail_match' module doesn't dispatch any + * values + * itself - all that has to happen in either the match-callbacks or the + * submit_match callback. + * + * RETURN VALUE + * Zero upon success, non-zero otherwise. + */ +int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, + int (*submit_match)(cu_match_t *match, + void *user_data), + void *user_data, + void (*free_user_data)(void *user_data)); + +/* + * NAME + * tail_match_add_match_simple + * + * DESCRIPTION + * A simplified version of `tail_match_add_match'. The regular expressen + * `regex' + * must match a number, which is then dispatched according to `ds_type'. See + * the `match_create_simple' function in utils_match.h for a description how + * this flag effects calculation of a new value. + * The values gathered are dispatched by the tail_match module in this case. + * The + * passed `plugin', `plugin_instance', `type', and `type_instance' are + * directly used when submitting these values. + * With excluderegex it is possible to exlude lines from the match. + * The `latency_cfg' specifies configuration for submitting latency. + * + * RETURN VALUE + * Zero upon success, non-zero otherwise. + */ +int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, + const char *excluderegex, int ds_type, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance, + const latency_config_t latency_cfg, + const cdtime_t interval); + +/* + * NAME + * tail_match_read + * + * DESCRIPTION + * This function should be called periodically by plugins. It reads new lines + * from the logfile using `utils_tail' and tries to match them using all + * added `utils_match' objects. + * After all lines have been read and processed, the submit_match callback is + * called or, in case of tail_match_add_match_simple, the data is dispatched + * to + * the daemon directly. + * + * RETURN VALUE + * Zero on success, nonzero on failure. +*/ +int tail_match_read(cu_tail_match_t *obj); + +/* vim: set sw=2 sts=2 ts=8 : */