X-Git-Url: https://git.octo.it/?p=collectd.git;a=blobdiff_plain;f=src%2Ffilter_chain.c;h=c87b87735afdea2da2bbb6adb8e908eb283031f8;hp=e51e07ec7bb3bdf67c82fa254454d60316cd6b4c;hb=633c3966f770e4d46651a2fe219a18d8a9907a9f;hpb=eab9dd12e4730f22f8b48d2abc16518272b6e53c diff --git a/src/filter_chain.c b/src/filter_chain.c index e51e07ec..c87b8773 100644 --- a/src/filter_chain.c +++ b/src/filter_chain.c @@ -1,65 +1,33 @@ /** - * collectd - src/filter_chain.h - * Copyright (C) 2008 Florian octo Forster + * collectd - src/filter_chain.c + * Copyright (C) 2008-2010 Florian octo Forster * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. + * 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: * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * 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 + * Florian octo Forster **/ -/* - * First tell the compiler to stick to the C99 and POSIX standards as close as - * possible. - */ -#ifndef __STRICT_ANSI__ /* {{{ */ -# define __STRICT_ANSI__ -#endif - -#ifndef _ISOC99_SOURCE -# define _ISOC99_SOURCE -#endif - -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif -#define _POSIX_C_SOURCE 200112L - -#if 0 -/* Single UNIX needed for strdup. */ -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif -#define _XOPEN_SOURCE 500 -#endif - -#ifndef _REENTRANT -# define _REENTRANT -#endif - -#ifndef _THREAD_SAFE -# define _THREAD_SAFE -#endif - -#ifdef _GNU_SOURCE -# undef _GNU_SOURCE -#endif -/* }}} */ - #include "collectd.h" #include "configfile.h" #include "plugin.h" +#include "utils_complain.h" #include "common.h" #include "filter_chain.h" @@ -91,9 +59,9 @@ struct fc_target_s }; /* }}} */ /* List of rules, used in fc_chain_t */ -struct fc_fule_s; -typedef struct fc_fule_s fc_rule_t; /* {{{ */ -struct fc_fule_s +struct fc_rule_s; +typedef struct fc_rule_s fc_rule_t; /* {{{ */ +struct fc_rule_s { char name[DATA_MAX_NAME_LEN]; fc_match_t *matches; @@ -102,9 +70,7 @@ struct fc_fule_s }; /* }}} */ /* List of chains, used for `chain_list_head' */ -struct fc_chain_s; -typedef struct fc_chain_s fc_chain_t; /* {{{ */ -struct fc_chain_s +struct fc_chain_s /* {{{ */ { char name[DATA_MAX_NAME_LEN]; fc_rule_t *rules; @@ -131,7 +97,7 @@ static void fc_free_matches (fc_match_t *m) /* {{{ */ (*m->proc.destroy) (&m->user_data); else if (m->user_data != NULL) { - ERROR ("Filter sybsystem: fc_free_matches: There is user data, but no " + ERROR ("Filter subsystem: fc_free_matches: There is user data, but no " "destroy functions has been specified. " "Memory will probably be lost!"); } @@ -151,7 +117,7 @@ static void fc_free_targets (fc_target_t *t) /* {{{ */ (*t->proc.destroy) (&t->user_data); else if (t->user_data != NULL) { - ERROR ("Filter sybsystem: fc_free_targets: There is user data, but no " + ERROR ("Filter subsystem: fc_free_targets: There is user data, but no " "destroy functions has been specified. " "Memory will probably be lost!"); } @@ -213,7 +179,7 @@ static char *fc_strdup (const char *orig) /* {{{ */ * * The configuration looks somewhat like this: * - * + * * * * Plugin "^mysql$" @@ -270,19 +236,21 @@ static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */ sstrncpy (m->name, ptr->name, sizeof (m->name)); memcpy (&m->proc, &ptr->proc, sizeof (m->proc)); - assert (m->proc.create != NULL); m->user_data = NULL; m->next = NULL; - status = (*m->proc.create) (ci, &m->user_data); - if (status != 0) + if (m->proc.create != NULL) { - WARNING ("Filter subsystem: Failed to create a %s match.", - m->name); - fc_free_matches (m); - return (-1); + status = (*m->proc.create) (ci, &m->user_data); + if (status != 0) + { + WARNING ("Filter subsystem: Failed to create a %s match.", + m->name); + fc_free_matches (m); + return (-1); + } } - + if (*matches_head != NULL) { ptr = *matches_head; @@ -333,7 +301,7 @@ static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */ t = (fc_target_t *) malloc (sizeof (*t)); if (t == NULL) { - ERROR ("fc_config_add_match: malloc failed."); + ERROR ("fc_config_add_target: malloc failed."); return (-1); } memset (t, 0, sizeof (*t)); @@ -348,7 +316,7 @@ static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */ status = (*t->proc.create) (ci, &t->user_data); if (status != 0) { - WARNING ("Filter subsystem: Failed to create a %s match.", + WARNING ("Filter subsystem: Failed to create a %s target.", t->name); fc_free_targets (t); return (-1); @@ -472,9 +440,10 @@ static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ { - fc_chain_t *chain; + fc_chain_t *chain = NULL; int status = 0; int i; + int new_chain = 1; if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) @@ -484,17 +453,26 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ return (-1); } - chain = (fc_chain_t *) malloc (sizeof (*chain)); + if (chain_list_head != NULL) + { + if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL) + new_chain = 0; + } + if (chain == NULL) { - ERROR ("fc_config_add_chain: malloc failed."); - return (-1); + chain = (fc_chain_t *) malloc (sizeof (*chain)); + if (chain == NULL) + { + ERROR ("fc_config_add_chain: malloc failed."); + return (-1); + } + memset (chain, 0, sizeof (*chain)); + sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name)); + chain->rules = NULL; + chain->targets = NULL; + chain->next = NULL; } - memset (chain, 0, sizeof (*chain)); - sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name)); - chain->rules = NULL; - chain->targets = NULL; - chain->next = NULL; for (i = 0; i < ci->children_num; i++) { @@ -516,21 +494,6 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ break; } /* for (ci->children) */ - /* Additional sanity checking. */ - while (status == 0) - { - if (chain->targets == NULL) - { - WARNING ("Filter subsystem: Chain %s: No default target has been " - "specified. Please make sure that there is a block within " - "the block!", chain->name); - status = -1; - break; - } - - break; - } /* while (status == 0) */ - if (status != 0) { fc_free_chains (chain); @@ -539,6 +502,9 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ if (chain_list_head != NULL) { + if (!new_chain) + return (0); + fc_chain_t *ptr; ptr = chain_list_head; @@ -555,103 +521,6 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */ return (0); } /* }}} int fc_config_add_chain */ -int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */ - fc_chain_t *chain) -{ - fc_rule_t *rule; - fc_target_t *target; - int status; - - if (chain == NULL) - return (-1); - - DEBUG ("fc_process_chain (chain = %s);", chain->name); - - status = FC_ACTION_CONTINUE; - - for (rule = chain->rules; rule != NULL; rule = rule->next) - { - fc_match_t *match; - - if (rule->name[0] != 0) - { - DEBUG ("fc_process_chain: Testing the `%s' rule.", rule->name); - } - - /* N. B.: rule->matches may be NULL. */ - for (match = rule->matches; match != NULL; match = match->next) - { - status = (*match->proc.match) (ds, vl, /* meta = */ NULL, - &match->user_data); - if (status < 0) - { - WARNING ("fc_process_chain: A match failed."); - break; - } - else if (status != FC_MATCH_MATCHES) - break; - } - - /* for-loop has been aborted: Either error or no match. */ - if (match != NULL) - continue; - - if (rule->name[0] != 0) - { - DEBUG ("fc_process_chain: Rule `%s' matches.", rule->name); - } - - for (target = rule->targets; target != NULL; target = target->next) - { - /* If we get here, all matches have matched the value. Execute the - * target. */ - status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, - &target->user_data); - if (status < 0) - { - WARNING ("fc_process_chain: A target failed."); - continue; - } - else if (status == FC_ACTION_CONTINUE) - continue; - else if (status == FC_ACTION_STOP) - break; - else - { - WARNING ("fc_process_chain: Unknown target return value: %i", status); - } - } - - if (status == FC_ACTION_STOP) - { - if (rule->name[0] != 0) - { - DEBUG ("fc_process_chain: Rule `%s' signaled the stop condition.", - rule->name); - } - break; - } - } /* for (rule) */ - - /* for-loop has been aborted: A target returned `FC_ACTION_STOP' */ - if (rule != NULL) - return (0); - - for (target = chain->targets; target != NULL; target = target->next) - { - /* If we get here, all matches have matched the value. Execute the - * target. */ - status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, - &target->user_data); - if (status < 0) - { - WARNING ("fc_process_chain: The default target failed."); - } - } - - return (0); -} /* }}} int fc_process_chain */ - /* * Built-in target "jump" * @@ -708,7 +577,8 @@ static int fc_bit_jump_destroy (void **user_data) /* {{{ */ } /* }}} int fc_bit_jump_destroy */ static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */ - value_list_t *vl, notification_meta_t **meta, void **user_data) + value_list_t *vl, notification_meta_t __attribute__((unused)) **meta, + void **user_data) { char *chain_name; fc_chain_t *chain; @@ -730,16 +600,28 @@ static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */ status = fc_process_chain (ds, vl, chain); if (status < 0) return (status); - - return (FC_ACTION_CONTINUE); + else if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else + return (FC_TARGET_CONTINUE); } /* }}} int fc_bit_jump_invoke */ -static int fc_bit_stop_invoke (const data_set_t *ds, /* {{{ */ - value_list_t *vl, notification_meta_t **meta, void **user_data) +static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */ + value_list_t __attribute__((unused)) *vl, + notification_meta_t __attribute__((unused)) **meta, + void __attribute__((unused)) **user_data) { - return (FC_ACTION_STOP); + return (FC_TARGET_STOP); } /* }}} int fc_bit_stop_invoke */ +static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */ + value_list_t __attribute__((unused)) *vl, + notification_meta_t __attribute__((unused)) **meta, + void __attribute__((unused)) **user_data) +{ + return (FC_TARGET_RETURN); +} /* }}} int fc_bit_return_invoke */ + static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */ void **user_data) { @@ -817,7 +699,8 @@ static int fc_bit_write_destroy (void **user_data) /* {{{ */ } /* }}} int fc_bit_write_destroy */ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ - value_list_t *vl, notification_meta_t **meta, void **user_data) + value_list_t *vl, notification_meta_t __attribute__((unused)) **meta, + void **user_data) { char **plugin_list; int status; @@ -828,12 +711,31 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ if ((plugin_list == NULL) || (plugin_list[0] == NULL)) { + static c_complain_t enoent_complaint = C_COMPLAIN_INIT_STATIC; + status = plugin_write (/* plugin = */ NULL, ds, vl); - if (status != 0) + if (status == ENOENT) + { + /* in most cases this is a permanent error, so use the complain + * mechanism rather than spamming the logs */ + c_complain (LOG_INFO, &enoent_complaint, + "Filter subsystem: Built-in target `write': Dispatching value to " + "all write plugins failed with status %i (ENOENT). " + "Most likely this means you didn't load any write plugins.", + status); + } + else if (status != 0) { INFO ("Filter subsystem: Built-in target `write': Dispatching value to " "all write plugins failed with status %i.", status); } + else + { + assert (status == 0); + c_release (LOG_INFO, &enoent_complaint, "Filter subsystem: " + "Built-in target `write': Some write plugin is back to normal " + "operation. `write' succeeded."); + } } else { @@ -850,7 +752,7 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ } /* for (i = 0; plugin_list[i] != NULL; i++) */ } - return (FC_ACTION_CONTINUE); + return (FC_TARGET_CONTINUE); } /* }}} int fc_bit_write_invoke */ static int fc_init_once (void) /* {{{ */ @@ -874,6 +776,12 @@ static int fc_init_once (void) /* {{{ */ fc_register_target ("stop", tproc); memset (&tproc, 0, sizeof (tproc)); + tproc.create = NULL; + tproc.destroy = NULL; + tproc.invoke = fc_bit_return_invoke; + fc_register_target ("return", tproc); + + memset (&tproc, 0, sizeof (tproc)); tproc.create = fc_bit_write_create; tproc.destroy = fc_bit_write_destroy; tproc.invoke = fc_bit_write_invoke; @@ -954,23 +862,182 @@ int fc_register_target (const char *name, target_proc_t proc) /* {{{ */ return (0); } /* }}} int fc_register_target */ -/* Iterate over all rules in the chain and execute all targets for which all - * matches match. */ -int fc_process (const data_set_t *ds, value_list_t *vl) /* {{{ */ +fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */ { fc_chain_t *chain; + if (chain_name == NULL) + return (NULL); + for (chain = chain_list_head; chain != NULL; chain = chain->next) - if (strcasecmp ("Main", chain->name) == 0) + if (strcasecmp (chain_name, chain->name) == 0) + return (chain); + + return (NULL); +} /* }}} int fc_chain_get_by_name */ + +int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */ + fc_chain_t *chain) +{ + fc_rule_t *rule; + fc_target_t *target; + int status; + + if (chain == NULL) + return (-1); + + DEBUG ("fc_process_chain (chain = %s);", chain->name); + + status = FC_TARGET_CONTINUE; + for (rule = chain->rules; rule != NULL; rule = rule->next) + { + fc_match_t *match; + + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Testing the `%s' rule.", + chain->name, rule->name); + } + + /* N. B.: rule->matches may be NULL. */ + for (match = rule->matches; match != NULL; match = match->next) + { + /* FIXME: Pass the meta-data to match targets here (when implemented). */ + status = (*match->proc.match) (ds, vl, /* meta = */ NULL, + &match->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): A match failed.", chain->name); + break; + } + else if (status != FC_MATCH_MATCHES) + break; + } + + /* for-loop has been aborted: Either error or no match. */ + if (match != NULL) + { + status = FC_TARGET_CONTINUE; + continue; + } + + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Rule `%s' matches.", + chain->name, rule->name); + } + + for (target = rule->targets; target != NULL; target = target->next) + { + /* If we get here, all matches have matched the value. Execute the + * target. */ + /* FIXME: Pass the meta-data to match targets here (when implemented). */ + status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, + &target->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): A target failed.", chain->name); + continue; + } + else if (status == FC_TARGET_CONTINUE) + continue; + else if (status == FC_TARGET_STOP) + break; + else if (status == FC_TARGET_RETURN) + break; + else + { + WARNING ("fc_process_chain (%s): Unknown return value " + "from target `%s': %i", + chain->name, target->name, status); + } + } + + if ((status == FC_TARGET_STOP) + || (status == FC_TARGET_RETURN)) + { + if (rule->name[0] != 0) + { + DEBUG ("fc_process_chain (%s): Rule `%s' signaled " + "the %s condition.", + chain->name, rule->name, + (status == FC_TARGET_STOP) ? "stop" : "return"); + } + break; + } + else + { + status = FC_TARGET_CONTINUE; + } + } /* for (rule) */ + + if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else if (status == FC_TARGET_RETURN) + return (FC_TARGET_CONTINUE); + + /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */ + if (rule != NULL) + return (FC_TARGET_CONTINUE); + + DEBUG ("fc_process_chain (%s): Executing the default targets.", + chain->name); + + status = FC_TARGET_CONTINUE; + for (target = chain->targets; target != NULL; target = target->next) + { + /* If we get here, all matches have matched the value. Execute the + * target. */ + /* FIXME: Pass the meta-data to match targets here (when implemented). */ + status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL, + &target->user_data); + if (status < 0) + { + WARNING ("fc_process_chain (%s): The default target failed.", + chain->name); + } + else if (status == FC_TARGET_CONTINUE) + continue; + else if (status == FC_TARGET_STOP) + break; + else if (status == FC_TARGET_RETURN) break; + else + { + WARNING ("fc_process_chain (%s): Unknown return value " + "from target `%s': %i", + chain->name, target->name, status); + } + } - if (chain != NULL) - return (fc_process_chain (ds, vl, chain)); + if ((status == FC_TARGET_STOP) + || (status == FC_TARGET_RETURN)) + { + assert (target != NULL); + DEBUG ("fc_process_chain (%s): Default target `%s' signaled " + "the %s condition.", + chain->name, target->name, + (status == FC_TARGET_STOP) ? "stop" : "return"); + if (status == FC_TARGET_STOP) + return (FC_TARGET_STOP); + else + return (FC_TARGET_CONTINUE); + } - ERROR ("fc_process: TODO: Implement default behavior!"); + DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.", + chain->name); - return (0); -} /* }}} int fc_process */ + return (FC_TARGET_CONTINUE); +} /* }}} int fc_process_chain */ + +/* Iterate over all rules in the chain and execute all targets for which all + * matches match. */ +int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + /* FIXME: Pass the meta-data to match targets here (when implemented). */ + return (fc_bit_write_invoke (ds, vl, + /* meta = */ NULL, /* user_data = */ NULL)); +} /* }}} int fc_default_action */ int fc_configure (const oconfig_item_t *ci) /* {{{ */ {