root/src/noit_filters.c

Revision 70c30ef22f6d1312375e0392164d755db5824cb5, 24.6 kB (checked in by Phil Maddox <philip.maddox@circonus.com>, 1 week ago)

Use mtevAssert and mtevFatal Instead Of assert() and abort()

Use libmtev calls to safely flush logs and abort rather than calling
the assert and abort calls directly.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <mtev_defines.h>
35
36 #include <mtev_log.h>
37 #include <mtev_hash.h>
38 #include <mtev_atomic.h>
39 #include <mtev_watchdog.h>
40 #include <mtev_capabilities_listener.h>
41 #include <mtev_conf.h>
42
43 #include "noit_mtev_bridge.h"
44 #include "noit_check.h"
45 #include "noit_conf_checks.h"
46 #include "noit_filters.h"
47
48 #include <pcre.h>
49 #include <libxml/tree.h>
50
51 static mtev_hash_table *filtersets = NULL;
52 static pthread_mutex_t filterset_lock;
53 static pcre *fallback_no_match = NULL;
54 #define LOCKFS() pthread_mutex_lock(&filterset_lock)
55 #define UNLOCKFS() pthread_mutex_unlock(&filterset_lock)
56
57 typedef enum { NOIT_FILTER_ACCEPT, NOIT_FILTER_DENY, NOIT_FILTER_SKIPTO } noit_ruletype_t;
58 typedef struct _filterrule {
59   char *ruleid;
60   char *skipto;
61   struct _filterrule *skipto_rule;
62   noit_ruletype_t type;
63   pcre *target_override;
64   pcre *target;
65   pcre_extra *target_e;
66   mtev_hash_table *target_ht;
67   int target_auto_hash_max;
68   pcre *module_override;
69   pcre *module;
70   pcre_extra *module_e;
71   mtev_hash_table *module_ht;
72   int module_auto_hash_max;
73   pcre *name_override;
74   pcre *name;
75   pcre_extra *name_e;
76   mtev_hash_table *name_ht;
77   int name_auto_hash_max;
78   pcre *metric_override;
79   pcre *metric;
80   pcre_extra *metric_e;
81   mtev_hash_table *metric_ht;
82   int metric_auto_hash_max;
83   struct _filterrule *next;
84 } filterrule_t;
85
86 typedef struct {
87   mtev_atomic32_t ref_cnt;
88   char *name;
89   filterrule_t *rules;
90 } filterset_t;
91
92 #define FRF(r,a) do { \
93   if(r->a) pcre_free(r->a); \
94   if(r->a##_e) pcre_free(r->a##_e); \
95   if(r->a##_ht) { \
96     mtev_hash_destroy(r->a##_ht, free, NULL); \
97     free(r->a##_ht); \
98   } \
99 } while(0)
100
101 static void
102 filterrule_free(void *vp) {
103   filterrule_t *r = vp;
104   FRF(r,target);
105   FRF(r,module);
106   FRF(r,name);
107   FRF(r,metric);
108   free(r->ruleid);
109   free(r->skipto);
110   free(r);
111 }
112 static void
113 filterset_free(void *vp) {
114   filterset_t *fs = vp;
115   filterrule_t *r;
116   if(mtev_atomic_dec32(&fs->ref_cnt) != 0) return;
117   mtevL(noit_debug, "Freeing filterset [%d]: %s\n", fs->ref_cnt, fs->name);
118   while(fs->rules) {
119     r = fs->rules->next;
120     filterrule_free(fs->rules);
121     fs->rules = r;
122   }
123   if(fs->name) free(fs->name);
124   free(fs);
125 }
126 void
127 noit_filter_compile_add(mtev_conf_section_t setinfo) {
128   mtev_conf_section_t *rules;
129   int j, fcnt;
130   char filterset_name[256];
131   filterset_t *set;
132   if(!mtev_conf_get_stringbuf(setinfo, "@name",
133                               filterset_name, sizeof(filterset_name))) {
134     mtevL(noit_error,
135           "filterset with no name, skipping as it cannot be referenced.\n");
136     return;
137   }
138   set = calloc(1, sizeof(*set));
139   set->ref_cnt = 1;
140   set->name = strdup(filterset_name);
141
142   rules = mtev_conf_get_sections(setinfo, "rule", &fcnt);
143   /* Here we will work through the list backwards pushing the rules on
144    * the front of the list.  That way we can simply walk them in order
145    * for the application process.
146    */
147   mtevL(noit_debug, "Compiling filterset '%s'\n", set->name);
148   for(j=fcnt-1; j>=0; j--) {
149     filterrule_t *rule;
150     char buffer[256];
151     if(!mtev_conf_get_stringbuf(rules[j], "@type", buffer, sizeof(buffer)) ||
152        (strcmp(buffer, "accept") && strcmp(buffer, "allow") && strcmp(buffer, "deny") &&
153         strncmp(buffer, "skipto:", strlen("skipto:")))) {
154       mtevL(noit_error, "rule must have type 'accept' or 'allow' or 'deny' or 'skipto:'\n");
155       continue;
156     }
157     mtevL(noit_debug, "Prepending %s into %s\n", buffer, set->name);
158     rule = calloc(1, sizeof(*rule));
159     if(!strncasecmp(buffer, "skipto:", strlen("skipto:"))) {
160       rule->type = NOIT_FILTER_SKIPTO;
161       rule->skipto = strdup(buffer+strlen("skipto:"));
162     }
163     else {
164       rule->type = (!strcmp(buffer, "accept") || !strcmp(buffer, "allow")) ?
165                      NOIT_FILTER_ACCEPT : NOIT_FILTER_DENY;
166     }
167
168     if(mtev_conf_get_stringbuf(rules[j], "@id", buffer, sizeof(buffer))) {
169       rule->ruleid = strdup(buffer);
170     }
171     /* Compile any hash tables, should they exist */
172 #define HT_COMPILE(rname) do { \
173     mtev_conf_section_t *htentries; \
174     int hte_cnt, hti, tablesize = 2, auto_max = 0; \
175     char *htstr; \
176     htentries = mtev_conf_get_sections(rules[j], #rname, &hte_cnt); \
177     mtev_conf_get_int(rules[j], "@" #rname "_auto_add", &auto_max); \
178     if(hte_cnt || auto_max > 0) { \
179       rule->rname##_auto_hash_max = auto_max; \
180       rule->rname##_ht = calloc(1, sizeof(*(rule->rname##_ht))); \
181       while(tablesize < hte_cnt) tablesize <<= 1; \
182       mtev_hash_init_size(rule->rname##_ht, tablesize); \
183       for(hti=0; hti<hte_cnt; hti++) { \
184         if(!mtev_conf_get_string(htentries[hti], "self::node()", &htstr)) \
185           mtevL(noit_error, "Error fetching text content from filter match.\n"); \
186         else \
187           mtev_hash_replace(rule->rname##_ht, htstr, strlen(htstr), NULL, free, NULL); \
188       } \
189     } \
190     free(htentries); \
191 } while(0);
192     HT_COMPILE(target);
193     HT_COMPILE(module);
194     HT_COMPILE(name);
195     HT_COMPILE(metric);
196    
197     /* Compile our rules */
198 #define RULE_COMPILE(rname) do { \
199   char *longre = NULL; \
200   if(mtev_conf_get_string(rules[j], "@" #rname, &longre)) { \
201     const char *error; \
202     int erroffset; \
203     rule->rname = pcre_compile(longre, 0, &error, &erroffset, NULL); \
204     if(!rule->rname) { \
205       mtevL(noit_debug, "set '%s' rule '%s: %s' compile failed: %s\n", \
206             set->name, #rname, longre, error ? error : "???"); \
207       rule->rname##_override = fallback_no_match; \
208     } \
209     else { \
210       rule->rname##_e = pcre_study(rule->rname, 0, &error); \
211     } \
212     free(longre); \
213   } \
214 } while(0)
215
216     if(rule->target_ht == NULL)
217       RULE_COMPILE(target);
218     if(rule->module_ht == NULL)
219       RULE_COMPILE(module);
220     if(rule->name_ht == NULL)
221       RULE_COMPILE(name);
222     if(rule->metric_ht == NULL)
223       RULE_COMPILE(metric);
224     rule->next = set->rules;
225     set->rules = rule;
226   }
227
228   filterrule_t *cursor;
229   for(cursor = set->rules; cursor->next; cursor = cursor->next) {
230     if(cursor->skipto) {
231       filterrule_t *target;
232       for(target = cursor->next; target; target = target->next) {
233         if(target->ruleid && !strcmp(cursor->skipto, target->ruleid)) {
234           cursor->skipto_rule = target;
235           break;
236         }
237       }
238       if(!cursor->skipto_rule)
239         mtevL(noit_error, "filterset %s skipto:%s not found\n",
240               set->name, cursor->skipto);
241     }
242   }
243   free(rules);
244   LOCKFS();
245   mtev_hash_replace(filtersets, set->name, strlen(set->name), (void *)set,
246                     NULL, filterset_free);
247   UNLOCKFS();
248 }
249 int
250 noit_filter_exists(const char *name) {
251   int exists;
252   void *v;
253   LOCKFS();
254   exists = mtev_hash_retrieve(filtersets, name, strlen(name), &v);
255   UNLOCKFS();
256   return exists;
257 }
258 int
259 noit_filter_remove(mtev_conf_section_t vnode) {
260   int removed;
261   char *name = (char *)xmlGetProp(vnode, (xmlChar *)"name");
262   if(!name) return 0;
263   LOCKFS();
264   removed = mtev_hash_delete(filtersets, name, strlen(name),
265                              NULL, filterset_free);
266   UNLOCKFS();
267   return removed;
268 }
269 void
270 noit_filters_from_conf() {
271   mtev_conf_section_t *sets;
272   int i, cnt;
273
274   sets = mtev_conf_get_sections(NULL, "/noit/filtersets//filterset", &cnt);
275   for(i=0; i<cnt; i++) {
276     mtev_watchdog_child_heartbeat();
277     noit_filter_compile_add(sets[i]);
278   }
279   free(sets);
280 }
281
282 void
283 noit_refresh_filtersets(mtev_console_closure_t ncct,
284                         mtev_conf_t_userdata_t *info) {
285   noit_filters_from_conf();
286   nc_printf(ncct, "Reloaded %d filtersets.\n",
287             filtersets ? mtev_hash_size(filtersets) : 0);
288 }
289
290 static mtev_boolean
291 noit_apply_filterrule(mtev_hash_table *m,
292                       pcre *p, pcre_extra *pe, const char *subj) {
293   int rc, ovector[30];
294   if(m) {
295     void *vptr;
296     return mtev_hash_retrieve(m, subj, strlen(subj), &vptr);
297   }
298   if(!p) return mtev_true;
299   rc = pcre_exec(p, pe, subj, strlen(subj), 0, 0, ovector, 30);
300   if(rc >= 0) return mtev_true;
301   return mtev_false;
302 }
303 static int
304 noit_filter_update_conf_rule(const char *fname, int idx, const char *rname, const char *value) {
305   char xpath[1024];
306   xmlNodePtr rulenode, child;
307
308   snprintf(xpath, sizeof(xpath), "//filtersets/filterset[@name=\"%s\"]/rule[%d]", fname, idx);
309   rulenode = mtev_conf_get_section(NULL, xpath);
310   if(!rulenode) return -1;
311   child = xmlNewNode(NULL, (xmlChar *)rname);
312   xmlNodeAddContent(child, (xmlChar *)value);
313   xmlAddChild(rulenode, child);
314   CONF_DIRTY(rulenode);
315   mtev_conf_mark_changed();
316   mtev_conf_request_write();
317   return 0;
318 }
319 mtev_boolean
320 noit_apply_filterset(const char *filterset,
321                      noit_check_t *check,
322                      metric_t *metric) {
323   /* We pass in filterset here just in case someone wants to apply
324    * a filterset other than check->filterset.. You never know.
325    */
326   void *vfs;
327   if(!filterset) return mtev_true;   /* No filter */
328   if(!filtersets) return mtev_false; /* Couldn't possibly match */
329
330   LOCKFS();
331   if(mtev_hash_retrieve(filtersets, filterset, strlen(filterset), &vfs)) {
332     filterset_t *fs = (filterset_t *)vfs;
333     filterrule_t *r, *skipto_rule = NULL;
334     int idx = 0;
335     mtev_atomic_inc32(&fs->ref_cnt);
336     UNLOCKFS();
337 #define MATCHES(rname, value) noit_apply_filterrule(r->rname##_ht, r->rname ? r->rname : r->rname##_override, r->rname ? r->rname##_e : NULL, value)
338     for(r = fs->rules; r; r = r->next) {
339       int need_target, need_module, need_name, need_metric;
340       /* If we're targeting a skipto rule, match or continue */
341       idx++;
342       if(skipto_rule && skipto_rule != r) continue;
343       skipto_rule = NULL;
344
345       need_target = !MATCHES(target, check->target);
346       need_module = !MATCHES(module, check->module);
347       need_name = !MATCHES(name, check->name);
348       need_metric = !MATCHES(metric, metric->metric_name);
349       if(!need_target && !need_module && !need_name && !need_metric) {
350         if(r->type == NOIT_FILTER_SKIPTO) {
351           skipto_rule = r->skipto_rule;
352           continue;
353         }
354         return (r->type == NOIT_FILTER_ACCEPT) ? mtev_true : mtev_false;
355       }
356       /* If we need some of these and we have an auto setting that isn't fulfilled for each of them, we can add and succeed */
357 #define CHECK_ADD(rname) (!need_##rname || (r->rname##_auto_hash_max > 0 && r->rname##_ht && mtev_hash_size(r->rname##_ht) < r->rname##_auto_hash_max))
358       if(CHECK_ADD(target) && CHECK_ADD(module) && CHECK_ADD(name) && CHECK_ADD(metric)) {
359 #define UPDATE_FILTER_RULE(rnum, rname, value) do { \
360   mtev_hash_replace(r->rname##_ht, strdup(value), strlen(value), NULL, free, NULL); \
361   if(noit_filter_update_conf_rule(fs->name, rnum, #rname, value) < 0) { \
362     mtevL(noit_error, "Error updating configuration for new filter auto_add on %s=%s\n", #rname, value); \
363   } \
364 } while(0)
365         if(need_target) UPDATE_FILTER_RULE(idx, target, check->target);
366         if(need_module) UPDATE_FILTER_RULE(idx, module, check->module);
367         if(need_name) UPDATE_FILTER_RULE(idx, name, check->name);
368         if(need_metric) UPDATE_FILTER_RULE(idx, metric, metric->metric_name);
369         noit_filterset_log_auto_add(fs->name, check, metric, r->type == NOIT_FILTER_ACCEPT);
370         if(r->type == NOIT_FILTER_SKIPTO) {
371           skipto_rule = r->skipto_rule;
372           continue;
373         }
374         return (r->type == NOIT_FILTER_ACCEPT) ? mtev_true : mtev_false;
375       }
376     }
377     filterset_free(fs);
378     return mtev_false;
379   }
380   UNLOCKFS();
381   return mtev_false;
382 }
383
384 static char *
385 conf_t_filterset_prompt(EditLine *el) {
386   mtev_console_closure_t ncct;
387   mtev_conf_t_userdata_t *info;
388   static char *tl = "noit(conf)# ";
389   static char *pfmt = "noit(conf:filterset:%.*s%s)# ";
390   int max_space, namelen;
391   el_get(el, EL_USERDATA, (void *)&ncct);
392   if(!ncct) return tl;
393   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
394   if(!info) return tl;
395   max_space = sizeof(info->prompt) - strlen(pfmt) + 6 - 1;
396   namelen = strlen(info->filter_name);
397   if(namelen > max_space)
398     snprintf(info->prompt, sizeof(info->prompt), pfmt,
399              max_space - 3, info->filter_name, "...");
400   else
401     snprintf(info->prompt, sizeof(info->prompt), pfmt,
402              namelen, info->filter_name, "");
403   return info->prompt;
404 }
405
406 static int
407 noit_console_filter_show(mtev_console_closure_t ncct,
408                          int argc, char **argv,
409                          mtev_console_state_t *state,
410                          void *closure) {
411   mtev_conf_t_userdata_t *info;
412   char xpath[1024];
413   xmlNodePtr fsnode;
414   mtev_conf_section_t *rules;
415   int i, rulecnt;
416
417   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
418   snprintf(xpath, sizeof(xpath), "/%s",
419            info->path);
420   fsnode = mtev_conf_get_section(NULL, xpath);
421   if(!fsnode) {
422     nc_printf(ncct, "internal error\n");
423     return -1;
424   }
425   rules = mtev_conf_get_sections(fsnode, "rule", &rulecnt);
426   for(i=0; i<rulecnt; i++) {
427     char val[256];
428     val[0] = '\0';
429     mtev_conf_get_stringbuf(rules[i], "@type", val, sizeof(val));
430     nc_printf(ncct, "Rule %d [%s]:\n", i+1, val);
431 #define DUMP_ATTR(a) do { \
432   char *vstr; \
433   mtev_conf_section_t ht; \
434   int cnt; \
435   ht = mtev_conf_get_sections(rules[i], #a, &cnt); \
436   if(ht && cnt) { \
437     nc_printf(ncct, "\t%s: hash match of %d items\n", #a, cnt); \
438   } \
439   else if(mtev_conf_get_string(rules[i], "@" #a, &vstr)) { \
440     nc_printf(ncct, "\t%s: /%s/\n", #a, val); \
441     free(vstr); \
442   } \
443   free(ht); \
444 } while(0)
445     DUMP_ATTR(target);
446     DUMP_ATTR(module);
447     DUMP_ATTR(name);
448     DUMP_ATTR(metric);
449     DUMP_ATTR(id);
450     DUMP_ATTR(skipto);
451   }
452   if(rules) free(rules);
453   return 0;
454 }
455 static int
456 noit_console_rule_configure(mtev_console_closure_t ncct,
457                             int argc, char **argv,
458                             mtev_console_state_t *state,
459                             void *closure) {
460   xmlNodePtr fsnode = NULL;
461   mtev_conf_t_userdata_t *info;
462   char xpath[1024];
463
464   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
465   snprintf(xpath, sizeof(xpath), "/%s",
466            info->path);
467   fsnode = mtev_conf_get_section(NULL, xpath);
468   if(!fsnode) {
469     nc_printf(ncct, "internal error");
470     return -1;
471   }
472   if(closure) {
473     int rulenum;
474     xmlNodePtr byebye;
475     /* removing a rule */
476     if(argc != 1) {
477       nc_printf(ncct, "requires one argument\n");
478       return -1;
479     }
480     rulenum = atoi(argv[0]);
481     snprintf(xpath, sizeof(xpath), "rule[%d]", rulenum);
482     byebye = mtev_conf_get_section(fsnode, xpath);
483     if(!byebye) {
484       nc_printf(ncct, "cannot find rule\n");
485       return -1;
486     }
487     xmlUnlinkNode(byebye);
488     xmlFreeNode(byebye);
489     nc_printf(ncct, "removed\n");
490   }
491   else {
492     xmlNodePtr (*add_func)(xmlNodePtr, xmlNodePtr);
493     xmlNodePtr add_arg, new_rule;
494     int i, needs_type = 1;
495     if(argc < 1 || argc % 2) {
496       nc_printf(ncct, "even number of arguments required\n");
497       return -1;
498     }
499     if(!strcmp(argv[0], "before") || !strcmp(argv[0], "after")) {
500       int rulenum = atoi(argv[1]);
501       snprintf(xpath, sizeof(xpath), "rule[%d]", rulenum);
502       add_arg = mtev_conf_get_section(fsnode, xpath);
503       if(!add_arg) {
504         nc_printf(ncct, "%s rule not found\n", xpath);
505         return -1;
506       }
507       if(*argv[0] == 'b') add_func = xmlAddPrevSibling;
508       else add_func = xmlAddNextSibling;
509       argc -= 2;
510       argv += 2;
511     }
512     else {
513       add_func = xmlAddChild;
514       add_arg = fsnode;
515     }
516     for(i=0;i<argc;i+=2) {
517       if(!strcmp(argv[i], "type")) needs_type = 0;
518       else if(strcmp(argv[i], "target") && strcmp(argv[i], "module") &&
519               strcmp(argv[i], "name") && strcmp(argv[i], "metric") &&
520               strcmp(argv[i], "id")) {
521         nc_printf(ncct, "unknown attribute '%s'\n", argv[i]);
522         return -1;
523       }
524     }
525     if(needs_type) {
526       nc_printf(ncct, "type <allow|deny> is required\n");
527       return -1;
528     }
529     new_rule = xmlNewNode(NULL, (xmlChar *)"rule");
530     for(i=0;i<argc;i+=2)
531       xmlSetProp(new_rule, (xmlChar *)argv[i], (xmlChar *)argv[i+1]);
532     add_func(add_arg, new_rule);
533     noit_filter_compile_add((mtev_conf_section_t *)fsnode);
534   }
535   return 0;
536 }
537 static int
538 noit_console_filter_configure(mtev_console_closure_t ncct,
539                               int argc, char **argv,
540                               mtev_console_state_t *state,
541                               void *closure) {
542   xmlNodePtr parent, fsnode = NULL;
543   int rv = -1;
544   mtev_conf_t_userdata_t *info;
545   char xpath[1024];
546
547   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
548   if(!info) {
549     nc_printf(ncct, "internal error\n");
550     goto cleanup;
551   }
552   if(strncmp(info->path, "/filtersets/", strlen("/filtersets/")) &&
553      strcmp(info->path, "/filtersets")) {
554     nc_printf(ncct, "filterset only allows inside /filtersets (not %s)\n",
555               info->path);
556     goto cleanup;
557   }
558   if(argc != 1) {
559     nc_printf(ncct, "filterset requires one argument\n");
560     goto cleanup;
561   }
562   snprintf(xpath, sizeof(xpath), "/%s", info->path);
563   parent = mtev_conf_get_section(NULL, xpath);
564   if(!parent) {
565     nc_printf(ncct, "internal error, can't final current working path\n");
566     goto cleanup;
567   }
568   snprintf(xpath, sizeof(xpath), "filterset[@name=\"%s\"]", argv[0]);
569   fsnode = mtev_conf_get_section(parent, xpath);
570   if(closure) {
571     int removed;
572     removed = noit_filter_remove(fsnode);
573     nc_printf(ncct, "%sremoved filterset '%s'\n",
574               removed ? "" : "failed to ", argv[0]);
575     if(removed) {
576       CONF_REMOVE(fsnode);
577       xmlUnlinkNode(fsnode);
578       xmlFreeNode(fsnode);
579     }
580     rv = !removed;
581     goto cleanup;
582   }
583   if(!fsnode) {
584     void *vfs;
585     nc_printf(ncct, "Cannot find filterset '%s'\n", argv[0]);
586     LOCKFS();
587     if(mtev_hash_retrieve(filtersets, argv[0], strlen(argv[0]), &vfs)) {
588       UNLOCKFS();
589       nc_printf(ncct, "filter of the same name already exists\n");
590       goto cleanup;
591     }
592     UNLOCKFS();
593     /* Fine the parent path */
594     fsnode = xmlNewNode(NULL, (xmlChar *)"filterset");
595     xmlSetProp(fsnode, (xmlChar *)"name", (xmlChar *)argv[0]);
596     xmlAddChild(parent, fsnode);
597     nc_printf(ncct, "created new filterset\n");
598   }
599
600   if(info) {
601     char *xmlpath = NULL;
602     free(info->path);
603     xmlpath = (char *)xmlGetNodePath(fsnode);
604     info->path = strdup(xmlpath + strlen("/noit"));
605     free(xmlpath);
606     strlcpy(info->filter_name, argv[0], sizeof(info->filter_name));
607     if(state) {
608       mtev_console_state_push_state(ncct, state);
609       mtev_console_state_init(ncct);
610     }
611   }
612  cleanup:
613   return rv;
614 }
615
616 static int
617 filterset_accum(noit_check_t *check, void *closure) {
618   mtev_hash_table *active = closure;
619   if(!check->filterset) return 0;
620   if(mtev_hash_delete(active, check->filterset, strlen(check->filterset), free, NULL))
621     return 1;
622   return 0;
623 }
624
625 int
626 noit_filtersets_cull_unused() {
627   mtev_hash_table active = MTEV_HASH_EMPTY;
628   char *buffer = NULL;
629   mtev_conf_section_t *declares;
630   int i, n_uses = 0, n_declares = 0, removed = 0;
631   const char *declare_xpath = "//filterset[@name and not (@cull='false')]";
632
633   declares = mtev_conf_get_sections(NULL, declare_xpath, &n_declares);
634   if(declares) {
635     /* store all unit filtersets used */
636     for(i=0;i<n_declares;i++) {
637       if(!buffer) buffer = malloc(128);
638       if(mtev_conf_get_stringbuf(declares[i], "@name", buffer, 128)) {
639         if(mtev_hash_store(&active, buffer, strlen(buffer), declares[i])) {
640           buffer = NULL;
641         }
642         else {
643           void *vnode = NULL;
644           /* We've just hit a duplicate.... check to see if there's an existing
645            * entry and if there is, load the latest one and delete the old
646            * one. */
647           mtev_hash_retrieve(&active, buffer, strlen(buffer), &vnode);
648           if (vnode) {
649             noit_filter_compile_add(declares[i]);
650             CONF_REMOVE(vnode);
651             xmlUnlinkNode(vnode);
652             xmlFreeNode(vnode);
653             removed++;
654             if(mtev_hash_replace(&active, buffer, strlen(buffer), declares[i], free, NULL)) {
655               buffer = NULL;
656             }
657           }
658         }
659       }
660     }
661     if(buffer) free(buffer);
662     free(declares);
663   }
664
665   n_uses = noit_poller_do(filterset_accum, &active);
666
667   if(n_uses > 0 && mtev_hash_size(&active) > 0) {
668     mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
669     const char *filter_name;
670     int filter_name_len;
671     void *vnode;
672     while(mtev_hash_next(&active, &iter, &filter_name, &filter_name_len,
673                          &vnode)) {
674       if(noit_filter_remove(vnode)) {
675         CONF_REMOVE(vnode);
676         xmlUnlinkNode(vnode);
677         xmlFreeNode(vnode);
678         removed++;
679       }
680     }
681   }
682
683   mtev_hash_destroy(&active, free, NULL);
684   return removed;
685 }
686
687 static int
688 noit_console_filter_cull(mtev_console_closure_t ncct,
689                          int argc, char **argv,
690                          mtev_console_state_t *state,
691                          void *closure) {
692   int rv = 0;
693   mtev_conf_t_userdata_t *info;
694
695   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
696   if(!info) {
697     nc_printf(ncct, "internal error\n");
698     return -1;
699   }
700   if(strncmp(info->path, "/filtersets/", strlen("/filtersets/")) &&
701      strcmp(info->path, "/filtersets")) {
702     nc_printf(ncct, "filterset only allows inside /filtersets (not %s)\n",
703               info->path);
704     return -1;
705   }
706   rv = noit_filtersets_cull_unused();
707   nc_printf(ncct, "Culled %d unused filtersets\n", rv);
708   return 0;
709 }
710 static void
711 register_console_filter_commands() {
712   mtev_console_state_t *tl, *filterset_state, *nostate;
713   cmd_info_t *confcmd, *conf_t_cmd, *no_cmd;
714
715   tl = mtev_console_state_initial();
716   confcmd = mtev_console_state_get_cmd(tl, "configure");
717   mtevAssert(confcmd && confcmd->dstate);
718
719   conf_t_cmd = mtev_console_state_get_cmd(confcmd->dstate, "terminal");
720   mtevAssert(conf_t_cmd && conf_t_cmd->dstate);
721
722   nostate = mtev_console_state_alloc();
723   mtev_console_state_add_cmd(nostate,
724     NCSCMD("rule", noit_console_rule_configure, NULL, NULL, (void *)1));
725
726   filterset_state = mtev_console_state_alloc();
727   mtev_console_state_add_cmd(filterset_state,
728     NCSCMD("exit", mtev_console_config_cd, NULL, NULL, ".."));
729   mtev_console_state_add_cmd(filterset_state,
730     NCSCMD("status", noit_console_filter_show, NULL, NULL, NULL));
731   mtev_console_state_add_cmd(filterset_state,
732     NCSCMD("rule", noit_console_rule_configure, NULL, NULL, NULL));
733   mtev_console_state_add_cmd(filterset_state,
734     NCSCMD("no", mtev_console_state_delegate, mtev_console_opt_delegate,
735            nostate, NULL));
736
737   filterset_state->console_prompt_function = conf_t_filterset_prompt;
738
739   mtev_console_state_add_cmd(conf_t_cmd->dstate,
740     NCSCMD("filterset", noit_console_filter_configure,
741            NULL, filterset_state, NULL));
742
743   mtev_console_state_add_cmd(conf_t_cmd->dstate,
744     NCSCMD("cull", noit_console_filter_cull,
745            NULL, NULL, NULL));
746
747   no_cmd = mtev_console_state_get_cmd(conf_t_cmd->dstate, "no");
748   mtevAssert(no_cmd && no_cmd->dstate);
749   mtev_console_state_add_cmd(no_cmd->dstate,
750     NCSCMD("filterset", noit_console_filter_configure, NULL, NULL, (void *)1));
751 }
752
753 void
754 noit_filters_init() {
755   const char *error;
756   int erroffset;
757   pthread_mutex_init(&filterset_lock, NULL);
758   filtersets = calloc(1, sizeof(mtev_hash_table));
759   fallback_no_match = pcre_compile("^(?=a)b", 0, &error, &erroffset, NULL);
760   if(!fallback_no_match) {
761     mtevL(noit_error, "Filter initialization failed (nomatch filter)\n");
762     exit(-1);
763   }
764   mtev_capabilities_add_feature("filterset:hash", NULL);
765   register_console_filter_commands();
766   noit_filters_from_conf();
767 }
Note: See TracBrowser for help on using the browser.