2 * collectd - src/match_value.c
3 * Copyright (C) 2008 Florian Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian Forster <octo at collectd.org>
28 * This module allows to filter and rewrite value lists based on
29 * Perl-compatible regular expressions.
34 #include "filter_chain.h"
35 #include "utils/common/common.h"
36 #include "utils_cache.h"
45 typedef struct mv_match_s mv_match_t;
53 size_t data_sources_num;
57 * internal helper functions
59 static void mv_free_match(mv_match_t *m) /* {{{ */
64 if (m->data_sources != NULL) {
65 for (size_t i = 0; i < m->data_sources_num; ++i)
66 free(m->data_sources[i]);
67 free(m->data_sources);
71 } /* }}} void mv_free_match */
73 static int mv_config_add_satisfy(mv_match_t *m, /* {{{ */
75 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
76 ERROR("`value' match: `%s' needs exactly one string argument.", ci->key);
80 if (strcasecmp("All", ci->values[0].value.string) == 0)
81 m->satisfy = SATISFY_ALL;
82 else if (strcasecmp("Any", ci->values[0].value.string) == 0)
83 m->satisfy = SATISFY_ANY;
85 ERROR("`value' match: Passing `%s' to the `%s' option is invalid. "
86 "The argument must either be `All' or `Any'.",
87 ci->values[0].value.string, ci->key);
92 } /* }}} int mv_config_add_satisfy */
94 static int mv_config_add_data_source(mv_match_t *m, /* {{{ */
96 size_t new_data_sources_num;
99 /* Check number of arbuments. */
100 if (ci->values_num < 1) {
101 ERROR("`value' match: `%s' needs at least one argument.", ci->key);
105 /* Check type of arguments */
106 for (int i = 0; i < ci->values_num; i++) {
107 if (ci->values[i].type == OCONFIG_TYPE_STRING)
110 ERROR("`value' match: `%s' accepts only string arguments "
111 "(argument %i is a %s).",
113 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) ? "truth value"
118 /* Allocate space for the char pointers */
119 new_data_sources_num = m->data_sources_num + ((size_t)ci->values_num);
120 temp = realloc(m->data_sources, new_data_sources_num * sizeof(char *));
122 ERROR("`value' match: realloc failed.");
125 m->data_sources = temp;
127 /* Copy the strings, allocating memory as needed. */
128 for (int i = 0; i < ci->values_num; i++) {
129 /* If we get here, there better be memory for us to write to. */
130 assert(m->data_sources_num < new_data_sources_num);
132 size_t j = m->data_sources_num;
133 m->data_sources[j] = sstrdup(ci->values[i].value.string);
134 if (m->data_sources[j] == NULL) {
135 ERROR("`value' match: sstrdup failed.");
138 m->data_sources_num++;
142 } /* }}} int mv_config_add_data_source */
144 static int mv_config_add_gauge(gauge_t *ret_value, /* {{{ */
145 oconfig_item_t *ci) {
147 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
148 ERROR("`value' match: `%s' needs exactly one numeric argument.", ci->key);
152 *ret_value = ci->values[0].value.number;
155 } /* }}} int mv_config_add_gauge */
157 static int mv_config_add_boolean(int *ret_value, /* {{{ */
158 oconfig_item_t *ci) {
160 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
161 ERROR("`value' match: `%s' needs exactly one boolean argument.", ci->key);
165 if (ci->values[0].value.boolean)
171 } /* }}} int mv_config_add_boolean */
173 static int mv_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
178 m = calloc(1, sizeof(*m));
180 ERROR("mv_create: calloc failed.");
187 m->satisfy = SATISFY_ALL;
188 m->data_sources = NULL;
189 m->data_sources_num = 0;
192 for (int i = 0; i < ci->children_num; i++) {
193 oconfig_item_t *child = ci->children + i;
195 if (strcasecmp("Min", child->key) == 0)
196 status = mv_config_add_gauge(&m->min, child);
197 else if (strcasecmp("Max", child->key) == 0)
198 status = mv_config_add_gauge(&m->max, child);
199 else if (strcasecmp("Invert", child->key) == 0)
200 status = mv_config_add_boolean(&m->invert, child);
201 else if (strcasecmp("Satisfy", child->key) == 0)
202 status = mv_config_add_satisfy(m, child);
203 else if (strcasecmp("DataSource", child->key) == 0)
204 status = mv_config_add_data_source(m, child);
206 ERROR("`value' match: The `%s' configuration option is not "
207 "understood and will be ignored.",
216 /* Additional sanity-checking */
217 while (status == 0) {
218 if (isnan(m->min) && isnan(m->max)) {
219 ERROR("`value' match: Neither minimum nor maximum are defined. "
220 "This match will be ignored.");
234 } /* }}} int mv_create */
236 static int mv_destroy(void **user_data) /* {{{ */
238 if ((user_data != NULL) && (*user_data != NULL))
239 mv_free_match(*user_data);
241 } /* }}} int mv_destroy */
243 static int mv_match(const data_set_t *ds, const value_list_t *vl, /* {{{ */
244 notification_meta_t __attribute__((unused)) * *meta,
250 if ((user_data == NULL) || (*user_data == NULL))
255 values = uc_get_rate(ds, vl);
256 if (values == NULL) {
257 ERROR("`value' match: Retrieving the current rate from the cache "
262 status = FC_MATCH_NO_MATCH;
264 for (size_t i = 0; i < ds->ds_num; i++) {
265 int value_matches = 0;
267 /* Check if this data source is relevant. */
268 if (m->data_sources != NULL) {
271 for (j = 0; j < m->data_sources_num; j++)
272 if (strcasecmp(ds->ds[i].name, m->data_sources[j]) == 0)
275 /* No match, ignore this data source. */
276 if (j >= m->data_sources_num)
280 DEBUG("`value' match: current = %g; min = %g; max = %g; invert = %s;",
281 values[i], m->min, m->max, m->invert ? "true" : "false");
283 if ((!isnan(m->min) && (values[i] < m->min)) ||
284 (!isnan(m->max) && (values[i] > m->max)))
296 if (value_matches != 0) {
297 status = FC_MATCH_MATCHES;
298 if (m->satisfy == SATISFY_ANY)
301 status = FC_MATCH_NO_MATCH;
302 if (m->satisfy == SATISFY_ALL)
305 } /* for (i = 0; i < ds->ds_num; i++) */
309 } /* }}} int mv_match */
311 void module_register(void) {
312 match_proc_t mproc = {0};
314 mproc.create = mv_create;
315 mproc.destroy = mv_destroy;
316 mproc.match = mv_match;
317 fc_register_match("value", mproc);
318 } /* module_register */