root/src/modules/httptrap.c

Revision 4ed37cf09ac9817ced9312616da97a3a1e90c6b3, 19.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 months ago)

cleanup of modules, verbose structure setting

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, 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 #include "noit_defines.h"
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <math.h>
39 #include <ctype.h>
40
41 #include "noit_module.h"
42 #include "noit_check.h"
43 #include "noit_check_tools.h"
44 #include "noit_rest.h"
45 #include "yajl-lib/yajl_parse.h"
46 #include "utils/noit_log.h"
47 #include "utils/noit_hash.h"
48
49 #define MAX_DEPTH 32
50
51 static noit_log_stream_t nlerr = NULL;
52 static noit_log_stream_t nldeb = NULL;
53
54 typedef struct _mod_config {
55   noit_hash_table *options;
56   noit_boolean asynch_metrics;
57 } httptrap_mod_config_t;
58
59 typedef struct httptrap_closure_s {
60   noit_module_t *self;
61   int stats_count;
62 } httptrap_closure_t;
63
64 struct value_list {
65   char *v;
66   struct value_list *next;
67 };
68 struct rest_json_payload {
69   noit_check_t *check;
70   stats_t *stats;
71   yajl_handle parser;
72   int len;
73   int complete;
74   char *error;
75   int depth;
76   char *keys[MAX_DEPTH];
77   char array_depth[MAX_DEPTH];
78   unsigned char last_special_key;
79   unsigned char saw_complex_type;
80   metric_type_t last_type;
81   struct value_list *last_value;
82   int cnt;
83   noit_boolean immediate;
84 };
85
86 #define NEW_LV(json,a) do { \
87   struct value_list *nlv = malloc(sizeof(*nlv)); \
88   nlv->v = a; \
89   nlv->next = json->last_value; \
90   json->last_value = nlv; \
91 } while(0)
92
93 static noit_boolean
94 noit_httptrap_check_aynsch(noit_module_t *self,
95                            noit_check_t *check) {
96   const char *config_val;
97   httptrap_mod_config_t *conf;
98   if(!self) return noit_true;
99   conf = noit_module_get_userdata(self);
100   if(!conf) return noit_true;
101   noit_boolean is_asynch = conf->asynch_metrics;
102   if(noit_hash_retr_str(check->config,
103                         "asynch_metrics", strlen("asynch_metrics"),
104                         (const char **)&config_val)) {
105     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
106       is_asynch = noit_false;
107     else if(!strcasecmp(config_val, "true") || !strcasecmp(config_val, "on"))
108       is_asynch = noit_true;
109   }
110
111   if(is_asynch) check->flags |= NP_SUPPRESS_METRICS;
112   else check->flags &= ~NP_SUPPRESS_METRICS;
113   return is_asynch;
114 }
115
116 static void
117 set_array_key(struct rest_json_payload *json) {
118   if(json->array_depth[json->depth] > 0) {
119     char str[256];
120     int strLen;
121     snprintf(str, sizeof(str), "%d", json->array_depth[json->depth] - 1);
122     json->array_depth[json->depth]++;
123     strLen = strlen(str);
124     if(json->keys[json->depth]) free(json->keys[json->depth]);
125     json->keys[json->depth] = NULL;
126     if(json->depth == 0) {
127       json->keys[json->depth] = malloc(strLen+1);
128       memcpy(json->keys[json->depth], str, strLen);
129       json->keys[json->depth][strLen] = '\0';
130     }
131     else {
132       int uplen = strlen(json->keys[json->depth-1]);
133       if(uplen + 1 + strLen > 255) return;
134       json->keys[json->depth] = malloc(uplen + 1 + strLen + 1);
135       memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
136       json->keys[json->depth][uplen] = '`';
137       memcpy(json->keys[json->depth] + uplen + 1, str, strLen);
138       json->keys[json->depth][uplen + 1 + strLen] = '\0';
139     }
140   }
141 }
142 static int
143 httptrap_yajl_cb_null(void *ctx) {
144   struct rest_json_payload *json = ctx;
145   if(json->depth<0) return 0;
146   set_array_key(json);
147   if(json->last_special_key == 0x2) {
148     NEW_LV(json, NULL);
149     return 1;
150   }
151   if(json->last_special_key) return 0;
152   noit_stats_set_metric(json->check, json->stats,
153       json->keys[json->depth], METRIC_INT32, NULL);
154   if(json->immediate)
155     noit_stats_log_immediate_metric(json->check,
156         json->keys[json->depth], METRIC_INT32, NULL);
157   json->cnt++;
158   return 1;
159 }
160 static int
161 httptrap_yajl_cb_boolean(void *ctx, int boolVal) {
162   int ival;
163   struct rest_json_payload *json = ctx;
164   if(json->depth<0) return 0;
165   set_array_key(json);
166   if(json->last_special_key == 0x2) {
167     NEW_LV(json, strdup(boolVal ? "1" : "0"));
168     return 1;
169   }
170   if(json->last_special_key) return 0;
171   ival = boolVal ? 1 : 0;
172   noit_stats_set_metric(json->check, json->stats,
173       json->keys[json->depth], METRIC_INT32, &ival);
174   if(json->immediate)
175     noit_stats_log_immediate_metric(json->check,
176         json->keys[json->depth], METRIC_INT32, &ival);
177   json->cnt++;
178   return 1;
179 }
180 static int
181 httptrap_yajl_cb_number(void *ctx, const char * numberVal,
182                         size_t numberLen) {
183   char val[128];
184   struct rest_json_payload *json = ctx;
185   if(json->depth<0) return 0;
186   set_array_key(json);
187   if(json->last_special_key == 0x2) {
188     char *str;
189     str = malloc(numberLen+1);
190     memcpy(str, numberVal, numberLen);
191     str[numberLen] = '\0';
192     NEW_LV(json, str);
193     return 1;
194   }
195   if(json->last_special_key) return 0;
196   if(numberLen > sizeof(val)-1) numberLen = sizeof(val)-1;
197   memcpy(val, numberVal, numberLen);
198   val[numberLen] = '\0';
199   noit_stats_set_metric(json->check, json->stats,
200       json->keys[json->depth], METRIC_GUESS, val);
201   if(json->immediate)
202     noit_stats_log_immediate_metric(json->check,
203         json->keys[json->depth], METRIC_GUESS, val);
204   json->cnt++;
205   return 1;
206 }
207 static int
208 httptrap_yajl_cb_string(void *ctx, const unsigned char * stringVal,
209                         size_t stringLen) {
210   struct rest_json_payload *json = ctx;
211   char val[4096];
212   if(json->depth<0) return 0;
213   set_array_key(json);
214   if(json->last_special_key == 0x1) {
215     if(stringLen != 1) return 0;
216     if(*stringVal == 'L' || *stringVal == 'l' ||
217         *stringVal == 'I' || *stringVal == 'i' ||
218         *stringVal == 'n' || *stringVal == 's') {
219       json->last_type = *stringVal;
220       json->saw_complex_type |= 0x1;
221       return 1;
222     }
223     return 0;
224   }
225   else if(json->last_special_key == 0x2) {
226     char *str;
227     str = malloc(stringLen+1);
228     memcpy(str, stringVal, stringLen);
229     str[stringLen] = '\0';
230     NEW_LV(json, str);
231     json->saw_complex_type |= 0x2;
232     return 1;
233   }
234   if(stringLen > sizeof(val)-1) stringLen = sizeof(val)-1;
235   memcpy(val, stringVal, stringLen);
236   val[stringLen] = '\0';
237   noit_stats_set_metric(json->check, json->stats,
238       json->keys[json->depth], METRIC_GUESS, val);
239   if(json->immediate)
240     noit_stats_log_immediate_metric(json->check,
241         json->keys[json->depth], METRIC_GUESS, val);
242   json->cnt++;
243   return 1;
244 }
245 static int
246 httptrap_yajl_cb_start_map(void *ctx) {
247   struct rest_json_payload *json = ctx;
248   set_array_key(json);
249   json->depth++;
250   if(json->depth >= MAX_DEPTH) return 0;
251   return 1;
252 }
253 static int
254 httptrap_yajl_cb_end_map(void *ctx) {
255   struct value_list *p, *last_p = NULL;
256   struct rest_json_payload *json = ctx;
257   json->depth--;
258   if(json->saw_complex_type == 0x3) {
259     long double total = 0, cnt = 0;
260     noit_boolean use_avg = noit_false;
261     for(p=json->last_value;p;p=p->next) {
262       noit_stats_set_metric_coerce(json->check, json->stats,
263           json->keys[json->depth], json->last_type, p->v);
264       last_p = p;
265       if(p->v != NULL &&
266          (json->last_type == 'L' || json->last_type == 'l' ||
267           json->last_type == 'I' || json->last_type == 'i' ||
268           json->last_type == 'n')) {
269         total += strtold(p->v, NULL);
270         cnt = cnt + 1;
271         use_avg = noit_true;
272       }
273       json->cnt++;
274     }
275     if(json->immediate && last_p != NULL) {
276       if(use_avg) {
277         double avg = total / cnt;
278         noit_stats_log_immediate_metric(json->check,
279             json->keys[json->depth], 'n', &avg);
280       }
281       else {
282         noit_stats_log_immediate_metric(json->check,
283             json->keys[json->depth], json->last_type, last_p->v);
284       }
285     }
286   }
287   json->saw_complex_type = 0;
288   for(p=json->last_value;p;) {
289     struct value_list *savenext;
290     savenext = p->next;
291     if(p->v) free(p->v);
292     savenext = p->next;
293     free(p);
294     p = savenext;
295   }
296   json->last_value = NULL;
297   return 1;
298 }
299 static int
300 httptrap_yajl_cb_start_array(void *ctx) {
301   struct rest_json_payload *json = ctx;
302   json->depth++;
303   json->array_depth[json->depth]++;
304   return 1;
305 }
306 static int
307 httptrap_yajl_cb_end_array(void *ctx) {
308   struct rest_json_payload *json = ctx;
309   json->array_depth[json->depth] = 0;
310   json->depth--;
311   return 1;
312 }
313 static int
314 httptrap_yajl_cb_map_key(void *ctx, const unsigned char * key,
315                          size_t stringLen) {
316   struct rest_json_payload *json = ctx;
317   if(stringLen > 255) return 0;
318   if(json->keys[json->depth]) free(json->keys[json->depth]);
319   json->keys[json->depth] = NULL;
320   if(stringLen == 5 && memcmp(key, "_type", 5) == 0) {
321     json->last_special_key = 0x1;
322     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
323     return 1;
324   }
325   if(stringLen == 6 && memcmp(key, "_value", 6) == 0) {
326     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
327     json->last_special_key = 0x2;
328     json->saw_complex_type |= 0x2;
329     return 1;
330   }
331   json->last_special_key = 0;
332   if(json->depth == 0) {
333     json->keys[json->depth] = malloc(stringLen+1);
334     memcpy(json->keys[json->depth], key, stringLen);
335     json->keys[json->depth][stringLen] = '\0';
336   }
337   else {
338     int uplen = strlen(json->keys[json->depth-1]);
339     if(uplen + 1 + stringLen > 255) return 0;
340     json->keys[json->depth] = malloc(uplen + 1 + stringLen + 1);
341     memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
342     json->keys[json->depth][uplen] = '`';
343     memcpy(json->keys[json->depth] + uplen + 1, key, stringLen);
344     json->keys[json->depth][uplen + 1 + stringLen] = '\0';
345   }
346   return 1;
347 }
348 static yajl_callbacks httptrap_yajl_callbacks = {
349   .yajl_null = httptrap_yajl_cb_null,
350   .yajl_boolean = httptrap_yajl_cb_boolean,
351   .yajl_number = httptrap_yajl_cb_number,
352   .yajl_string = httptrap_yajl_cb_string,
353   .yajl_start_map = httptrap_yajl_cb_start_map,
354   .yajl_map_key = httptrap_yajl_cb_map_key,
355   .yajl_end_map = httptrap_yajl_cb_end_map,
356   .yajl_start_array = httptrap_yajl_cb_start_array,
357   .yajl_end_array = httptrap_yajl_cb_end_array
358 };
359
360 static void
361 rest_json_payload_free(void *f) {
362   int i;
363   struct rest_json_payload *json = f;
364   if(json->parser) yajl_free(json->parser);
365   if(json->error) free(json->error);
366   for(i=0;i<MAX_DEPTH;i++)
367     if(json->keys[i]) free(json->keys[i]);
368   if(json->last_value) free(json->last_value);
369   free(json);
370 }
371
372 static struct rest_json_payload *
373 rest_get_json_upload(noit_http_rest_closure_t *restc,
374                     int *mask, int *complete) {
375   struct rest_json_payload *rxc;
376   noit_http_request *req = noit_http_session_request(restc->http_ctx);
377   httptrap_closure_t *ccl;
378   int content_length;
379   char buffer[32768];
380
381   content_length = noit_http_request_content_length(req);
382   rxc = restc->call_closure;
383   ccl = rxc->check->closure;
384   rxc->immediate = noit_httptrap_check_aynsch(ccl->self, rxc->check);
385   while(!rxc->complete) {
386     int len;
387     len = noit_http_session_req_consume(
388             restc->http_ctx, buffer,
389             MIN(content_length - rxc->len, sizeof(buffer)),
390             sizeof(buffer),
391             mask);
392     if(len > 0) {
393       yajl_status status;
394       status = yajl_parse(rxc->parser, (unsigned char *)buffer, len);
395       if(status != yajl_status_ok) {
396         unsigned char *err;
397         *complete = 1;
398         err = yajl_get_error(rxc->parser, 0, (unsigned char *)buffer, len);
399         rxc->error = strdup((char *)err);
400         yajl_free_error(rxc->parser, err);
401         return rxc;
402       }
403       rxc->len += len;
404     }
405     if(len < 0 && errno == EAGAIN) return NULL;
406     else if(len < 0) {
407       *complete = 1;
408       return NULL;
409     }
410     content_length = noit_http_request_content_length(req);
411     if((noit_http_request_payload_chunked(req) && len == 0) ||
412        (rxc->len == content_length)) {
413       rxc->complete = 1;
414       yajl_complete_parse(rxc->parser);
415     }
416   }
417
418   *complete = 1;
419   return rxc;
420 }
421
422 static int httptrap_submit(noit_module_t *self, noit_check_t *check,
423                            noit_check_t *cause) {
424   httptrap_closure_t *ccl;
425   struct timeval duration;
426   /* We are passive, so we don't do anything for transient checks */
427   if(check->flags & NP_TRANSIENT) return 0;
428
429   noit_httptrap_check_aynsch(self, check);
430   if(!check->closure) {
431     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
432     memset(ccl, 0, sizeof(httptrap_closure_t));
433     ccl->self = self;
434   } else {
435     // Don't count the first run
436     char human_buffer[256];
437     ccl = (httptrap_closure_t*)check->closure;
438     gettimeofday(&check->stats.inprogress.whence, NULL);
439     sub_timeval(check->stats.inprogress.whence, check->last_fire_time, &duration);
440     check->stats.inprogress.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
441
442     snprintf(human_buffer, sizeof(human_buffer),
443              "dur=%d,run=%d,stats=%d", check->stats.inprogress.duration,
444              check->generation, ccl->stats_count);
445     noitL(nldeb, "httptrap(%s) [%s]\n", check->target, human_buffer);
446
447     // Not sure what to do here
448     check->stats.inprogress.available = (ccl->stats_count > 0) ?
449         NP_AVAILABLE : NP_UNAVAILABLE;
450     check->stats.inprogress.state = (ccl->stats_count > 0) ?
451         NP_GOOD : NP_BAD;
452     check->stats.inprogress.status = human_buffer;
453     if(check->last_fire_time.tv_sec)
454       noit_check_passive_set_stats(check, &check->stats.inprogress);
455
456     memcpy(&check->last_fire_time, &check->stats.inprogress.whence, sizeof(duration));
457   }
458   ccl->stats_count = 0;
459   noit_check_stats_clear(check, &check->stats.inprogress);
460   return 0;
461 }
462
463 static int
464 push_payload_at_check(struct rest_json_payload *rxc) {
465   httptrap_closure_t *ccl;
466   noit_boolean immediate;
467   char key[256];
468
469   if (!rxc->check || strcmp(rxc->check->module, "httptrap")) return 0;
470   if (rxc->check->closure == NULL) return 0;
471   ccl = rxc->check->closure;
472   immediate = noit_httptrap_check_aynsch(ccl->self,rxc->check);
473
474   /* do it here */
475   ccl->stats_count = rxc->cnt;
476   return rxc->cnt;
477 }
478
479 static int
480 rest_httptrap_handler(noit_http_rest_closure_t *restc,
481                       int npats, char **pats) {
482   int mask, complete = 0, cnt;
483   struct rest_json_payload *rxc = NULL;
484   const char *error = "internal error", *secret = NULL;
485   noit_http_session_ctx *ctx = restc->http_ctx;
486   char json_out[128];
487   noit_check_t *check;
488   uuid_t check_id;
489
490   if(npats != 2) {
491     error = "bad uri";
492     goto error;
493   }
494   if(uuid_parse(pats[0], check_id)) {
495     error = "uuid parse error";
496     goto error;
497   }
498
499   if(restc->call_closure == NULL) {
500     httptrap_closure_t *ccl;
501     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
502     check = noit_poller_lookup(check_id);
503     if(!check || strcmp(check->module, "httptrap")) {
504       error = "no such httptrap check";
505       goto error;
506     }
507     (void)noit_hash_retr_str(check->config, "secret", strlen("secret"), &secret);
508     if(!secret) secret = "";
509     if(strcmp(pats[1], secret)) {
510       error = "secret mismatch";
511       goto error;
512     }
513     rxc->check = check;
514     ccl = check->closure;
515     if(!ccl) {
516       error = "noitd is booting, try again in a bit";
517       goto error;
518     }
519     rxc->stats = &rxc->check->stats.inprogress;
520     rxc->parser = yajl_alloc(&httptrap_yajl_callbacks, NULL, rxc);
521     rxc->depth = -1;
522     yajl_config(rxc->parser, yajl_allow_comments, 1);
523     yajl_config(rxc->parser, yajl_dont_validate_strings, 1);
524     yajl_config(rxc->parser, yajl_allow_trailing_garbage, 1);
525     yajl_config(rxc->parser, yajl_allow_partial_values, 1);
526     restc->call_closure_free = rest_json_payload_free;
527   }
528
529   rxc = rest_get_json_upload(restc, &mask, &complete);
530   if(rxc == NULL && !complete) return mask;
531
532   if(!rxc) goto error;
533   if(rxc->error) goto error;
534
535   cnt = push_payload_at_check(rxc);
536
537   noit_http_response_ok(ctx, "application/json");
538   snprintf(json_out, sizeof(json_out),
539            "{ \"stats\": %d }", cnt);
540   noit_http_response_append(ctx, json_out, strlen(json_out));
541   noit_http_response_end(ctx);
542   return 0;
543
544  error:
545   noit_http_response_server_error(ctx, "application/json");
546   noit_http_response_append(ctx, "{ error: \"", 10);
547   if(rxc && rxc->error) error = rxc->error;
548   noit_http_response_append(ctx, error, strlen(error));
549   noit_http_response_append(ctx, "\" }", 3);
550   noit_http_response_end(ctx);
551   return 0;
552 }
553
554 static int noit_httptrap_initiate_check(noit_module_t *self,
555                                         noit_check_t *check,
556                                         int once, noit_check_t *cause) {
557   check->flags |= NP_PASSIVE_COLLECTION;
558   if (check->closure == NULL) {
559     httptrap_closure_t *ccl;
560     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
561     ccl->self = self;
562   }
563   INITIATE_CHECK(httptrap_submit, self, check, cause);
564   return 0;
565 }
566
567 static int noit_httptrap_config(noit_module_t *self, noit_hash_table *options) {
568   httptrap_mod_config_t *conf;
569   conf = noit_module_get_userdata(self);
570   if(conf) {
571     if(conf->options) {
572       noit_hash_destroy(conf->options, free, free);
573       free(conf->options);
574     }
575   }
576   else
577     conf = calloc(1, sizeof(*conf));
578   conf->options = options;
579   noit_module_set_userdata(self, conf);
580   return 1;
581 }
582
583 static int noit_httptrap_onload(noit_image_t *self) {
584   if(!nlerr) nlerr = noit_log_stream_find("error/httptrap");
585   if(!nldeb) nldeb = noit_log_stream_find("debug/httptrap");
586   if(!nlerr) nlerr = noit_error;
587   if(!nldeb) nldeb = noit_debug;
588   return 0;
589 }
590
591 static int noit_httptrap_init(noit_module_t *self) {
592   const char *config_val;
593   httptrap_mod_config_t *conf;
594   conf = noit_module_get_userdata(self);
595
596   conf->asynch_metrics = noit_true;
597   if(noit_hash_retr_str(conf->options,
598                         "asynch_metrics", strlen("asynch_metrics"),
599                         (const char **)&config_val)) {
600     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
601       conf->asynch_metrics = noit_false;
602   }
603
604   noit_module_set_userdata(self, conf);
605
606   /* register rest handler */
607   noit_http_rest_register("PUT", "/module/httptrap/",
608                           "^(" UUID_REGEX ")/([^/]*).*$",
609                           rest_httptrap_handler);
610   noit_http_rest_register("POST", "/module/httptrap/",
611                           "^(" UUID_REGEX ")/([^/]*).*$",
612                           rest_httptrap_handler);
613   return 0;
614 }
615
616 #include "httptrap.xmlh"
617 noit_module_t httptrap = {
618   {
619     .magic = NOIT_MODULE_MAGIC,
620     .version = NOIT_MODULE_ABI_VERSION,
621     .name = "httptrap",
622     .description = "httptrap collection",
623     .xml_description = httptrap_xml_description,
624     .onload = noit_httptrap_onload
625   },
626   noit_httptrap_config,
627   noit_httptrap_init,
628   noit_httptrap_initiate_check,
629   NULL
630 };
Note: See TracBrowser for help on using the browser.