1 /**
2 * collectd - src/filter_chain.c
3 * Copyright (C) 2008-2010 Florian octo Forster
4 *
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:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
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.
22 *
23 * Authors:
24 * Florian octo Forster <octo at collectd.org>
25 **/
27 #include "collectd.h"
28 #include "configfile.h"
29 #include "plugin.h"
30 #include "utils_complain.h"
31 #include "common.h"
32 #include "filter_chain.h"
34 /*
35 * Data types
36 */
37 /* List of matches, used in fc_rule_t and for the global `match_list_head'
38 * variable. */
39 struct fc_match_s;
40 typedef struct fc_match_s fc_match_t; /* {{{ */
41 struct fc_match_s
42 {
43 char name[DATA_MAX_NAME_LEN];
44 match_proc_t proc;
45 void *user_data;
46 fc_match_t *next;
47 }; /* }}} */
49 /* List of targets, used in fc_rule_t and for the global `target_list_head'
50 * variable. */
51 struct fc_target_s;
52 typedef struct fc_target_s fc_target_t; /* {{{ */
53 struct fc_target_s
54 {
55 char name[DATA_MAX_NAME_LEN];
56 void *user_data;
57 target_proc_t proc;
58 fc_target_t *next;
59 }; /* }}} */
61 /* List of rules, used in fc_chain_t */
62 struct fc_rule_s;
63 typedef struct fc_rule_s fc_rule_t; /* {{{ */
64 struct fc_rule_s
65 {
66 char name[DATA_MAX_NAME_LEN];
67 fc_match_t *matches;
68 fc_target_t *targets;
69 fc_rule_t *next;
70 }; /* }}} */
72 /* List of chains, used for `chain_list_head' */
73 struct fc_chain_s /* {{{ */
74 {
75 char name[DATA_MAX_NAME_LEN];
76 fc_rule_t *rules;
77 fc_target_t *targets;
78 fc_chain_t *next;
79 }; /* }}} */
81 /* Writer configuration. */
82 struct fc_writer_s;
83 typedef struct fc_writer_s fc_writer_t; /* {{{ */
84 struct fc_writer_s
85 {
86 char *plugin;
87 c_complain_t complaint;
88 }; /* }}} */
90 /*
91 * Global variables
92 */
93 static fc_match_t *match_list_head;
94 static fc_target_t *target_list_head;
95 static fc_chain_t *chain_list_head;
97 /*
98 * Private functions
99 */
100 static void fc_free_matches (fc_match_t *m) /* {{{ */
101 {
102 if (m == NULL)
103 return;
105 if (m->proc.destroy != NULL)
106 (*m->proc.destroy) (&m->user_data);
107 else if (m->user_data != NULL)
108 {
109 ERROR ("Filter subsystem: fc_free_matches: There is user data, but no "
110 "destroy functions has been specified. "
111 "Memory will probably be lost!");
112 }
114 if (m->next != NULL)
115 fc_free_matches (m->next);
117 free (m);
118 } /* }}} void fc_free_matches */
120 static void fc_free_targets (fc_target_t *t) /* {{{ */
121 {
122 if (t == NULL)
123 return;
125 if (t->proc.destroy != NULL)
126 (*t->proc.destroy) (&t->user_data);
127 else if (t->user_data != NULL)
128 {
129 ERROR ("Filter subsystem: fc_free_targets: There is user data, but no "
130 "destroy functions has been specified. "
131 "Memory will probably be lost!");
132 }
134 if (t->next != NULL)
135 fc_free_targets (t->next);
137 free (t);
138 } /* }}} void fc_free_targets */
140 static void fc_free_rules (fc_rule_t *r) /* {{{ */
141 {
142 if (r == NULL)
143 return;
145 fc_free_matches (r->matches);
146 fc_free_targets (r->targets);
148 if (r->next != NULL)
149 fc_free_rules (r->next);
151 free (r);
152 } /* }}} void fc_free_rules */
154 static void fc_free_chains (fc_chain_t *c) /* {{{ */
155 {
156 if (c == NULL)
157 return;
159 fc_free_rules (c->rules);
160 fc_free_targets (c->targets);
162 if (c->next != NULL)
163 fc_free_chains (c->next);
165 free (c);
166 } /* }}} void fc_free_chains */
168 static char *fc_strdup (const char *orig) /* {{{ */
169 {
170 size_t sz;
171 char *dest;
173 if (orig == NULL)
174 return (NULL);
176 sz = strlen (orig) + 1;
177 dest = (char *) malloc (sz);
178 if (dest == NULL)
179 return (NULL);
181 memcpy (dest, orig, sz);
183 return (dest);
184 } /* }}} char *fc_strdup */
186 /*
187 * Configuration.
188 *
189 * The configuration looks somewhat like this:
190 *
191 * <Chain "PreCache">
192 * <Rule>
193 * <Match "regex">
194 * Plugin "^mysql$"
195 * Type "^mysql_command$"
196 * TypeInstance "^show_"
197 * </Match>
198 * <Target "drop">
199 * </Target>
200 * </Rule>
201 *
202 * <Target "write">
203 * Plugin "rrdtool"
204 * </Target>
205 * </Chain>
206 */
207 static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
208 oconfig_item_t *ci)
209 {
210 fc_match_t *m;
211 fc_match_t *ptr;
212 int status;
214 if ((ci->values_num != 1)
215 || (ci->values[0].type != OCONFIG_TYPE_STRING))
216 {
217 WARNING ("Filter subsystem: `Match' blocks require "
218 "exactly one string argument.");
219 return (-1);
220 }
222 ptr = match_list_head;
223 while (ptr != NULL)
224 {
225 if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
226 break;
227 ptr = ptr->next;
228 }
230 if (ptr == NULL)
231 {
232 WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
233 "Did you load the appropriate plugin?",
234 ci->values[0].value.string);
235 return (-1);
236 }
238 m = (fc_match_t *) malloc (sizeof (*m));
239 if (m == NULL)
240 {
241 ERROR ("fc_config_add_match: malloc failed.");
242 return (-1);
243 }
244 memset (m, 0, sizeof (*m));
246 sstrncpy (m->name, ptr->name, sizeof (m->name));
247 memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
248 m->user_data = NULL;
249 m->next = NULL;
251 if (m->proc.create != NULL)
252 {
253 status = (*m->proc.create) (ci, &m->user_data);
254 if (status != 0)
255 {
256 WARNING ("Filter subsystem: Failed to create a %s match.",
257 m->name);
258 fc_free_matches (m);
259 return (-1);
260 }
261 }
263 if (*matches_head != NULL)
264 {
265 ptr = *matches_head;
266 while (ptr->next != NULL)
267 ptr = ptr->next;
269 ptr->next = m;
270 }
271 else
272 {
273 *matches_head = m;
274 }
276 return (0);
277 } /* }}} int fc_config_add_match */
279 static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
280 oconfig_item_t *ci)
281 {
282 fc_target_t *t;
283 fc_target_t *ptr;
284 int status;
286 if ((ci->values_num != 1)
287 || (ci->values[0].type != OCONFIG_TYPE_STRING))
288 {
289 WARNING ("Filter subsystem: `Target' blocks require "
290 "exactly one string argument.");
291 return (-1);
292 }
294 ptr = target_list_head;
295 while (ptr != NULL)
296 {
297 if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
298 break;
299 ptr = ptr->next;
300 }
302 if (ptr == NULL)
303 {
304 WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
305 "Did you load the appropriate plugin?",
306 ci->values[0].value.string);
307 return (-1);
308 }
310 t = (fc_target_t *) malloc (sizeof (*t));
311 if (t == NULL)
312 {
313 ERROR ("fc_config_add_target: malloc failed.");
314 return (-1);
315 }
316 memset (t, 0, sizeof (*t));
318 sstrncpy (t->name, ptr->name, sizeof (t->name));
319 memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
320 t->user_data = NULL;
321 t->next = NULL;
323 if (t->proc.create != NULL)
324 {
325 status = (*t->proc.create) (ci, &t->user_data);
326 if (status != 0)
327 {
328 WARNING ("Filter subsystem: Failed to create a %s target.",
329 t->name);
330 fc_free_targets (t);
331 return (-1);
332 }
333 }
334 else
335 {
336 t->user_data = NULL;
337 }
339 if (*targets_head != NULL)
340 {
341 ptr = *targets_head;
342 while (ptr->next != NULL)
343 ptr = ptr->next;
345 ptr->next = t;
346 }
347 else
348 {
349 *targets_head = t;
350 }
352 return (0);
353 } /* }}} int fc_config_add_target */
355 static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
356 oconfig_item_t *ci)
357 {
358 fc_rule_t *rule;
359 char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
360 int status = 0;
361 int i;
363 if (ci->values_num > 1)
364 {
365 WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
366 return (-1);
367 }
368 else if ((ci->values_num == 1)
369 && (ci->values[0].type != OCONFIG_TYPE_STRING))
370 {
371 WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
372 "or no argument at all.");
373 return (-1);
374 }
376 rule = (fc_rule_t *) malloc (sizeof (*rule));
377 if (rule == NULL)
378 {
379 ERROR ("fc_config_add_rule: malloc failed.");
380 return (-1);
381 }
382 memset (rule, 0, sizeof (*rule));
383 rule->next = NULL;
385 if (ci->values_num == 1)
386 {
387 sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
388 ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
389 ci->values[0].value.string);
390 }
392 for (i = 0; i < ci->children_num; i++)
393 {
394 oconfig_item_t *option = ci->children + i;
396 if (strcasecmp ("Match", option->key) == 0)
397 status = fc_config_add_match (&rule->matches, option);
398 else if (strcasecmp ("Target", option->key) == 0)
399 status = fc_config_add_target (&rule->targets, option);
400 else
401 {
402 WARNING ("Filter subsystem: %s: Option `%s' not allowed "
403 "inside a <Rule> block.", rule_name, option->key);
404 status = -1;
405 }
407 if (status != 0)
408 break;
409 } /* for (ci->children) */
411 /* Additional sanity checking. */
412 while (status == 0)
413 {
414 if (rule->targets == NULL)
415 {
416 WARNING ("Filter subsystem: %s: No target has been specified.",
417 rule_name);
418 status = -1;
419 break;
420 }
422 break;
423 } /* while (status == 0) */
425 if (status != 0)
426 {
427 fc_free_rules (rule);
428 return (-1);
429 }
431 if (chain->rules != NULL)
432 {
433 fc_rule_t *ptr;
435 ptr = chain->rules;
436 while (ptr->next != NULL)
437 ptr = ptr->next;
439 ptr->next = rule;
440 }
441 else
442 {
443 chain->rules = rule;
444 }
446 return (0);
447 } /* }}} int fc_config_add_rule */
449 static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
450 {
451 fc_chain_t *chain = NULL;
452 int status = 0;
453 int i;
454 int new_chain = 1;
456 if ((ci->values_num != 1)
457 || (ci->values[0].type != OCONFIG_TYPE_STRING))
458 {
459 WARNING ("Filter subsystem: <Chain> blocks require exactly one "
460 "string argument.");
461 return (-1);
462 }
464 if (chain_list_head != NULL)
465 {
466 if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
467 new_chain = 0;
468 }
470 if (chain == NULL)
471 {
472 chain = (fc_chain_t *) malloc (sizeof (*chain));
473 if (chain == NULL)
474 {
475 ERROR ("fc_config_add_chain: malloc failed.");
476 return (-1);
477 }
478 memset (chain, 0, sizeof (*chain));
479 sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
480 chain->rules = NULL;
481 chain->targets = NULL;
482 chain->next = NULL;
483 }
485 for (i = 0; i < ci->children_num; i++)
486 {
487 oconfig_item_t *option = ci->children + i;
489 if (strcasecmp ("Rule", option->key) == 0)
490 status = fc_config_add_rule (chain, option);
491 else if (strcasecmp ("Target", option->key) == 0)
492 status = fc_config_add_target (&chain->targets, option);
493 else
494 {
495 WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
496 "inside a <Chain> block.", chain->name, option->key);
497 status = -1;
498 }
500 if (status != 0)
501 break;
502 } /* for (ci->children) */
504 if (status != 0)
505 {
506 fc_free_chains (chain);
507 return (-1);
508 }
510 if (chain_list_head != NULL)
511 {
512 if (!new_chain)
513 return (0);
515 fc_chain_t *ptr;
517 ptr = chain_list_head;
518 while (ptr->next != NULL)
519 ptr = ptr->next;
521 ptr->next = chain;
522 }
523 else
524 {
525 chain_list_head = chain;
526 }
528 return (0);
529 } /* }}} int fc_config_add_chain */
531 /*
532 * Built-in target "jump"
533 *
534 * Prefix `bit' like `_b_uilt-_i_n _t_arget'
535 */
536 static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
537 void **user_data)
538 {
539 oconfig_item_t *ci_chain;
541 if (ci->children_num != 1)
542 {
543 ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
544 "one `Chain' argument!");
545 return (-1);
546 }
548 ci_chain = ci->children;
549 if (strcasecmp ("Chain", ci_chain->key) != 0)
550 {
551 ERROR ("Filter subsystem: The built-in target `jump' does not "
552 "support the configuration option `%s'.",
553 ci_chain->key);
554 return (-1);
555 }
557 if ((ci_chain->values_num != 1)
558 || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
559 {
560 ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
561 "needs exactly one string argument.");
562 return (-1);
563 }
565 *user_data = fc_strdup (ci_chain->values[0].value.string);
566 if (*user_data == NULL)
567 {
568 ERROR ("fc_bit_jump_create: fc_strdup failed.");
569 return (-1);
570 }
572 return (0);
573 } /* }}} int fc_bit_jump_create */
575 static int fc_bit_jump_destroy (void **user_data) /* {{{ */
576 {
577 if (user_data != NULL)
578 {
579 free (*user_data);
580 *user_data = NULL;
581 }
583 return (0);
584 } /* }}} int fc_bit_jump_destroy */
586 static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
587 value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
588 void **user_data)
589 {
590 char *chain_name;
591 fc_chain_t *chain;
592 int status;
594 chain_name = *user_data;
596 for (chain = chain_list_head; chain != NULL; chain = chain->next)
597 if (strcasecmp (chain_name, chain->name) == 0)
598 break;
600 if (chain == NULL)
601 {
602 ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
603 "named `%s'.", chain_name);
604 return (-1);
605 }
607 status = fc_process_chain (ds, vl, chain);
608 if (status < 0)
609 return (status);
610 else if (status == FC_TARGET_STOP)
611 return (FC_TARGET_STOP);
612 else
613 return (FC_TARGET_CONTINUE);
614 } /* }}} int fc_bit_jump_invoke */
616 static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
617 value_list_t __attribute__((unused)) *vl,
618 notification_meta_t __attribute__((unused)) **meta,
619 void __attribute__((unused)) **user_data)
620 {
621 return (FC_TARGET_STOP);
622 } /* }}} int fc_bit_stop_invoke */
624 static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
625 value_list_t __attribute__((unused)) *vl,
626 notification_meta_t __attribute__((unused)) **meta,
627 void __attribute__((unused)) **user_data)
628 {
629 return (FC_TARGET_RETURN);
630 } /* }}} int fc_bit_return_invoke */
632 static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
633 void **user_data)
634 {
635 int i;
637 fc_writer_t *plugin_list = NULL;
638 size_t plugin_list_len = 0;
640 for (i = 0; i < ci->children_num; i++)
641 {
642 oconfig_item_t *child = ci->children + i;
643 fc_writer_t *temp;
644 int j;
646 if (strcasecmp ("Plugin", child->key) != 0)
647 {
648 ERROR ("Filter subsystem: The built-in target `write' does not "
649 "support the configuration option `%s'.",
650 child->key);
651 continue;
652 }
654 for (j = 0; j < child->values_num; j++)
655 {
656 char *plugin;
658 if (child->values[j].type != OCONFIG_TYPE_STRING)
659 {
660 ERROR ("Filter subsystem: Built-in target `write': "
661 "The `Plugin' option accepts only string arguments.");
662 continue;
663 }
664 plugin = child->values[j].value.string;
666 temp = (fc_writer_t *) realloc (plugin_list, (plugin_list_len + 2)
667 * (sizeof (*plugin_list)));
668 if (temp == NULL)
669 {
670 ERROR ("fc_bit_write_create: realloc failed.");
671 continue;
672 }
673 plugin_list = temp;
675 plugin_list[plugin_list_len].plugin = fc_strdup (plugin);
676 if (plugin_list[plugin_list_len].plugin == NULL)
677 {
678 ERROR ("fc_bit_write_create: fc_strdup failed.");
679 continue;
680 }
681 C_COMPLAIN_INIT (&plugin_list[plugin_list_len].complaint);
682 plugin_list_len++;
683 plugin_list[plugin_list_len].plugin = NULL;
684 } /* for (j = 0; j < child->values_num; j++) */
685 } /* for (i = 0; i < ci->children_num; i++) */
687 *user_data = plugin_list;
689 return (0);
690 } /* }}} int fc_bit_write_create */
692 static int fc_bit_write_destroy (void **user_data) /* {{{ */
693 {
694 fc_writer_t *plugin_list;
695 size_t i;
697 if ((user_data == NULL) || (*user_data == NULL))
698 return (0);
700 plugin_list = *user_data;
702 for (i = 0; plugin_list[i].plugin != NULL; i++)
703 free (plugin_list[i].plugin);
704 free (plugin_list);
706 return (0);
707 } /* }}} int fc_bit_write_destroy */
709 static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
710 value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
711 void **user_data)
712 {
713 fc_writer_t *plugin_list;
714 int status;
716 plugin_list = NULL;
717 if (user_data != NULL)
718 plugin_list = *user_data;
720 if ((plugin_list == NULL) || (plugin_list[0].plugin == NULL))
721 {
722 static c_complain_t write_complaint = C_COMPLAIN_INIT_STATIC;
724 status = plugin_write (/* plugin = */ NULL, ds, vl);
725 if (status == ENOENT)
726 {
727 /* in most cases this is a permanent error, so use the complain
728 * mechanism rather than spamming the logs */
729 c_complain (LOG_INFO, &write_complaint,
730 "Filter subsystem: Built-in target `write': Dispatching value to "
731 "all write plugins failed with status %i (ENOENT). "
732 "Most likely this means you didn't load any write plugins.",
733 status);
735 plugin_log_available_writers ();
736 }
737 else if (status != 0)
738 {
739 /* often, this is a permanent error (e.g. target system unavailable),
740 * so use the complain mechanism rather than spamming the logs */
741 c_complain (LOG_INFO, &write_complaint,
742 "Filter subsystem: Built-in target `write': Dispatching value to "
743 "all write plugins failed with status %i.", status);
744 }
745 else
746 {
747 assert (status == 0);
748 c_release (LOG_INFO, &write_complaint, "Filter subsystem: "
749 "Built-in target `write': Some write plugin is back to normal "
750 "operation. `write' succeeded.");
751 }
752 }
753 else
754 {
755 size_t i;
757 for (i = 0; plugin_list[i].plugin != NULL; i++)
758 {
759 status = plugin_write (plugin_list[i].plugin, ds, vl);
760 if (status != 0)
761 {
762 c_complain (LOG_INFO, &plugin_list[i].complaint,
763 "Filter subsystem: Built-in target `write': Dispatching value to "
764 "the `%s' plugin failed with status %i.",
765 plugin_list[i].plugin, status);
767 plugin_log_available_writers ();
768 }
769 else
770 {
771 c_release (LOG_INFO, &plugin_list[i].complaint,
772 "Filter subsystem: Built-in target `write': Plugin `%s' is back "
773 "to normal operation. `write' succeeded.", plugin_list[i].plugin);
774 }
775 } /* for (i = 0; plugin_list[i] != NULL; i++) */
776 }
778 return (FC_TARGET_CONTINUE);
779 } /* }}} int fc_bit_write_invoke */
781 static int fc_init_once (void) /* {{{ */
782 {
783 static int done = 0;
784 target_proc_t tproc;
786 if (done != 0)
787 return (0);
789 memset (&tproc, 0, sizeof (tproc));
790 tproc.create = fc_bit_jump_create;
791 tproc.destroy = fc_bit_jump_destroy;
792 tproc.invoke = fc_bit_jump_invoke;
793 fc_register_target ("jump", tproc);
795 memset (&tproc, 0, sizeof (tproc));
796 tproc.create = NULL;
797 tproc.destroy = NULL;
798 tproc.invoke = fc_bit_stop_invoke;
799 fc_register_target ("stop", tproc);
801 memset (&tproc, 0, sizeof (tproc));
802 tproc.create = NULL;
803 tproc.destroy = NULL;
804 tproc.invoke = fc_bit_return_invoke;
805 fc_register_target ("return", tproc);
807 memset (&tproc, 0, sizeof (tproc));
808 tproc.create = fc_bit_write_create;
809 tproc.destroy = fc_bit_write_destroy;
810 tproc.invoke = fc_bit_write_invoke;
811 fc_register_target ("write", tproc);
813 done++;
814 return (0);
815 } /* }}} int fc_init_once */
817 /*
818 * Public functions
819 */
820 /* Add a match to list of available matches. */
821 int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
822 {
823 fc_match_t *m;
825 DEBUG ("fc_register_match (%s);", name);
827 m = (fc_match_t *) malloc (sizeof (*m));
828 if (m == NULL)
829 return (-ENOMEM);
830 memset (m, 0, sizeof (*m));
832 sstrncpy (m->name, name, sizeof (m->name));
833 memcpy (&m->proc, &proc, sizeof (m->proc));
834 m->next = NULL;
836 if (match_list_head == NULL)
837 {
838 match_list_head = m;
839 }
840 else
841 {
842 fc_match_t *ptr;
844 ptr = match_list_head;
845 while (ptr->next != NULL)
846 ptr = ptr->next;
848 ptr->next = m;
849 }
851 return (0);
852 } /* }}} int fc_register_match */
854 /* Add a target to list of available targets. */
855 int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
856 {
857 fc_target_t *t;
859 DEBUG ("fc_register_target (%s);", name);
861 t = (fc_target_t *) malloc (sizeof (*t));
862 if (t == NULL)
863 return (-ENOMEM);
864 memset (t, 0, sizeof (*t));
866 sstrncpy (t->name, name, sizeof (t->name));
867 memcpy (&t->proc, &proc, sizeof (t->proc));
868 t->next = NULL;
870 if (target_list_head == NULL)
871 {
872 target_list_head = t;
873 }
874 else
875 {
876 fc_target_t *ptr;
878 ptr = target_list_head;
879 while (ptr->next != NULL)
880 ptr = ptr->next;
882 ptr->next = t;
883 }
885 return (0);
886 } /* }}} int fc_register_target */
888 fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */
889 {
890 fc_chain_t *chain;
892 if (chain_name == NULL)
893 return (NULL);
895 for (chain = chain_list_head; chain != NULL; chain = chain->next)
896 if (strcasecmp (chain_name, chain->name) == 0)
897 return (chain);
899 return (NULL);
900 } /* }}} int fc_chain_get_by_name */
902 int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
903 fc_chain_t *chain)
904 {
905 fc_rule_t *rule;
906 fc_target_t *target;
907 int status = FC_TARGET_CONTINUE;
909 if (chain == NULL)
910 return (-1);
912 DEBUG ("fc_process_chain (chain = %s);", chain->name);
914 for (rule = chain->rules; rule != NULL; rule = rule->next)
915 {
916 fc_match_t *match;
917 status = FC_TARGET_CONTINUE;
919 if (rule->name[0] != 0)
920 {
921 DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
922 chain->name, rule->name);
923 }
925 /* N. B.: rule->matches may be NULL. */
926 for (match = rule->matches; match != NULL; match = match->next)
927 {
928 /* FIXME: Pass the meta-data to match targets here (when implemented). */
929 status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
930 &match->user_data);
931 if (status < 0)
932 {
933 WARNING ("fc_process_chain (%s): A match failed.", chain->name);
934 break;
935 }
936 else if (status != FC_MATCH_MATCHES)
937 break;
938 }
940 /* for-loop has been aborted: Either error or no match. */
941 if (match != NULL)
942 {
943 status = FC_TARGET_CONTINUE;
944 continue;
945 }
947 if (rule->name[0] != 0)
948 {
949 DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
950 chain->name, rule->name);
951 }
953 for (target = rule->targets; target != NULL; target = target->next)
954 {
955 /* If we get here, all matches have matched the value. Execute the
956 * target. */
957 /* FIXME: Pass the meta-data to match targets here (when implemented). */
958 status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
959 &target->user_data);
960 if (status < 0)
961 {
962 WARNING ("fc_process_chain (%s): A target failed.", chain->name);
963 continue;
964 }
965 else if (status == FC_TARGET_CONTINUE)
966 continue;
967 else if (status == FC_TARGET_STOP)
968 break;
969 else if (status == FC_TARGET_RETURN)
970 break;
971 else
972 {
973 WARNING ("fc_process_chain (%s): Unknown return value "
974 "from target `%s': %i",
975 chain->name, target->name, status);
976 }
977 }
979 if ((status == FC_TARGET_STOP) || (status == FC_TARGET_RETURN))
980 {
981 if (rule->name[0] != 0)
982 {
983 DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
984 "the %s condition.",
985 chain->name, rule->name,
986 (status == FC_TARGET_STOP) ? "stop" : "return");
987 }
988 break;
989 }
990 } /* for (rule) */
992 if ((status == FC_TARGET_STOP) || (status == FC_TARGET_RETURN))
993 return (status);
995 DEBUG ("fc_process_chain (%s): Executing the default targets.",
996 chain->name);
998 status = FC_TARGET_CONTINUE;
999 for (target = chain->targets; target != NULL; target = target->next)
1000 {
1001 /* If we get here, all matches have matched the value. Execute the
1002 * target. */
1003 /* FIXME: Pass the meta-data to match targets here (when implemented). */
1004 status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
1005 &target->user_data);
1006 if (status < 0)
1007 {
1008 WARNING ("fc_process_chain (%s): The default target failed.",
1009 chain->name);
1010 }
1011 else if (status == FC_TARGET_CONTINUE)
1012 continue;
1013 else if (status == FC_TARGET_STOP)
1014 break;
1015 else if (status == FC_TARGET_RETURN)
1016 break;
1017 else
1018 {
1019 WARNING ("fc_process_chain (%s): Unknown return value "
1020 "from target `%s': %i",
1021 chain->name, target->name, status);
1022 }
1023 }
1025 if ((status == FC_TARGET_STOP)
1026 || (status == FC_TARGET_RETURN))
1027 {
1028 assert (target != NULL);
1029 DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
1030 "the %s condition.",
1031 chain->name, target->name,
1032 (status == FC_TARGET_STOP) ? "stop" : "return");
1033 if (status == FC_TARGET_STOP)
1034 return (FC_TARGET_STOP);
1035 else
1036 return (FC_TARGET_CONTINUE);
1037 }
1039 DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
1040 chain->name);
1042 return (FC_TARGET_CONTINUE);
1043 } /* }}} int fc_process_chain */
1045 /* Iterate over all rules in the chain and execute all targets for which all
1046 * matches match. */
1047 int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */
1048 {
1049 /* FIXME: Pass the meta-data to match targets here (when implemented). */
1050 return (fc_bit_write_invoke (ds, vl,
1051 /* meta = */ NULL, /* user_data = */ NULL));
1052 } /* }}} int fc_default_action */
1054 int fc_configure (const oconfig_item_t *ci) /* {{{ */
1055 {
1056 fc_init_once ();
1058 if (ci == NULL)
1059 return (-EINVAL);
1061 if (strcasecmp ("Chain", ci->key) == 0)
1062 return (fc_config_add_chain (ci));
1064 WARNING ("Filter subsystem: Unknown top level config option `%s'.",
1065 ci->key);
1067 return (-1);
1068 } /* }}} int fc_configure */
1070 /* vim: set sw=2 sts=2 et fdm=marker : */