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)
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)
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
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)
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_match.h"
-
-#include <regex.h>
-
-#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 : */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 : */
+++ /dev/null
-/**
- * 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 <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * 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 */
+++ /dev/null
-/**
- * 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 <lukeh at c-ware.com>
- *
- * 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 */
+++ /dev/null
-/*
- * 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 <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * 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 : */
+++ /dev/null
-/*
- * 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 <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * 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 : */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_match.h"
+
+#include <regex.h>
+
+#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 : */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 : */
--- /dev/null
+/**
+ * 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 <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * 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 */
--- /dev/null
+/**
+ * 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 <lukeh at c-ware.com>
+ *
+ * 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 */
--- /dev/null
+/*
+ * 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 <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * 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 : */
--- /dev/null
+/*
+ * 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 <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * 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 : */