root/src/noit_filters.c

Revision 637731dd84df22000af7615a62662e35d76413a9, 15.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

fixes #350

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include "utils/noit_hash.h"
36 #include "utils/noit_atomic.h"
37 #include "noit_conf.h"
38 #include "noit_check.h"
39 #include "noit_conf_checks.h"
40 #include "noit_filters.h"
41
42 #include <pcre.h>
43 #include <assert.h>
44 #include <libxml/tree.h>
45
46 static noit_hash_table *filtersets = NULL;
47 static pthread_mutex_t filterset_lock;
48 #define LOCKFS() pthread_mutex_lock(&filterset_lock)
49 #define UNLOCKFS() pthread_mutex_unlock(&filterset_lock)
50
51 typedef enum { NOIT_FILTER_ACCEPT, NOIT_FILTER_DENY } noit_ruletype_t;
52 typedef struct _filterrule {
53   noit_ruletype_t type;
54   pcre *target;
55   pcre_extra *target_e;
56   pcre *module;
57   pcre_extra *module_e;
58   pcre *name;
59   pcre_extra *name_e;
60   pcre *metric;
61   pcre_extra *metric_e;
62   struct _filterrule *next;
63 } filterrule_t;
64
65 typedef struct {
66   noit_atomic32_t ref_cnt;
67   char *name;
68   filterrule_t *rules;
69 } filterset_t;
70
71 #define FRF(r,a) do { \
72   if(r->a) pcre_free(r->a); \
73   if(r->a##_e) pcre_free(r->a##_e); \
74 } while(0)
75
76 static void
77 filterrule_free(void *vp) {
78   filterrule_t *r = vp;
79   FRF(r,target);
80   FRF(r,module);
81   FRF(r,name);
82   FRF(r,metric);
83   free(r);
84 }
85 static void
86 filterset_free(void *vp) {
87   filterset_t *fs = vp;
88   filterrule_t *r;
89   if(noit_atomic_dec32(&fs->ref_cnt) != 0) return;
90   noitL(noit_error, "Freeing filterset [%d]: %s\n", fs->ref_cnt, fs->name);
91   while(fs->rules) {
92     r = fs->rules->next;
93     filterrule_free(fs->rules);
94     fs->rules = r;
95   }
96   if(fs->name) free(fs->name);
97   free(fs);
98 }
99 void
100 noit_filter_compile_add(noit_conf_section_t setinfo) {
101   noit_conf_section_t *rules;
102   int j, fcnt;
103   char filterset_name[256];
104   filterset_t *set;
105   if(!noit_conf_get_stringbuf(setinfo, "@name",
106                               filterset_name, sizeof(filterset_name))) {
107     noitL(noit_error,
108           "filterset with no name, skipping as it cannot be referenced.\n");
109     return;
110   }
111   set = calloc(1, sizeof(*set));
112   set->ref_cnt = 1;
113   set->name = strdup(filterset_name);
114
115   rules = noit_conf_get_sections(setinfo, "rule", &fcnt);
116   /* Here we will work through the list backwards pushing the rules on
117    * the front of the list.  That way we can simply walk them in order
118    * for the application process.
119    */
120   noitL(noit_debug, "Compiling filterset '%s'\n", set->name);
121   for(j=fcnt-1; j>=0; j--) {
122     filterrule_t *rule;
123     char buffer[256];
124     if(!noit_conf_get_stringbuf(rules[j], "@type", buffer, sizeof(buffer)) ||
125        (strcmp(buffer, "accept") && strcmp(buffer, "allow") && strcmp(buffer, "deny"))) {
126       noitL(noit_error, "rule must have type 'accept' or 'allow' or 'deny'\n");
127       continue;
128     }
129     noitL(noit_debug, "Prepending %s into %s\n", buffer, set->name);
130     rule = calloc(1, sizeof(*rule));
131     rule->type = (!strcmp(buffer, "accept") || !strcmp(buffer, "allow")) ?
132                    NOIT_FILTER_ACCEPT : NOIT_FILTER_DENY;
133
134     /* Compile our rules */
135 #define RULE_COMPILE(rname) do { \
136   char *longre = NULL; \
137   if(noit_conf_get_string(rules[j], "@" #rname, &longre)) { \
138     const char *error; \
139     int erroffset; \
140     rule->rname = pcre_compile(longre, 0, &error, &erroffset, NULL); \
141     if(!rule->rname) { \
142       noitL(noit_error, "set '%s' rule '%s: %s' compile failed: %s\n", \
143             set->name, #rname, longre, error ? error : "???"); \
144     } \
145     else { \
146       rule->rname##_e = pcre_study(rule->rname, 0, &error); \
147     } \
148     free(longre); \
149   } \
150 } while(0)
151
152     RULE_COMPILE(target);
153     RULE_COMPILE(module);
154     RULE_COMPILE(name);
155     RULE_COMPILE(metric);
156     rule->next = set->rules;
157     set->rules = rule;
158   }
159   free(rules);
160   LOCKFS();
161   noit_hash_replace(filtersets, set->name, strlen(set->name), (void *)set,
162                     NULL, filterset_free);
163   UNLOCKFS();
164 }
165 int
166 noit_filter_exists(const char *name) {
167   int exists;
168   void *v;
169   LOCKFS();
170   exists = noit_hash_retrieve(filtersets, name, strlen(name), &v);
171   UNLOCKFS();
172   return exists;
173 }
174 int
175 noit_filter_remove(noit_conf_section_t vnode) {
176   int removed;
177   xmlNodePtr node = vnode;
178   char *name = (char *)xmlGetProp(vnode, (xmlChar *)"name");
179   if(!name) return 0;
180   LOCKFS();
181   removed = noit_hash_delete(filtersets, name, strlen(name),
182                              NULL, filterset_free);
183   UNLOCKFS();
184   return removed;
185 }
186 void
187 noit_filters_from_conf() {
188   noit_conf_section_t *sets;
189   int i, cnt;
190
191   sets = noit_conf_get_sections(NULL, "/noit/filtersets//filterset", &cnt);
192   for(i=0; i<cnt; i++) {
193     noit_filter_compile_add(sets[i]);
194   }
195   free(sets);
196 }
197
198 void
199 noit_refresh_filtersets(noit_console_closure_t ncct,
200                         noit_conf_t_userdata_t *info) {
201   noit_filters_from_conf();
202   nc_printf(ncct, "Reloaded %d filtersets.\n",
203             filtersets ? filtersets->size : 0);
204 }
205
206 static noit_boolean
207 noit_apply_filterrule(pcre *p, pcre_extra *pe, const char *subj) {
208   int rc, ovector[30];
209   if(!p) return noit_true;
210   rc = pcre_exec(p, pe, subj, strlen(subj), 0, 0, ovector, 30);
211   if(rc >= 0) return noit_true;
212   return noit_false;
213 }
214 noit_boolean
215 noit_apply_filterset(const char *filterset,
216                      noit_check_t *check,
217                      metric_t *metric) {
218   /* We pass in filterset here just in case someone wants to apply
219    * a filterset other than check->filterset.. You never know.
220    */
221   void *vfs;
222   if(!filterset) return noit_true;   /* No filter */
223   if(!filtersets) return noit_false; /* Couldn't possibly match */
224
225   LOCKFS();
226   if(noit_hash_retrieve(filtersets, filterset, strlen(filterset), &vfs)) {
227     filterset_t *fs = (filterset_t *)vfs;
228     filterrule_t *r;
229     noit_atomic_inc32(&fs->ref_cnt);
230     UNLOCKFS();
231 #define MATCHES(rname, value) noit_apply_filterrule(r->rname, r->rname##_e, value)
232     for(r = fs->rules; r; r = r->next) {
233       if(MATCHES(target, check->target) &&
234          MATCHES(module, check->module) &&
235          MATCHES(name, check->name) &&
236          MATCHES(metric, metric->metric_name))
237         return (r->type == NOIT_FILTER_ACCEPT) ? noit_true : noit_false;
238     }
239     filterset_free(fs);
240     return noit_false;
241   }
242   UNLOCKFS();
243   return noit_false;
244 }
245
246 static char *
247 conf_t_filterset_prompt(EditLine *el) {
248   noit_console_closure_t ncct;
249   noit_conf_t_userdata_t *info;
250   static char *tl = "noit(conf)# ";
251   static char *pfmt = "noit(conf:filterset:%.*s%s)# ";
252   int max_space, namelen;
253   el_get(el, EL_USERDATA, (void *)&ncct);
254   if(!ncct) return tl;
255   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
256   if(!info) return tl;
257   max_space = sizeof(info->prompt) - strlen(pfmt) + 6 - 1;
258   namelen = strlen(info->filter_name);
259   if(namelen > max_space)
260     snprintf(info->prompt, sizeof(info->prompt), pfmt,
261              max_space - 3, info->filter_name, "...");
262   else
263     snprintf(info->prompt, sizeof(info->prompt), pfmt,
264              namelen, info->filter_name, "");
265   return info->prompt;
266 }
267
268 static int
269 noit_console_filter_show(noit_console_closure_t ncct,
270                          int argc, char **argv,
271                          noit_console_state_t *state,
272                          void *closure) {
273   noit_conf_t_userdata_t *info;
274   char xpath[1024];
275   xmlNodePtr fsnode;
276   noit_conf_section_t *rules;
277   int i, rulecnt;
278
279   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
280   snprintf(xpath, sizeof(xpath), "/%s",
281            info->path);
282   fsnode = noit_conf_get_section(NULL, xpath);
283   if(!fsnode) {
284     nc_printf(ncct, "internal error\n");
285     return -1;
286   }
287   rules = noit_conf_get_sections(fsnode, "rule", &rulecnt);
288   for(i=0; i<rulecnt; i++) {
289     char val[256];
290     val[0] = '\0';
291     noit_conf_get_stringbuf(rules[i], "@type", val, sizeof(val));
292     nc_printf(ncct, "Rule %d [%s]:\n", i+1, val);
293 #define DUMP_ATTR(a) do { \
294   char *vstr; \
295   if(noit_conf_get_string(rules[i], "@" #a, &vstr)) { \
296     nc_printf(ncct, "\t%s: /%s/\n", #a, val); \
297     free(vstr); \
298   } \
299 } while(0)
300     DUMP_ATTR(target);
301     DUMP_ATTR(module);
302     DUMP_ATTR(name);
303     DUMP_ATTR(metric);
304   }
305   if(rules) free(rules);
306   return 0;
307 }
308 static int
309 noit_console_rule_configure(noit_console_closure_t ncct,
310                             int argc, char **argv,
311                             noit_console_state_t *state,
312                             void *closure) {
313   xmlNodePtr fsnode = NULL;
314   noit_conf_t_userdata_t *info;
315   char xpath[1024];
316
317   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
318   snprintf(xpath, sizeof(xpath), "/%s",
319            info->path);
320   fsnode = noit_conf_get_section(NULL, xpath);
321   if(!fsnode) {
322     nc_printf(ncct, "internal error");
323     return -1;
324   }
325   if(closure) {
326     int rulenum;
327     xmlNodePtr byebye;
328     /* removing a rule */
329     if(argc != 1) {
330       nc_printf(ncct, "requires one argument\n");
331       return -1;
332     }
333     rulenum = atoi(argv[0]);
334     snprintf(xpath, sizeof(xpath), "rule[%d]", rulenum);
335     byebye = noit_conf_get_section(fsnode, xpath);
336     if(!byebye) {
337       nc_printf(ncct, "cannot find rule\n");
338       return -1;
339     }
340     xmlUnlinkNode(byebye);
341     xmlFreeNode(byebye);
342     nc_printf(ncct, "removed\n");
343   }
344   else {
345     xmlNodePtr (*add_func)(xmlNodePtr, xmlNodePtr);
346     xmlNodePtr add_arg, new_rule;
347     int i, needs_type = 1;
348     if(argc < 1 || argc % 2) {
349       nc_printf(ncct, "even number of arguments required\n");
350       return -1;
351     }
352     if(!strcmp(argv[0], "before") || !strcmp(argv[0], "after")) {
353       int rulenum = atoi(argv[1]);
354       snprintf(xpath, sizeof(xpath), "rule[%d]", rulenum);
355       add_arg = noit_conf_get_section(fsnode, xpath);
356       if(!add_arg) {
357         nc_printf(ncct, "%s rule not found\n", xpath);
358         return -1;
359       }
360       if(*argv[0] == 'b') add_func = xmlAddPrevSibling;
361       else add_func = xmlAddNextSibling;
362       argc -= 2;
363       argv += 2;
364     }
365     else {
366       add_func = xmlAddChild;
367       add_arg = fsnode;
368     }
369     for(i=0;i<argc;i+=2) {
370       if(!strcmp(argv[i], "type")) needs_type = 0;
371       else if(strcmp(argv[i], "target") && strcmp(argv[i], "module") &&
372               strcmp(argv[i], "name") && strcmp(argv[i], "metric")) {
373         nc_printf(ncct, "unknown attribute '%s'\n", argv[i]);
374         return -1;
375       }
376     }
377     if(needs_type) {
378       nc_printf(ncct, "type <allow|deny> is required\n");
379       return -1;
380     }
381     new_rule = xmlNewNode(NULL, (xmlChar *)"rule");
382     for(i=0;i<argc;i+=2)
383       xmlSetProp(new_rule, (xmlChar *)argv[i], (xmlChar *)argv[i+1]);
384     add_func(add_arg, new_rule);
385     noit_filter_compile_add((noit_conf_section_t *)fsnode);
386   }
387   return 0;
388 }
389 static int
390 noit_console_filter_configure(noit_console_closure_t ncct,
391                               int argc, char **argv,
392                               noit_console_state_t *state,
393                               void *closure) {
394   xmlNodePtr parent, fsnode = NULL;
395   int rv = -1;
396   noit_conf_t_userdata_t *info;
397   char xpath[1024];
398
399   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
400   if(!info) {
401     nc_printf(ncct, "internal error\n");
402     goto cleanup;
403   }
404   if(strncmp(info->path, "/filtersets/", strlen("/filtersets/")) &&
405      strcmp(info->path, "/filtersets")) {
406     nc_printf(ncct, "filterset only allows inside /filtersets (not %s)\n",
407               info->path);
408     goto cleanup;
409   }
410   if(argc != 1) {
411     nc_printf(ncct, "filterset requires one argument\n");
412     goto cleanup;
413   }
414   snprintf(xpath, sizeof(xpath), "/%s", info->path);
415   parent = noit_conf_get_section(NULL, xpath);
416   if(!parent) {
417     nc_printf(ncct, "internal error, can't final current working path\n");
418     goto cleanup;
419   }
420   snprintf(xpath, sizeof(xpath), "filterset[@name=\"%s\"]", argv[0]);
421   fsnode = noit_conf_get_section(parent, xpath);
422   if(closure) {
423     int removed;
424     removed = noit_filter_remove(fsnode);
425     nc_printf(ncct, "%sremoved filterset '%s'\n",
426               removed ? "" : "failed to ", argv[0]);
427     if(removed) {
428       xmlUnlinkNode(fsnode);
429       xmlFreeNode(fsnode);
430     }
431     rv = !removed;
432     goto cleanup;
433   }
434   if(!fsnode) {
435     void *vfs;
436     nc_printf(ncct, "Cannot find filterset '%s'\n", argv[0]);
437     LOCKFS();
438     if(noit_hash_retrieve(filtersets, argv[0], strlen(argv[0]), &vfs)) {
439       UNLOCKFS();
440       nc_printf(ncct, "filter of the same name already exists\n");
441       goto cleanup;
442     }
443     UNLOCKFS();
444     /* Fine the parent path */
445     fsnode = xmlNewNode(NULL, (xmlChar *)"filterset");
446     xmlSetProp(fsnode, (xmlChar *)"name", (xmlChar *)argv[0]);
447     xmlAddChild(parent, fsnode);
448     nc_printf(ncct, "created new filterset\n");
449   }
450
451   if(info) {
452     if(info->path) free(info->path);
453     info->path = strdup((char *)xmlGetNodePath(fsnode) + strlen("/noit"));
454     strlcpy(info->filter_name, argv[0], sizeof(info->filter_name));
455     if(state) {
456       noit_console_state_push_state(ncct, state);
457       noit_console_state_init(ncct);
458     }
459   }
460  cleanup:
461   return rv;
462 }
463
464 static void
465 register_console_filter_commands() {
466   noit_console_state_t *tl, *filterset_state, *nostate;
467   cmd_info_t *confcmd, *conf_t_cmd, *no_cmd;
468
469   tl = noit_console_state_initial();
470   confcmd = noit_console_state_get_cmd(tl, "configure");
471   assert(confcmd && confcmd->dstate);
472
473   conf_t_cmd = noit_console_state_get_cmd(confcmd->dstate, "terminal");
474   assert(conf_t_cmd && conf_t_cmd->dstate);
475
476   nostate = noit_console_state_alloc();
477   noit_console_state_add_cmd(nostate,
478     NCSCMD("rule", noit_console_rule_configure, NULL, NULL, (void *)1));
479
480   filterset_state = noit_console_state_alloc();
481   noit_console_state_add_cmd(filterset_state,
482     NCSCMD("exit", noit_console_config_cd, NULL, NULL, ".."));
483   noit_console_state_add_cmd(filterset_state,
484     NCSCMD("status", noit_console_filter_show, NULL, NULL, NULL));
485   noit_console_state_add_cmd(filterset_state,
486     NCSCMD("rule", noit_console_rule_configure, NULL, NULL, NULL));
487   noit_console_state_add_cmd(filterset_state,
488     NCSCMD("no", noit_console_state_delegate, noit_console_opt_delegate,
489            nostate, NULL));
490
491   filterset_state->console_prompt_function = conf_t_filterset_prompt;
492
493   noit_console_state_add_cmd(conf_t_cmd->dstate,
494     NCSCMD("filterset", noit_console_filter_configure,
495            NULL, filterset_state, NULL));
496
497   no_cmd = noit_console_state_get_cmd(conf_t_cmd->dstate, "no");
498   assert(no_cmd && no_cmd->dstate);
499   noit_console_state_add_cmd(no_cmd->dstate,
500     NCSCMD("filterset", noit_console_filter_configure, NULL, NULL, (void *)1));
501 }
502
503 void
504 noit_filters_init() {
505   pthread_mutex_init(&filterset_lock, NULL);
506   filtersets = calloc(1, sizeof(noit_hash_table));
507   register_console_filter_commands();
508   noit_filters_from_conf();
509 }
Note: See TracBrowser for help on using the browser.