src/utils_cmd_flush.c: Fix parsing of the "timeout" option.
[collectd.git] / src / filter_chain.c
1 /**
2  * collectd - src/filter_chain.h
3  * Copyright (C) 2008-2010  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "configfile.h"
24 #include "plugin.h"
25 #include "common.h"
26 #include "filter_chain.h"
27
28 /*
29  * Data types
30  */
31 /* List of matches, used in fc_rule_t and for the global `match_list_head'
32  * variable. */
33 struct fc_match_s;
34 typedef struct fc_match_s fc_match_t; /* {{{ */
35 struct fc_match_s
36 {
37   char name[DATA_MAX_NAME_LEN];
38   match_proc_t proc;
39   void *user_data;
40   fc_match_t *next;
41 }; /* }}} */
42
43 /* List of targets, used in fc_rule_t and for the global `target_list_head'
44  * variable. */
45 struct fc_target_s;
46 typedef struct fc_target_s fc_target_t; /* {{{ */
47 struct fc_target_s
48 {
49   char name[DATA_MAX_NAME_LEN];
50   void *user_data;
51   target_proc_t proc;
52   fc_target_t *next;
53 }; /* }}} */
54
55 /* List of rules, used in fc_chain_t */
56 struct fc_rule_s;
57 typedef struct fc_rule_s fc_rule_t; /* {{{ */
58 struct fc_rule_s
59 {
60   char name[DATA_MAX_NAME_LEN];
61   fc_match_t  *matches;
62   fc_target_t *targets;
63   fc_rule_t *next;
64 }; /* }}} */
65
66 /* List of chains, used for `chain_list_head' */
67 struct fc_chain_s /* {{{ */
68 {
69   char name[DATA_MAX_NAME_LEN];
70   fc_rule_t   *rules;
71   fc_target_t *targets;
72   fc_chain_t  *next;
73 }; /* }}} */
74
75 /*
76  * Global variables
77  */
78 static fc_match_t  *match_list_head;
79 static fc_target_t *target_list_head;
80 static fc_chain_t  *chain_list_head;
81
82 /*
83  * Private functions
84  */
85 static void fc_free_matches (fc_match_t *m) /* {{{ */
86 {
87   if (m == NULL)
88     return;
89
90   if (m->proc.destroy != NULL)
91     (*m->proc.destroy) (&m->user_data);
92   else if (m->user_data != NULL)
93   {
94     ERROR ("Filter sybsystem: fc_free_matches: There is user data, but no "
95         "destroy functions has been specified. "
96         "Memory will probably be lost!");
97   }
98
99   if (m->next != NULL)
100     fc_free_matches (m->next);
101
102   free (m);
103 } /* }}} void fc_free_matches */
104
105 static void fc_free_targets (fc_target_t *t) /* {{{ */
106 {
107   if (t == NULL)
108     return;
109
110   if (t->proc.destroy != NULL)
111     (*t->proc.destroy) (&t->user_data);
112   else if (t->user_data != NULL)
113   {
114     ERROR ("Filter sybsystem: fc_free_targets: There is user data, but no "
115         "destroy functions has been specified. "
116         "Memory will probably be lost!");
117   }
118
119   if (t->next != NULL)
120     fc_free_targets (t->next);
121
122   free (t);
123 } /* }}} void fc_free_targets */
124
125 static void fc_free_rules (fc_rule_t *r) /* {{{ */
126 {
127   if (r == NULL)
128     return;
129
130   fc_free_matches (r->matches);
131   fc_free_targets (r->targets);
132
133   if (r->next != NULL)
134     fc_free_rules (r->next);
135
136   free (r);
137 } /* }}} void fc_free_rules */
138
139 static void fc_free_chains (fc_chain_t *c) /* {{{ */
140 {
141   if (c == NULL)
142     return;
143
144   fc_free_rules (c->rules);
145   fc_free_targets (c->targets);
146
147   if (c->next != NULL)
148     fc_free_chains (c->next);
149
150   free (c);
151 } /* }}} void fc_free_chains */
152
153 static char *fc_strdup (const char *orig) /* {{{ */
154 {
155   size_t sz;
156   char *dest;
157
158   if (orig == NULL)
159     return (NULL);
160
161   sz = strlen (orig) + 1;
162   dest = (char *) malloc (sz);
163   if (dest == NULL)
164     return (NULL);
165
166   memcpy (dest, orig, sz);
167
168   return (dest);
169 } /* }}} char *fc_strdup */
170
171 /*
172  * Configuration.
173  *
174  * The configuration looks somewhat like this:
175  *
176  *  <Chain "PreCache">
177  *    <Rule>
178  *      <Match "regex">
179  *        Plugin "^mysql$"
180  *        Type "^mysql_command$"
181  *        TypeInstance "^show_"
182  *      </Match>
183  *      <Target "drop">
184  *      </Target>
185  *    </Rule>
186  *
187  *    <Target "write">
188  *      Plugin "rrdtool"
189  *    </Target>
190  *  </Chain>
191  */
192 static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
193     oconfig_item_t *ci)
194 {
195   fc_match_t *m;
196   fc_match_t *ptr;
197   int status;
198
199   if ((ci->values_num != 1)
200       || (ci->values[0].type != OCONFIG_TYPE_STRING))
201   {
202     WARNING ("Filter subsystem: `Match' blocks require "
203         "exactly one string argument.");
204     return (-1);
205   }
206
207   ptr = match_list_head;
208   while (ptr != NULL)
209   {
210     if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
211       break;
212     ptr = ptr->next;
213   }
214
215   if (ptr == NULL)
216   {
217     WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
218         "Did you load the appropriate plugin?",
219         ci->values[0].value.string);
220     return (-1);
221   }
222
223   m = (fc_match_t *) malloc (sizeof (*m));
224   if (m == NULL)
225   {
226     ERROR ("fc_config_add_match: malloc failed.");
227     return (-1);
228   }
229   memset (m, 0, sizeof (*m));
230
231   sstrncpy (m->name, ptr->name, sizeof (m->name));
232   memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
233   m->user_data = NULL;
234   m->next = NULL;
235
236   if (m->proc.create != NULL)
237   {
238     status = (*m->proc.create) (ci, &m->user_data);
239     if (status != 0)
240     {
241       WARNING ("Filter subsystem: Failed to create a %s match.",
242           m->name);
243       fc_free_matches (m);
244       return (-1);
245     }
246   }
247
248   if (*matches_head != NULL)
249   {
250     ptr = *matches_head;
251     while (ptr->next != NULL)
252       ptr = ptr->next;
253
254     ptr->next = m;
255   }
256   else
257   {
258     *matches_head = m;
259   }
260
261   return (0);
262 } /* }}} int fc_config_add_match */
263
264 static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
265     oconfig_item_t *ci)
266 {
267   fc_target_t *t;
268   fc_target_t *ptr;
269   int status;
270
271   if ((ci->values_num != 1)
272       || (ci->values[0].type != OCONFIG_TYPE_STRING))
273   {
274     WARNING ("Filter subsystem: `Target' blocks require "
275         "exactly one string argument.");
276     return (-1);
277   }
278
279   ptr = target_list_head;
280   while (ptr != NULL)
281   {
282     if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
283       break;
284     ptr = ptr->next;
285   }
286
287   if (ptr == NULL)
288   {
289     WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
290         "Did you load the appropriate plugin?",
291         ci->values[0].value.string);
292     return (-1);
293   }
294
295   t = (fc_target_t *) malloc (sizeof (*t));
296   if (t == NULL)
297   {
298     ERROR ("fc_config_add_target: malloc failed.");
299     return (-1);
300   }
301   memset (t, 0, sizeof (*t));
302
303   sstrncpy (t->name, ptr->name, sizeof (t->name));
304   memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
305   t->user_data = NULL;
306   t->next = NULL;
307
308   if (t->proc.create != NULL)
309   {
310     status = (*t->proc.create) (ci, &t->user_data);
311     if (status != 0)
312     {
313       WARNING ("Filter subsystem: Failed to create a %s target.",
314           t->name);
315       fc_free_targets (t);
316       return (-1);
317     }
318   }
319   else
320   {
321     t->user_data = NULL;
322   }
323   
324   if (*targets_head != NULL)
325   {
326     ptr = *targets_head;
327     while (ptr->next != NULL)
328       ptr = ptr->next;
329
330     ptr->next = t;
331   }
332   else
333   {
334     *targets_head = t;
335   }
336
337   return (0);
338 } /* }}} int fc_config_add_target */
339
340 static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
341     oconfig_item_t *ci)
342 {
343   fc_rule_t *rule;
344   char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
345   int status = 0;
346   int i;
347
348   if (ci->values_num > 1)
349   {
350     WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
351     return (-1);
352   }
353   else if ((ci->values_num == 1)
354       && (ci->values[0].type != OCONFIG_TYPE_STRING))
355   {
356     WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
357         "or no argument at all.");
358     return (-1);
359   }
360
361   rule = (fc_rule_t *) malloc (sizeof (*rule));
362   if (rule == NULL)
363   {
364     ERROR ("fc_config_add_rule: malloc failed.");
365     return (-1);
366   }
367   memset (rule, 0, sizeof (*rule));
368   rule->next = NULL;
369
370   if (ci->values_num == 1)
371   {
372     sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
373     ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
374         ci->values[0].value.string);
375   }
376
377   for (i = 0; i < ci->children_num; i++)
378   {
379     oconfig_item_t *option = ci->children + i;
380     status = 0;
381
382     if (strcasecmp ("Match", option->key) == 0)
383       status = fc_config_add_match (&rule->matches, option);
384     else if (strcasecmp ("Target", option->key) == 0)
385       status = fc_config_add_target (&rule->targets, option);
386     else
387     {
388       WARNING ("Filter subsystem: %s: Option `%s' not allowed "
389           "inside a <Rule> block.", rule_name, option->key);
390       status = -1;
391     }
392
393     if (status != 0)
394       break;
395   } /* for (ci->children) */
396
397   /* Additional sanity checking. */
398   while (status == 0)
399   {
400     if (rule->targets == NULL)
401     {
402       WARNING ("Filter subsystem: %s: No target has been specified.",
403           rule_name);
404       status = -1;
405       break;
406     }
407
408     break;
409   } /* while (status == 0) */
410
411   if (status != 0)
412   {
413     fc_free_rules (rule);
414     return (-1);
415   }
416
417   if (chain->rules != NULL)
418   {
419     fc_rule_t *ptr;
420
421     ptr = chain->rules;
422     while (ptr->next != NULL)
423       ptr = ptr->next;
424
425     ptr->next = rule;
426   }
427   else
428   {
429     chain->rules = rule;
430   }
431
432   return (0);
433 } /* }}} int fc_config_add_rule */
434
435 static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
436 {
437   fc_chain_t *chain;
438   int status = 0;
439   int i;
440
441   if ((ci->values_num != 1)
442       || (ci->values[0].type != OCONFIG_TYPE_STRING))
443   {
444     WARNING ("Filter subsystem: <Chain> blocks require exactly one "
445         "string argument.");
446     return (-1);
447   }
448
449   chain = (fc_chain_t *) malloc (sizeof (*chain));
450   if (chain == NULL)
451   {
452     ERROR ("fc_config_add_chain: malloc failed.");
453     return (-1);
454   }
455   memset (chain, 0, sizeof (*chain));
456   sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
457   chain->rules = NULL;
458   chain->targets = NULL;
459   chain->next = NULL;
460
461   for (i = 0; i < ci->children_num; i++)
462   {
463     oconfig_item_t *option = ci->children + i;
464     status = 0;
465
466     if (strcasecmp ("Rule", option->key) == 0)
467       status = fc_config_add_rule (chain, option);
468     else if (strcasecmp ("Target", option->key) == 0)
469       status = fc_config_add_target (&chain->targets, option);
470     else
471     {
472       WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
473           "inside a <Chain> block.", chain->name, option->key);
474       status = -1;
475     }
476
477     if (status != 0)
478       break;
479   } /* for (ci->children) */
480
481   if (status != 0)
482   {
483     fc_free_chains (chain);
484     return (-1);
485   }
486
487   if (chain_list_head != NULL)
488   {
489     fc_chain_t *ptr;
490
491     ptr = chain_list_head;
492     while (ptr->next != NULL)
493       ptr = ptr->next;
494
495     ptr->next = chain;
496   }
497   else
498   {
499     chain_list_head = chain;
500   }
501
502   return (0);
503 } /* }}} int fc_config_add_chain */
504
505 /*
506  * Built-in target "jump"
507  *
508  * Prefix `bit' like `_b_uilt-_i_n _t_arget'
509  */
510 static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
511     void **user_data)
512 {
513   oconfig_item_t *ci_chain;
514
515   if (ci->children_num != 1)
516   {
517     ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
518         "one `Chain' argument!");
519     return (-1);
520   }
521
522   ci_chain = ci->children;
523   if (strcasecmp ("Chain", ci_chain->key) != 0)
524   {
525     ERROR ("Filter subsystem: The built-in target `jump' does not "
526         "support the configuration option `%s'.",
527         ci_chain->key);
528     return (-1);
529   }
530
531   if ((ci_chain->values_num != 1)
532       || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
533   {
534     ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
535         "needs exactly one string argument.");
536     return (-1);
537   }
538
539   *user_data = fc_strdup (ci_chain->values[0].value.string);
540   if (*user_data == NULL)
541   {
542     ERROR ("fc_bit_jump_create: fc_strdup failed.");
543     return (-1);
544   }
545
546   return (0);
547 } /* }}} int fc_bit_jump_create */
548
549 static int fc_bit_jump_destroy (void **user_data) /* {{{ */
550 {
551   if (user_data != NULL)
552   {
553     free (*user_data);
554     *user_data = NULL;
555   }
556
557   return (0);
558 } /* }}} int fc_bit_jump_destroy */
559
560 static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
561     value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
562     void **user_data)
563 {
564   char *chain_name;
565   fc_chain_t *chain;
566   int status;
567
568   chain_name = *user_data;
569
570   for (chain = chain_list_head; chain != NULL; chain = chain->next)
571     if (strcasecmp (chain_name, chain->name) == 0)
572       break;
573
574   if (chain == NULL)
575   {
576     ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
577         "named `%s'.", chain_name);
578     return (-1);
579   }
580
581   status = fc_process_chain (ds, vl, chain);
582   if (status < 0)
583     return (status);
584   else if (status == FC_TARGET_STOP)
585     return (FC_TARGET_STOP);
586   else
587     return (FC_TARGET_CONTINUE);
588 } /* }}} int fc_bit_jump_invoke */
589
590 static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
591     value_list_t __attribute__((unused)) *vl,
592     notification_meta_t __attribute__((unused)) **meta,
593     void __attribute__((unused)) **user_data)
594 {
595   return (FC_TARGET_STOP);
596 } /* }}} int fc_bit_stop_invoke */
597
598 static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
599     value_list_t __attribute__((unused)) *vl,
600     notification_meta_t __attribute__((unused)) **meta,
601     void __attribute__((unused)) **user_data)
602 {
603   return (FC_TARGET_RETURN);
604 } /* }}} int fc_bit_return_invoke */
605
606 static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
607     void **user_data)
608 {
609   int i;
610
611   char **plugin_list;
612   size_t plugin_list_len;
613
614   plugin_list = NULL;
615   plugin_list_len = 0;
616
617   for (i = 0; i < ci->children_num; i++)
618   {
619     oconfig_item_t *child = ci->children + i;
620     char **temp;
621     int j;
622
623     if (strcasecmp ("Plugin", child->key) != 0)
624     {
625       ERROR ("Filter subsystem: The built-in target `write' does not "
626           "support the configuration option `%s'.",
627           child->key);
628       continue;
629     }
630
631     for (j = 0; j < child->values_num; j++)
632     {
633       if (child->values[j].type != OCONFIG_TYPE_STRING)
634       {
635         ERROR ("Filter subsystem: Built-in target `write': "
636             "The `Plugin' option accepts only string arguments.");
637         continue;
638       }
639
640       temp = (char **) realloc (plugin_list, (plugin_list_len + 2)
641           * (sizeof (*plugin_list)));
642       if (temp == NULL)
643       {
644         ERROR ("fc_bit_write_create: realloc failed.");
645         continue;
646       }
647       plugin_list = temp;
648
649       plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string);
650       if (plugin_list[plugin_list_len] == NULL)
651       {
652         ERROR ("fc_bit_write_create: fc_strdup failed.");
653         continue;
654       }
655       plugin_list_len++;
656       plugin_list[plugin_list_len] = NULL;
657     } /* for (j = 0; j < child->values_num; j++) */
658   } /* for (i = 0; i < ci->children_num; i++) */
659
660   *user_data = plugin_list;
661
662   return (0);
663 } /* }}} int fc_bit_write_create */
664
665 static int fc_bit_write_destroy (void **user_data) /* {{{ */
666 {
667   char **plugin_list;
668   size_t i;
669
670   if ((user_data == NULL) || (*user_data == NULL))
671     return (0);
672
673   plugin_list = *user_data;
674
675   for (i = 0; plugin_list[i] != NULL; i++)
676     free (plugin_list[i]);
677   free (plugin_list);
678
679   return (0);
680 } /* }}} int fc_bit_write_destroy */
681
682 static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
683     value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
684     void **user_data)
685 {
686   char **plugin_list;
687   int status;
688
689   plugin_list = NULL;
690   if (user_data != NULL)
691     plugin_list = *user_data;
692
693   if ((plugin_list == NULL) || (plugin_list[0] == NULL))
694   {
695     status = plugin_write (/* plugin = */ NULL, ds, vl);
696     if (status == ENOENT)
697     {
698       INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
699           "all write plugins failed with status %i (ENOENT). "
700           "Most likely this means you didn't load any write plugins.",
701           status);
702     }
703     else if (status != 0)
704     {
705       INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
706           "all write plugins failed with status %i.", status);
707     }
708   }
709   else
710   {
711     size_t i;
712
713     for (i = 0; plugin_list[i] != NULL; i++)
714     {
715       status = plugin_write (plugin_list[i], ds, vl);
716       if (status != 0)
717       {
718         INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
719             "the `%s' plugin failed with status %i.", plugin_list[i], status);
720       }
721     } /* for (i = 0; plugin_list[i] != NULL; i++) */
722   }
723
724   return (FC_TARGET_CONTINUE);
725 } /* }}} int fc_bit_write_invoke */
726
727 static int fc_init_once (void) /* {{{ */
728 {
729   static int done = 0;
730   target_proc_t tproc;
731
732   if (done != 0)
733     return (0);
734
735   memset (&tproc, 0, sizeof (tproc));
736   tproc.create  = fc_bit_jump_create;
737   tproc.destroy = fc_bit_jump_destroy;
738   tproc.invoke  = fc_bit_jump_invoke;
739   fc_register_target ("jump", tproc);
740
741   memset (&tproc, 0, sizeof (tproc));
742   tproc.create  = NULL;
743   tproc.destroy = NULL;
744   tproc.invoke  = fc_bit_stop_invoke;
745   fc_register_target ("stop", tproc);
746
747   memset (&tproc, 0, sizeof (tproc));
748   tproc.create  = NULL;
749   tproc.destroy = NULL;
750   tproc.invoke  = fc_bit_return_invoke;
751   fc_register_target ("return", tproc);
752
753   memset (&tproc, 0, sizeof (tproc));
754   tproc.create  = fc_bit_write_create;
755   tproc.destroy = fc_bit_write_destroy;
756   tproc.invoke  = fc_bit_write_invoke;
757   fc_register_target ("write", tproc);
758
759   done++;
760   return (0);
761 } /* }}} int fc_init_once */
762
763 /*
764  * Public functions
765  */
766 /* Add a match to list of available matches. */
767 int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
768 {
769   fc_match_t *m;
770
771   DEBUG ("fc_register_match (%s);", name);
772
773   m = (fc_match_t *) malloc (sizeof (*m));
774   if (m == NULL)
775     return (-ENOMEM);
776   memset (m, 0, sizeof (*m));
777
778   sstrncpy (m->name, name, sizeof (m->name));
779   memcpy (&m->proc, &proc, sizeof (m->proc));
780   m->next = NULL;
781
782   if (match_list_head == NULL)
783   {
784     match_list_head = m;
785   }
786   else
787   {
788     fc_match_t *ptr;
789
790     ptr = match_list_head;
791     while (ptr->next != NULL)
792       ptr = ptr->next;
793
794     ptr->next = m;
795   }
796
797   return (0);
798 } /* }}} int fc_register_match */
799
800 /* Add a target to list of available targets. */
801 int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
802 {
803   fc_target_t *t;
804
805   DEBUG ("fc_register_target (%s);", name);
806
807   t = (fc_target_t *) malloc (sizeof (*t));
808   if (t == NULL)
809     return (-ENOMEM);
810   memset (t, 0, sizeof (*t));
811
812   sstrncpy (t->name, name, sizeof (t->name));
813   memcpy (&t->proc, &proc, sizeof (t->proc));
814   t->next = NULL;
815
816   if (target_list_head == NULL)
817   {
818     target_list_head = t;
819   }
820   else
821   {
822     fc_target_t *ptr;
823
824     ptr = target_list_head;
825     while (ptr->next != NULL)
826       ptr = ptr->next;
827
828     ptr->next = t;
829   }
830
831   return (0);
832 } /* }}} int fc_register_target */
833
834 fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */
835 {
836   fc_chain_t *chain;
837
838   if (chain_name == NULL)
839     return (NULL);
840
841   for (chain = chain_list_head; chain != NULL; chain = chain->next)
842     if (strcasecmp (chain_name, chain->name) == 0)
843       return (chain);
844
845   return (NULL);
846 } /* }}} int fc_chain_get_by_name */
847
848 int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
849     fc_chain_t *chain)
850 {
851   fc_rule_t *rule;
852   fc_target_t *target;
853   int status;
854
855   if (chain == NULL)
856     return (-1);
857
858   DEBUG ("fc_process_chain (chain = %s);", chain->name);
859
860   status = FC_TARGET_CONTINUE;
861   for (rule = chain->rules; rule != NULL; rule = rule->next)
862   {
863     fc_match_t *match;
864
865     if (rule->name[0] != 0)
866     {
867       DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
868           chain->name, rule->name);
869     }
870
871     /* N. B.: rule->matches may be NULL. */
872     for (match = rule->matches; match != NULL; match = match->next)
873     {
874       /* FIXME: Pass the meta-data to match targets here (when implemented). */
875       status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
876           &match->user_data);
877       if (status < 0)
878       {
879         WARNING ("fc_process_chain (%s): A match failed.", chain->name);
880         break;
881       }
882       else if (status != FC_MATCH_MATCHES)
883         break;
884     }
885
886     /* for-loop has been aborted: Either error or no match. */
887     if (match != NULL)
888     {
889       status = FC_TARGET_CONTINUE;
890       continue;
891     }
892
893     if (rule->name[0] != 0)
894     {
895       DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
896           chain->name, rule->name);
897     }
898
899     for (target = rule->targets; target != NULL; target = target->next)
900     {
901       /* If we get here, all matches have matched the value. Execute the
902        * target. */
903       /* FIXME: Pass the meta-data to match targets here (when implemented). */
904       status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
905           &target->user_data);
906       if (status < 0)
907       {
908         WARNING ("fc_process_chain (%s): A target failed.", chain->name);
909         continue;
910       }
911       else if (status == FC_TARGET_CONTINUE)
912         continue;
913       else if (status == FC_TARGET_STOP)
914         break;
915       else if (status == FC_TARGET_RETURN)
916         break;
917       else
918       {
919         WARNING ("fc_process_chain (%s): Unknown return value "
920             "from target `%s': %i",
921             chain->name, target->name, status);
922       }
923     }
924
925     if ((status == FC_TARGET_STOP)
926         || (status == FC_TARGET_RETURN))
927     {
928       if (rule->name[0] != 0)
929       {
930         DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
931             "the %s condition.",
932             chain->name, rule->name,
933             (status == FC_TARGET_STOP) ? "stop" : "return");
934       }
935       break;
936     }
937     else
938     {
939       status = FC_TARGET_CONTINUE;
940     }
941   } /* for (rule) */
942
943   if (status == FC_TARGET_STOP)
944     return (FC_TARGET_STOP);
945   else if (status == FC_TARGET_RETURN)
946     return (FC_TARGET_CONTINUE);
947
948   /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */
949   if (rule != NULL)
950     return (FC_TARGET_CONTINUE);
951
952   DEBUG ("fc_process_chain (%s): Executing the default targets.",
953       chain->name);
954
955   status = FC_TARGET_CONTINUE;
956   for (target = chain->targets; target != NULL; target = target->next)
957   {
958     /* If we get here, all matches have matched the value. Execute the
959      * target. */
960     /* FIXME: Pass the meta-data to match targets here (when implemented). */
961     status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
962         &target->user_data);
963     if (status < 0)
964     {
965       WARNING ("fc_process_chain (%s): The default target failed.",
966           chain->name);
967     }
968     else if (status == FC_TARGET_CONTINUE)
969       continue;
970     else if (status == FC_TARGET_STOP)
971       break;
972     else if (status == FC_TARGET_RETURN)
973       break;
974     else
975     {
976       WARNING ("fc_process_chain (%s): Unknown return value "
977           "from target `%s': %i",
978           chain->name, target->name, status);
979     }
980   }
981
982   if ((status == FC_TARGET_STOP)
983       || (status == FC_TARGET_RETURN))
984   {
985     assert (target != NULL);
986     DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
987         "the %s condition.",
988         chain->name, target->name,
989         (status == FC_TARGET_STOP) ? "stop" : "return");
990     if (status == FC_TARGET_STOP)
991       return (FC_TARGET_STOP);
992     else
993       return (FC_TARGET_CONTINUE);
994   }
995
996   DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
997       chain->name);
998
999   return (FC_TARGET_CONTINUE);
1000 } /* }}} int fc_process_chain */
1001
1002 /* Iterate over all rules in the chain and execute all targets for which all
1003  * matches match. */
1004 int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */
1005 {
1006   /* FIXME: Pass the meta-data to match targets here (when implemented). */
1007   return (fc_bit_write_invoke (ds, vl,
1008         /* meta = */ NULL, /* user_data = */ NULL));
1009 } /* }}} int fc_default_action */
1010
1011 int fc_configure (const oconfig_item_t *ci) /* {{{ */
1012 {
1013   fc_init_once ();
1014
1015   if (ci == NULL)
1016     return (-EINVAL);
1017
1018   if (strcasecmp ("Chain", ci->key) == 0)
1019     return (fc_config_add_chain (ci));
1020
1021   WARNING ("Filter subsystem: Unknown top level config option `%s'.",
1022       ci->key);
1023
1024   return (-1);
1025 } /* }}} int fc_configure */
1026
1027 /* vim: set sw=2 sts=2 et fdm=marker : */